summaryrefslogtreecommitdiff
path: root/indra/llmessage
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llmessage
Print done when done.
Diffstat (limited to 'indra/llmessage')
-rw-r--r--indra/llmessage/llassetstorage.cpp1116
-rw-r--r--indra/llmessage/llassetstorage.h328
-rw-r--r--indra/llmessage/llbuffer.cpp746
-rw-r--r--indra/llmessage/llbuffer.h493
-rw-r--r--indra/llmessage/llbufferstream.cpp265
-rw-r--r--indra/llmessage/llbufferstream.h134
-rw-r--r--indra/llmessage/llcachename.cpp763
-rw-r--r--indra/llmessage/llcachename.h90
-rw-r--r--indra/llmessage/llchainio.cpp70
-rw-r--r--indra/llmessage/llchainio.h117
-rw-r--r--indra/llmessage/llcircuit.cpp1382
-rw-r--r--indra/llmessage/llcircuit.h315
-rw-r--r--indra/llmessage/llclassifiedflags.cpp49
-rw-r--r--indra/llmessage/llclassifiedflags.h31
-rw-r--r--indra/llmessage/lldatapacker.cpp1866
-rw-r--r--indra/llmessage/lldatapacker.h398
-rw-r--r--indra/llmessage/lldbstrings.h208
-rw-r--r--indra/llmessage/lldispatcher.cpp126
-rw-r--r--indra/llmessage/lldispatcher.h95
-rw-r--r--indra/llmessage/lleventflags.h17
-rw-r--r--indra/llmessage/llfiltersd2xmlrpc.cpp728
-rw-r--r--indra/llmessage/llfiltersd2xmlrpc.h253
-rw-r--r--indra/llmessage/llfollowcamparams.h43
-rw-r--r--indra/llmessage/llhost.cpp216
-rw-r--r--indra/llmessage/llhost.h133
-rw-r--r--indra/llmessage/llhttpassetstorage.cpp996
-rw-r--r--indra/llmessage/llhttpassetstorage.h116
-rw-r--r--indra/llmessage/llhttpclient.cpp278
-rw-r--r--indra/llmessage/llhttpclient.h83
-rw-r--r--indra/llmessage/llhttpnode.cpp449
-rw-r--r--indra/llmessage/llhttpnode.h306
-rw-r--r--indra/llmessage/llinstantmessage.cpp299
-rw-r--r--indra/llmessage/llinstantmessage.h308
-rw-r--r--indra/llmessage/llinvite.h15
-rw-r--r--indra/llmessage/lliobuffer.cpp96
-rw-r--r--indra/llmessage/lliobuffer.h117
-rw-r--r--indra/llmessage/lliohttpserver.cpp833
-rw-r--r--indra/llmessage/lliohttpserver.h95
-rw-r--r--indra/llmessage/lliopipe.cpp93
-rw-r--r--indra/llmessage/lliopipe.h291
-rw-r--r--indra/llmessage/lliosocket.cpp596
-rw-r--r--indra/llmessage/lliosocket.h355
-rw-r--r--indra/llmessage/llioutil.cpp76
-rw-r--r--indra/llmessage/llioutil.h154
-rw-r--r--indra/llmessage/llloginflags.h37
-rw-r--r--indra/llmessage/llmail.cpp285
-rw-r--r--indra/llmessage/llmail.h66
-rw-r--r--indra/llmessage/llmessagethrottle.cpp135
-rw-r--r--indra/llmessage/llmessagethrottle.h62
-rw-r--r--indra/llmessage/llmime.cpp613
-rw-r--r--indra/llmessage/llmime.h274
-rw-r--r--indra/llmessage/llnamevalue.cpp2141
-rw-r--r--indra/llmessage/llnamevalue.h186
-rw-r--r--indra/llmessage/llnullcipher.cpp40
-rw-r--r--indra/llmessage/llpacketack.h143
-rw-r--r--indra/llmessage/llpacketbuffer.cpp75
-rw-r--r--indra/llmessage/llpacketbuffer.h37
-rw-r--r--indra/llmessage/llpacketring.cpp293
-rw-r--r--indra/llmessage/llpacketring.h74
-rw-r--r--indra/llmessage/llpartdata.cpp307
-rw-r--r--indra/llmessage/llpartdata.h217
-rw-r--r--indra/llmessage/llpumpio.cpp1006
-rw-r--r--indra/llmessage/llpumpio.h406
-rw-r--r--indra/llmessage/llqueryflags.h32
-rw-r--r--indra/llmessage/llregionflags.h157
-rw-r--r--indra/llmessage/llregionhandle.h110
-rw-r--r--indra/llmessage/llsdappservices.cpp259
-rw-r--r--indra/llmessage/llsdappservices.h40
-rw-r--r--indra/llmessage/llsdhttpserver.cpp132
-rw-r--r--indra/llmessage/llsdhttpserver.h33
-rw-r--r--indra/llmessage/llsdrpcclient.cpp230
-rw-r--r--indra/llmessage/llsdrpcclient.h291
-rw-r--r--indra/llmessage/llsdrpcserver.cpp322
-rw-r--r--indra/llmessage/llsdrpcserver.h342
-rw-r--r--indra/llmessage/llservice.cpp93
-rw-r--r--indra/llmessage/llservice.h167
-rw-r--r--indra/llmessage/lltaskname.h42
-rw-r--r--indra/llmessage/llteleportflags.h43
-rw-r--r--indra/llmessage/llthrottle.cpp542
-rw-r--r--indra/llmessage/llthrottle.h81
-rw-r--r--indra/llmessage/lltransfermanager.cpp1270
-rw-r--r--indra/llmessage/lltransfermanager.h466
-rw-r--r--indra/llmessage/lltransfersourceasset.cpp235
-rw-r--r--indra/llmessage/lltransfersourceasset.h62
-rw-r--r--indra/llmessage/lltransfersourcefile.cpp151
-rw-r--r--indra/llmessage/lltransfersourcefile.h58
-rw-r--r--indra/llmessage/lltransfertargetfile.cpp104
-rw-r--r--indra/llmessage/lltransfertargetfile.h53
-rw-r--r--indra/llmessage/lltransfertargetvfile.cpp187
-rw-r--r--indra/llmessage/lltransfertargetvfile.h70
-rw-r--r--indra/llmessage/llurlrequest.cpp650
-rw-r--r--indra/llmessage/llurlrequest.h395
-rw-r--r--indra/llmessage/lluseroperation.cpp161
-rw-r--r--indra/llmessage/lluseroperation.h75
-rw-r--r--indra/llmessage/llvehicleparams.h105
-rw-r--r--indra/llmessage/llxfer.cpp350
-rw-r--r--indra/llmessage/llxfer.h98
-rw-r--r--indra/llmessage/llxfer_file.cpp416
-rw-r--r--indra/llmessage/llxfer_file.h68
-rw-r--r--indra/llmessage/llxfer_mem.cpp199
-rw-r--r--indra/llmessage/llxfer_mem.h61
-rw-r--r--indra/llmessage/llxfer_vfile.cpp320
-rw-r--r--indra/llmessage/llxfer_vfile.h74
-rw-r--r--indra/llmessage/llxfermanager.cpp1133
-rw-r--r--indra/llmessage/llxfermanager.h187
-rw-r--r--indra/llmessage/llxorcipher.cpp108
-rw-r--r--indra/llmessage/machine.h101
-rw-r--r--indra/llmessage/mean_collision_data.h81
-rw-r--r--indra/llmessage/message.cpp5876
-rw-r--r--indra/llmessage/message.h1253
-rw-r--r--indra/llmessage/message_prehash.cpp2964
-rw-r--r--indra/llmessage/message_prehash.h1498
-rw-r--r--indra/llmessage/message_string_table.cpp75
-rw-r--r--indra/llmessage/net.cpp516
-rw-r--r--indra/llmessage/net.h50
-rw-r--r--indra/llmessage/partsyspacket.cpp1277
-rw-r--r--indra/llmessage/partsyspacket.h243
-rw-r--r--indra/llmessage/patch_code.cpp390
-rw-r--r--indra/llmessage/patch_code.h28
-rw-r--r--indra/llmessage/patch_dct.cpp751
-rw-r--r--indra/llmessage/patch_dct.h73
-rw-r--r--indra/llmessage/patch_idct.cpp666
-rw-r--r--indra/llmessage/sound_ids.h293
123 files changed, 48042 insertions, 0 deletions
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
new file mode 100644
index 0000000000..b23567288d
--- /dev/null
+++ b/indra/llmessage/llassetstorage.cpp
@@ -0,0 +1,1116 @@
+/**
+ * @file llassetstorage.cpp
+ * @brief Implementation of the base asset storage system.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+// system library includes
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <algorithm>
+
+#include "llassetstorage.h"
+
+// linden library includes
+#include "llmath.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llsd.h"
+
+// this library includes
+#include "message.h"
+#include "llxfermanager.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "lldbstrings.h"
+
+#include "lltransfersourceasset.h"
+#include "lltransfertargetvfile.h" // For debugging
+
+LLAssetStorage *gAssetStorage = NULL;
+
+const LLUUID CATEGORIZE_LOST_AND_FOUND_ID("00000000-0000-0000-0000-000000000010");
+
+const F32 LL_ASSET_STORAGE_TIMEOUT = 300.0f; // anything that takes longer than this will abort
+
+
+
+///----------------------------------------------------------------------------
+/// LLAssetInfo
+///----------------------------------------------------------------------------
+
+LLAssetInfo::LLAssetInfo( void )
+: mDescription(),
+ mName(),
+ mUuid(),
+ mCreatorID(),
+ mType( LLAssetType::AT_NONE )
+{ }
+
+LLAssetInfo::LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id,
+ LLAssetType::EType type, const char* name,
+ const char* desc )
+: mUuid( object_id ),
+ mCreatorID( creator_id ),
+ mType( type )
+{
+ setName( name );
+ setDescription( desc );
+}
+
+LLAssetInfo::LLAssetInfo( const LLNameValue& nv )
+{
+ setFromNameValue( nv );
+}
+
+// make sure the name is short enough, and strip all pipes since they
+// are reserved characters in our inventory tracking system.
+void LLAssetInfo::setName( const std::string& name )
+{
+ if( !name.empty() )
+ {
+ mName.assign( name, 0, llmin((U32)name.size(), (U32)DB_INV_ITEM_NAME_STR_LEN) );
+ mName.erase( std::remove(mName.begin(), mName.end(), '|'),
+ mName.end() );
+ }
+}
+
+// make sure the name is short enough, and strip all pipes since they
+// are reserved characters in our inventory tracking system.
+void LLAssetInfo::setDescription( const std::string& desc )
+{
+ if( !desc.empty() )
+ {
+ mDescription.assign( desc, 0, llmin((U32)desc.size(),
+ (U32)DB_INV_ITEM_DESC_STR_LEN) );
+ mDescription.erase( std::remove(mDescription.begin(),
+ mDescription.end(), '|'),
+ mDescription.end() );
+ }
+}
+
+// Assets (aka potential inventory items) can be applied to an
+// object in the world. We'll store that as a string name value
+// pair where the name encodes part of asset info, and the value
+// the rest. LLAssetInfo objects will be responsible for parsing
+// the meaning out froman LLNameValue object. See the inventory
+// design docs for details. Briefly:
+// name=<inv_type>|<uuid>
+// value=<creatorid>|<name>|<description>|
+void LLAssetInfo::setFromNameValue( const LLNameValue& nv )
+{
+ std::string str;
+ std::string buf;
+ std::string::size_type pos1;
+ std::string::size_type pos2;
+
+ // convert the name to useful information
+ str.assign( nv.mName );
+ pos1 = str.find('|');
+ buf.assign( str, 0, pos1++ );
+ mType = LLAssetType::lookup( buf.c_str() );
+ buf.assign( str, pos1, std::string::npos );
+ mUuid.set( buf.c_str() );
+
+ // convert the value to useful information
+ str.assign( nv.getAsset() );
+ pos1 = str.find('|');
+ buf.assign( str, 0, pos1++ );
+ mCreatorID.set( buf.c_str() );
+ pos2 = str.find( '|', pos1 );
+ buf.assign( str, pos1, (pos2++) - pos1 );
+ setName( buf.c_str() );
+ buf.assign( str, pos2, std::string::npos );
+ setDescription( buf.c_str() );
+ llinfos << "uuid: " << mUuid << llendl;
+ llinfos << "creator: " << mCreatorID << llendl;
+}
+
+///----------------------------------------------------------------------------
+/// LLAssetRequest
+///----------------------------------------------------------------------------
+
+LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
+: mUUID(uuid),
+ mType(type),
+ mDownCallback( NULL ),
+ mUpCallback( NULL ),
+ mInfoCallback( NULL ),
+ mUserData( NULL ),
+ mHost(),
+ mIsTemp( FALSE ),
+ mIsLocal(FALSE),
+ mIsPriority(FALSE),
+ mDataSentInFirstPacket(FALSE),
+ mDataIsInVFS( FALSE )
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+LLAssetRequest::~LLAssetRequest()
+{
+}
+
+
+///----------------------------------------------------------------------------
+/// LLInvItemRequest
+///----------------------------------------------------------------------------
+
+LLInvItemRequest::LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType type)
+: mUUID(uuid),
+ mType(type),
+ mDownCallback( NULL ),
+ mUserData( NULL ),
+ mHost(),
+ mIsTemp( FALSE ),
+ mIsPriority(FALSE),
+ mDataSentInFirstPacket(FALSE),
+ mDataIsInVFS( FALSE )
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+LLInvItemRequest::~LLInvItemRequest()
+{
+}
+
+///----------------------------------------------------------------------------
+/// LLEstateAssetRequest
+///----------------------------------------------------------------------------
+
+LLEstateAssetRequest::LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType atype,
+ EstateAssetType etype)
+: mUUID(uuid),
+ mAType(atype),
+ mEstateAssetType(etype),
+ mDownCallback( NULL ),
+ mUserData( NULL ),
+ mHost(),
+ mIsTemp( FALSE ),
+ mIsPriority(FALSE),
+ mDataSentInFirstPacket(FALSE),
+ mDataIsInVFS( FALSE )
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+LLEstateAssetRequest::~LLEstateAssetRequest()
+{
+}
+
+
+///----------------------------------------------------------------------------
+/// LLAssetStorage
+///----------------------------------------------------------------------------
+
+// since many of these functions are called by the messaging and xfer systems,
+// they are declared as static and are passed a "this" handle
+// it's a C/C++ mish-mash!
+
+// TODO: permissions on modifications - maybe don't allow at all?
+// TODO: verify that failures get propogated down
+// TODO: rework tempfile handling?
+
+
+LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, const LLHost &upstream_host)
+{
+ _init(msg, xfer, vfs, upstream_host);
+}
+
+
+LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs)
+{
+ _init(msg, xfer, vfs, LLHost::invalid);
+}
+
+
+void LLAssetStorage::_init(LLMessageSystem *msg,
+ LLXferManager *xfer,
+ LLVFS *vfs,
+ const LLHost &upstream_host)
+{
+ mShutDown = FALSE;
+ mMessageSys = msg;
+ mXferManager = xfer;
+ mVFS = vfs;
+
+ setUpstream(upstream_host);
+ msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this);
+}
+
+LLAssetStorage::~LLAssetStorage()
+{
+ mShutDown = TRUE;
+
+ _cleanupRequests(TRUE, LL_ERR_CIRCUIT_GONE);
+
+ if (gMessageSystem)
+ {
+ // Warning! This won't work if there's more than one asset storage.
+ // unregister our callbacks with the message system
+ gMessageSystem->setHandlerFuncFast(_PREHASH_AssetUploadComplete, NULL, NULL);
+ }
+}
+
+void LLAssetStorage::setUpstream(const LLHost &upstream_host)
+{
+ llinfos << "AssetStorage: Setting upstream provider to " << upstream_host << llendl;
+
+ mUpstreamHost = upstream_host;
+}
+
+void LLAssetStorage::checkForTimeouts()
+{
+ _cleanupRequests(FALSE, LL_ERR_TCP_TIMEOUT);
+}
+
+void LLAssetStorage::_cleanupRequests(BOOL all, S32 error)
+{
+ const S32 NUM_QUEUES = 3;
+ F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
+
+ std::list<LLAssetRequest*>* requests[NUM_QUEUES];
+ requests[0] = &mPendingDownloads;
+ requests[1] = &mPendingUploads;
+ requests[2] = &mPendingLocalUploads;
+ static const char* REQUEST_TYPE[NUM_QUEUES] = { "download", "upload", "localuploads"};
+
+ std::list<LLAssetRequest*> timed_out;
+
+ for (S32 ii = 0; ii < NUM_QUEUES; ++ii)
+ {
+ for (std::list<LLAssetRequest*>::iterator iter = requests[ii]->begin();
+ iter != requests[ii]->end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ // if all is true, we want to clean up everything
+ // otherwise just check for timed out requests
+ // EXCEPT for upload timeouts
+ if (all
+ || ((0 == ii)
+ && LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime)))
+ {
+ llwarns << "Asset " << REQUEST_TYPE[ii] << " request "
+ << (all ? "aborted" : "timed out") << " for "
+ << tmp->getUUID() << "."
+ << LLAssetType::lookup(tmp->getType()) << llendl;
+
+ timed_out.push_front(tmp);
+ iter = requests[ii]->erase(curiter);
+ }
+ }
+ }
+
+ LLAssetInfo info;
+ for (std::list<LLAssetRequest*>::iterator iter = timed_out.begin();
+ iter != timed_out.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ if (tmp->mUpCallback)
+ {
+ tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error);
+ }
+ if (tmp->mDownCallback)
+ {
+ tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error);
+ }
+ if (tmp->mInfoCallback)
+ {
+ tmp->mInfoCallback(&info, tmp->mUserData, error);
+ }
+ delete tmp;
+ }
+
+}
+
+BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type)
+{
+ return mVFS->getExists(uuid, type);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// GET routines
+///////////////////////////////////////////////////////////////////////////
+
+// IW - uuid is passed by value to avoid side effects, please don't re-add &
+void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *,S32), void *user_data, BOOL is_priority)
+{
+ lldebugs << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << llendl;
+
+ if (mShutDown)
+ {
+ return; // don't get the asset or do any callbacks, we are shutting down
+ }
+
+ if (uuid.isNull())
+ {
+ // Special case early out for NULL uuid
+ if (callback)
+ {
+ callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
+ }
+ return;
+ }
+
+ BOOL exists = mVFS->getExists(uuid, type);
+ LLVFile file(mVFS, uuid, type);
+ U32 size = exists ? file.getSize() : 0;
+
+ if (size < 1)
+ {
+ if (exists)
+ {
+ llwarns << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << llendl;
+ file.remove();
+ }
+
+ BOOL duplicate = FALSE;
+
+ // check to see if there's a pending download of this uuid already
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingDownloads.begin();
+ iter != mPendingDownloads.end(); ++iter )
+ {
+ LLAssetRequest *tmp = *iter;
+ if ((type == tmp->getType()) && (uuid == tmp->getUUID()))
+ {
+ if (callback == tmp->mDownCallback && user_data == tmp->mUserData)
+ {
+ // this is a duplicate from the same subsystem - throw it away
+ llinfos << "Discarding duplicate request for UUID " << uuid << llendl;
+ return;
+ }
+ else
+ {
+ llinfos << "Adding additional non-duplicate request for UUID " << uuid << llendl;
+ }
+
+ // this is a duplicate request
+ // queue the request, but don't actually ask for it again
+ duplicate = TRUE;
+ break;
+ }
+ }
+
+ // This can be overridden by subclasses
+ _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority);
+ }
+ else
+ {
+ // we've already got the file
+ // theoretically, partial files w/o a pending request shouldn't happen
+ // unless there's a weird error
+ if (callback)
+ {
+ callback(mVFS, uuid, type, user_data, LL_ERR_NOERR);
+ }
+ }
+}
+
+void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype,
+ LLGetAssetCallback callback,
+ void *user_data, BOOL duplicate,
+ BOOL is_priority)
+{
+ if (mUpstreamHost.isOk())
+ {
+ // stash the callback info so we can find it after we get the response message
+ LLAssetRequest *req = new LLAssetRequest(uuid, atype);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ mPendingDownloads.push_back(req);
+
+ if (!duplicate)
+ {
+ // send request message to our upstream data provider
+ // Create a new asset transfer.
+ LLTransferSourceParamsAsset spa;
+ spa.setAsset(uuid, atype);
+
+ // Set our destination file, and the completion callback.
+ LLTransferTargetParamsVFile tpvf;
+ tpvf.setAsset(uuid, atype);
+ tpvf.setCallback(downloadCompleteCallback, req);
+
+ llinfos << "Starting transfer for " << uuid << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
+ ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+ }
+ }
+ else
+ {
+ // uh-oh, we shouldn't have gotten here
+ llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+}
+
+
+void LLAssetStorage::downloadCompleteCallback(S32 result, void *user_data)
+{
+ LLAssetRequest* req = (LLAssetRequest *)user_data;
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
+ return;
+ }
+
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+ LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset " << req->getUUID() << llendl;
+
+ result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ vfile.remove();
+ }
+ }
+
+ // find and callback ALL pending requests for this UUID
+ // SJB: We process the callbacks in reverse order, I do not know if this is important,
+ // but I didn't want to mess with it.
+ std::list<LLAssetRequest*> requests;
+ for (std::list<LLAssetRequest*>::iterator iter = gAssetStorage->mPendingDownloads.begin();
+ iter != gAssetStorage->mPendingDownloads.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ if ((tmp->getUUID() == req->getUUID()) && (tmp->getType()== req->getType()))
+ {
+ requests.push_front(tmp);
+ iter = gAssetStorage->mPendingDownloads.erase(curiter);
+ }
+ }
+ for (std::list<LLAssetRequest*>::iterator iter = requests.begin();
+ iter != requests.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ if (tmp->mDownCallback)
+ {
+ tmp->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), tmp->mUserData, result);
+ }
+ delete tmp;
+ }
+}
+
+void LLAssetStorage::getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype,
+ LLGetAssetCallback callback, void *user_data, BOOL is_priority)
+{
+ lldebugs << "LLAssetStorage::getEstateAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << ", estatetype " << etype << llendl;
+
+ //
+ // Probably will get rid of this early out?
+ //
+ if (asset_id.isNull())
+ {
+ // Special case early out for NULL uuid
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
+ }
+ return;
+ }
+
+ BOOL exists = mVFS->getExists(asset_id, atype);
+ LLVFile file(mVFS, asset_id, atype);
+ U32 size = exists ? file.getSize() : 0;
+
+ if (size < 1)
+ {
+ if (exists)
+ {
+ llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
+ file.remove();
+ }
+
+ // See whether we should talk to the object's originating sim, or the upstream provider.
+ LLHost source_host;
+ if (object_sim.isOk())
+ {
+ source_host = object_sim;
+ }
+ else
+ {
+ source_host = mUpstreamHost;
+ }
+ if (source_host.isOk())
+ {
+ // stash the callback info so we can find it after we get the response message
+ LLEstateAssetRequest *req = new LLEstateAssetRequest(asset_id, atype, etype);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ // send request message to our upstream data provider
+ // Create a new asset transfer.
+ LLTransferSourceParamsEstate spe;
+ spe.setAgentSession(agent_id, session_id);
+ spe.setEstateAssetType(etype);
+
+ // Set our destination file, and the completion callback.
+ LLTransferTargetParamsVFile tpvf;
+ tpvf.setAsset(asset_id, atype);
+ tpvf.setCallback(downloadEstateAssetCompleteCallback, req);
+
+ llinfos << "Starting transfer for " << asset_id << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
+ ttcp->requestTransfer(spe, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+ }
+ else
+ {
+ // uh-oh, we shouldn't have gotten here
+ llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+ }
+ else
+ {
+ // we've already got the file
+ // theoretically, partial files w/o a pending request shouldn't happen
+ // unless there's a weird error
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR);
+ }
+ }
+}
+
+void LLAssetStorage::downloadEstateAssetCompleteCallback(S32 result, void *user_data)
+{
+ LLEstateAssetRequest *req = (LLEstateAssetRequest *)user_data;
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
+ return;
+ }
+
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+ LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType());
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
+
+ result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ vfile.remove();
+ }
+ }
+
+ req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result);
+}
+
+void LLAssetStorage::getInvItemAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id,
+ const LLUUID &asset_id, LLAssetType::EType atype,
+ LLGetAssetCallback callback, void *user_data, BOOL is_priority)
+{
+ lldebugs << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << llendl;
+
+ //
+ // Probably will get rid of this early out?
+ //
+ if (asset_id.isNull())
+ {
+ // Special case early out for NULL uuid
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
+ }
+ return;
+ }
+
+ BOOL exists = mVFS->getExists(asset_id, atype);
+ LLVFile file(mVFS, asset_id, atype);
+ U32 size = exists ? file.getSize() : 0;
+
+ if (size < 1)
+ {
+ if (exists)
+ {
+ llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
+ file.remove();
+ }
+
+ // See whether we should talk to the object's originating sim, or the upstream provider.
+ LLHost source_host;
+ if (object_sim.isOk())
+ {
+ source_host = object_sim;
+ }
+ else
+ {
+ source_host = mUpstreamHost;
+ }
+ if (source_host.isOk())
+ {
+ // stash the callback info so we can find it after we get the response message
+ LLInvItemRequest *req = new LLInvItemRequest(asset_id, atype);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ // send request message to our upstream data provider
+ // Create a new asset transfer.
+ LLTransferSourceParamsInvItem spi;
+ spi.setAgentSession(agent_id, session_id);
+ spi.setInvItem(owner_id, task_id, item_id);
+ spi.setAsset(asset_id, atype);
+
+ // Set our destination file, and the completion callback.
+ LLTransferTargetParamsVFile tpvf;
+ tpvf.setAsset(asset_id, atype);
+ tpvf.setCallback(downloadInvItemCompleteCallback, req);
+
+ llinfos << "Starting transfer for " << asset_id << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
+ ttcp->requestTransfer(spi, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+ }
+ else
+ {
+ // uh-oh, we shouldn't have gotten here
+ llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+ }
+ else
+ {
+ // we've already got the file
+ // theoretically, partial files w/o a pending request shouldn't happen
+ // unless there's a weird error
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR);
+ }
+ }
+}
+
+
+void LLAssetStorage::downloadInvItemCompleteCallback(S32 result, void *user_data)
+{
+ LLInvItemRequest *req = (LLInvItemRequest *)user_data;
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
+ return;
+ }
+
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+ LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
+
+ result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ vfile.remove();
+ }
+ }
+
+ req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Store routines
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// virtual
+void LLAssetStorage::cancelStoreAsset(
+ const LLUUID& uuid,
+ LLAssetType::EType atype)
+{
+ bool do_callback = true;
+ LLAssetRequest* req = NULL;
+
+ if(mPendingUploads.size() > 0)
+ {
+ req = mPendingUploads.front();
+ if((req->getUUID() == uuid) && (req->getType() == atype))
+ {
+ // This is probably because the request is in progress - do
+ // not attempt to cancel.
+ do_callback = false;
+ }
+ }
+
+ if (mPendingLocalUploads.size() > 0)
+ {
+ req = mPendingLocalUploads.front();
+ if((req->getUUID() == uuid) && (req->getType() == atype))
+ {
+ // This is probably because the request is in progress - do
+ // not attempt to cancel.
+ do_callback = false;
+ }
+ }
+
+ if (do_callback)
+ {
+ // clear it out of the upload queue if it is there.
+ _callUploadCallbacks(uuid, atype, FALSE);
+ }
+}
+
+// static
+void LLAssetStorage::uploadCompleteCallback(const LLUUID& uuid, void *user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << llendl;
+ return;
+ }
+ LLAssetRequest *req = (LLAssetRequest *)user_data;
+ BOOL success = TRUE;
+
+ if (result)
+ {
+ llwarns << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << llendl;
+ success = FALSE;
+ }
+
+ // we're done grabbing the file, tell the client
+ gAssetStorage->mMessageSys->newMessageFast(_PREHASH_AssetUploadComplete);
+ gAssetStorage->mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
+ gAssetStorage->mMessageSys->addUUIDFast(_PREHASH_UUID, uuid);
+ gAssetStorage->mMessageSys->addS8Fast(_PREHASH_Type, req->getType());
+ gAssetStorage->mMessageSys->addBOOLFast(_PREHASH_Success, success);
+ gAssetStorage->mMessageSys->sendReliable(req->mHost);
+
+ delete req;
+}
+
+void LLAssetStorage::processUploadComplete(LLMessageSystem *msg, void **user_data)
+{
+ LLAssetStorage *this_ptr = (LLAssetStorage *)user_data;
+ LLUUID uuid;
+ S8 asset_type_s8;
+ LLAssetType::EType asset_type;
+ BOOL success = FALSE;
+
+ msg->getUUIDFast(_PREHASH_AssetBlock, _PREHASH_UUID, uuid);
+ msg->getS8Fast(_PREHASH_AssetBlock, _PREHASH_Type, asset_type_s8);
+ msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success);
+
+ asset_type = (LLAssetType::EType)asset_type_s8;
+ this_ptr->_callUploadCallbacks(uuid, asset_type, success);
+}
+
+void LLAssetStorage::_callUploadCallbacks(const LLUUID &uuid, LLAssetType::EType asset_type, BOOL success)
+{
+ // SJB: We process the callbacks in reverse order, I do not know if this is important,
+ // but I didn't want to mess with it.
+ std::list<LLAssetRequest*> requests;
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingUploads.begin();
+ iter != mPendingUploads.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* req = *curiter;
+ if ((req->getUUID() == uuid) && (req->getType() == asset_type))
+ {
+ requests.push_front(req);
+ iter = mPendingUploads.erase(curiter);
+ }
+ }
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingLocalUploads.begin();
+ iter != mPendingLocalUploads.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* req = *curiter;
+ if ((req->getUUID() == uuid) && (req->getType() == asset_type))
+ {
+ requests.push_front(req);
+ iter = mPendingLocalUploads.erase(curiter);
+ }
+ }
+ for (std::list<LLAssetRequest*>::iterator iter = requests.begin();
+ iter != requests.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* req = *curiter;
+ if (req->mUpCallback)
+ {
+ req->mUpCallback(uuid, req->mUserData, (success ? LL_ERR_NOERR : LL_ERR_ASSET_REQUEST_FAILED ));
+ }
+ delete req;
+ }
+}
+
+
+S32 LLAssetStorage::getNumPendingDownloads() const
+{
+ return mPendingDownloads.size();
+}
+
+S32 LLAssetStorage::getNumPendingUploads() const
+{
+ return mPendingUploads.size();
+}
+
+S32 LLAssetStorage::getNumPendingLocalUploads()
+{
+ return mPendingLocalUploads.size();
+}
+
+LLSD LLAssetStorage::getPendingTypes(const std::list<LLAssetRequest*>& requests) const
+{
+ LLSD type_counts;
+ std::list<LLAssetRequest*>::const_iterator it = requests.begin();
+ std::list<LLAssetRequest*>::const_iterator end = requests.end();
+ for ( ; it != end; ++it)
+ {
+ LLAssetRequest* req = *it;
+
+ const char* type_name = LLAssetType::lookupHumanReadable(req->getType());
+ type_counts[type_name] = type_counts[type_name].asInteger() + 1;
+ }
+ return type_counts;
+}
+
+LLSD LLAssetStorage::getPendingDownloadTypes() const
+{
+ return getPendingTypes(mPendingDownloads);
+}
+
+LLSD LLAssetStorage::getPendingUploadTypes() const
+{
+ return getPendingTypes(mPendingUploads);
+}
+
+// static
+const char* LLAssetStorage::getErrorString(S32 status)
+{
+ switch( status )
+ {
+ case LL_ERR_NOERR:
+ return "No error";
+
+ case LL_ERR_ASSET_REQUEST_FAILED:
+ return "Asset request: failed";
+
+ case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE:
+ return "Asset request: non-existent file";
+
+ case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
+ return "Asset request: asset not found in database";
+
+ case LL_ERR_EOF:
+ return "End of file";
+
+ case LL_ERR_CANNOT_OPEN_FILE:
+ return "Cannot open file";
+
+ case LL_ERR_FILE_NOT_FOUND:
+ return "File not found";
+
+ case LL_ERR_TCP_TIMEOUT:
+ return "File transfer timeout";
+
+ case LL_ERR_CIRCUIT_GONE:
+ return "Circuit gone";
+
+ default:
+ return "Unknown status";
+ }
+}
+
+
+
+void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32), void *user_data, BOOL is_priority)
+{
+ // check for duplicates here, since we're about to fool the normal duplicate checker
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingDownloads.begin();
+ iter != mPendingDownloads.end(); )
+ {
+ LLAssetRequest* tmp = *iter++;
+ if (type == tmp->getType() &&
+ uuid == tmp->getUUID() &&
+ legacyGetDataCallback == tmp->mDownCallback &&
+ callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback &&
+ user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData)
+ {
+ // this is a duplicate from the same subsystem - throw it away
+ llinfos << "Discarding duplicate request for UUID " << uuid << llendl;
+ return;
+ }
+ }
+
+
+ LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
+
+ legacy->mDownCallback = callback;
+ legacy->mUserData = user_data;
+
+ getAssetData(uuid, type, legacyGetDataCallback, (void **)legacy,
+ is_priority);
+}
+
+// static
+void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status)
+{
+ LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
+ char filename[LL_MAX_PATH] = ""; /* Flawfinder: ignore */
+
+ if (! status)
+ {
+ LLVFile file(vfs, uuid, type);
+
+ char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */
+
+ uuid.toString(uuid_str);
+ snprintf(filename,sizeof(filename),"%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type)); /* Flawfinder: ignore */
+
+ FILE *fp = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
+ if (fp)
+ {
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while (file.read(copy_buf, buf_size))
+ {
+ if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1)
+ {
+ // return a bad file error if we can't write the whole thing
+ status = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ status = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+
+ legacy->mDownCallback(filename, uuid, legacy->mUserData, status);
+ delete legacy;
+}
+
+// this is overridden on the viewer and the sim, so it doesn't really do anything
+// virtual
+void LLAssetStorage::storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority,
+ bool store_local)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// virtual
+// this does nothing, viewer and sim both override this.
+void LLAssetStorage::storeAssetData(
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file ,
+ bool is_priority,
+ bool store_local,
+ const LLUUID& requesting_agent_id)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// virtual
+// this does nothing, viewer and sim both override this.
+void LLAssetStorage::storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// virtual
+// this does nothing, viewer and sim both override this.
+void LLAssetStorage::storeAssetData(
+ const char* filename,
+ const LLTransactionID &transactoin_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// static
+void LLAssetStorage::legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status)
+{
+ LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
+ if (legacy && legacy->mUpCallback)
+ {
+ legacy->mUpCallback(uuid, legacy->mUserData, status);
+ }
+ delete legacy;
+}
+
+// virtual
+void LLAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
+{ }
+
+// virtual
+BOOL LLAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
+{ return FALSE; }
+
+// virtual
+std::string LLAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
+{ return std::string(); }
+
+// virtual
+LLUUID LLAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
+{ return LLUUID::null; }
+
+// virtual
+void LLAssetStorage::removeTempAssetData(const LLUUID& asset_id)
+{ }
+
+// virtual
+void LLAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
+{ }
+
+// virtual
+void LLAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
+{ }
+
+// virtual
+void LLAssetStorage::clearTempAssetData()
+{ }
diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h
new file mode 100644
index 0000000000..4ccbb08abd
--- /dev/null
+++ b/indra/llmessage/llassetstorage.h
@@ -0,0 +1,328 @@
+/**
+ * @file llassetstorage.h
+ * @brief definition of LLAssetStorage class which allows simple
+ * up/downloads of uuid,type asets
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLASSETSTORAGE_H
+#define LL_LLASSETSTORAGE_H
+
+#include <string>
+
+#include "lluuid.h"
+#include "lltimer.h"
+#include "llnamevalue.h"
+#include "llhost.h"
+#include "stdenums.h" // for EDragAndDropType
+#include "lltransfermanager.h" // For LLTSCode enum
+#include "llassettype.h"
+#include "llstring.h"
+
+// Forward declarations
+class LLMessageSystem;
+class LLXferManager;
+class LLAssetStorage;
+class LLVFS;
+class LLSD;
+
+class LLAssetInfo
+{
+protected:
+ std::string mDescription;
+ std::string mName;
+
+public:
+ LLUUID mUuid;
+ LLTransactionID mTransactionID;
+ LLUUID mCreatorID;
+ LLAssetType::EType mType;
+
+ LLAssetInfo( void );
+ LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id,
+ LLAssetType::EType type, const char* name, const char* desc );
+ LLAssetInfo( const LLNameValue& nv );
+
+ const std::string& getName( void ) const { return mName; }
+ const std::string& getDescription( void ) const { return mDescription; }
+ void setName( const std::string& name );
+ void setDescription( const std::string& desc );
+
+ // Assets (aka potential inventory items) can be applied to an
+ // object in the world. We'll store that as a string name value
+ // pair where the name encodes part of asset info, and the value
+ // the rest. LLAssetInfo objects will be responsible for parsing
+ // the meaning out froman LLNameValue object. See the inventory
+ // design docs for details.
+ void setFromNameValue( const LLNameValue& nv );
+};
+
+
+class LLAssetRequest
+{
+public:
+ LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType at);
+ virtual ~LLAssetRequest();
+
+ LLUUID getUUID() const { return mUUID; }
+ LLAssetType::EType getType() const { return mType; }
+protected:
+ LLUUID mUUID;
+ LLAssetType::EType mType;
+
+public:
+ void (*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32);
+ void (*mUpCallback)(const LLUUID&, void *, S32);
+ void (*mInfoCallback)(LLAssetInfo *, void *, S32);
+
+ void *mUserData;
+ LLHost mHost;
+ BOOL mIsTemp;
+ BOOL mIsLocal;
+ F64 mTime; // Message system time
+ BOOL mIsPriority;
+ BOOL mDataSentInFirstPacket;
+ BOOL mDataIsInVFS;
+ LLUUID mRequestingAgentID; // Only valid for uploads from an agent
+};
+
+
+class LLInvItemRequest
+{
+public:
+ LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType at);
+ virtual ~LLInvItemRequest();
+
+ LLUUID getUUID() const { return mUUID; }
+ LLAssetType::EType getType() const { return mType; }
+protected:
+ LLUUID mUUID;
+ LLAssetType::EType mType;
+
+public:
+ void (*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32);
+
+ void *mUserData;
+ LLHost mHost;
+ BOOL mIsTemp;
+ F64 mTime; // Message system time
+ BOOL mIsPriority;
+ BOOL mDataSentInFirstPacket;
+ BOOL mDataIsInVFS;
+
+};
+
+class LLEstateAssetRequest
+{
+public:
+ LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType at, EstateAssetType et);
+ virtual ~LLEstateAssetRequest();
+
+ LLUUID getUUID() const { return mUUID; }
+ LLAssetType::EType getAType() const { return mAType; }
+protected:
+ LLUUID mUUID;
+ LLAssetType::EType mAType;
+ EstateAssetType mEstateAssetType;
+
+public:
+ void (*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32);
+
+ void *mUserData;
+ LLHost mHost;
+ BOOL mIsTemp;
+ F64 mTime; // Message system time
+ BOOL mIsPriority;
+ BOOL mDataSentInFirstPacket;
+ BOOL mDataIsInVFS;
+
+};
+
+
+
+
+typedef void (*LLGetAssetCallback)(LLVFS *vfs, const LLUUID &asset_id,
+ LLAssetType::EType asset_type, void *user_data, S32 status);
+
+class LLAssetStorage
+{
+public:
+ // VFS member is public because static child methods need it :(
+ LLVFS *mVFS;
+ typedef void (*LLStoreAssetCallback)(const LLUUID &asset_id, void *user_data, S32 status);
+
+protected:
+ BOOL mShutDown;
+ LLHost mUpstreamHost;
+
+ LLMessageSystem *mMessageSys;
+ LLXferManager *mXferManager;
+
+ std::list<LLAssetRequest*> mPendingDownloads;
+ std::list<LLAssetRequest*> mPendingUploads;
+ std::list<LLAssetRequest*> mPendingLocalUploads;
+
+public:
+ LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host);
+
+ LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs);
+ virtual ~LLAssetStorage();
+
+ void setUpstream(const LLHost &upstream_host);
+
+ virtual BOOL hasLocalAsset(const LLUUID &uuid, LLAssetType::EType type);
+
+ // public interface methods
+ // note that your callback may get called BEFORE the function returns
+
+ virtual void getAssetData(const LLUUID uuid, LLAssetType::EType atype, LLGetAssetCallback cb, void *user_data, BOOL is_priority = FALSE);
+
+ /*
+ * TransactionID version
+ * Viewer needs the store_local
+ */
+ virtual void storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false);
+
+ /*
+ * AssetID version
+ * Sim needs both store_local and requesting_agent_id.
+ */
+ virtual void storeAssetData(
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false,
+ const LLUUID& requesting_agent_id = LLUUID::null);
+
+ // This call will attempt to clear a store asset. This will only
+ // attempt to cancel an upload that has not yet begun. The
+ // callback will be called with an error code.
+ virtual void cancelStoreAsset(
+ const LLUUID& uuid,
+ LLAssetType::EType oatype);
+
+ virtual void checkForTimeouts();
+
+ void getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype,
+ LLGetAssetCallback callback, void *user_data, BOOL is_priority);
+
+ void getInvItemAsset(const LLHost &object_sim,
+ const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id,
+ const LLUUID &asset_id, LLAssetType::EType atype,
+ LLGetAssetCallback cb, void *user_data, BOOL is_priority = FALSE); // Get a particular inventory item.
+
+
+ S32 getNumPendingDownloads() const;
+ S32 getNumPendingUploads() const;
+ S32 getNumPendingLocalUploads();
+
+ // Returns a map from type to num pending, eg 'texture' => 5, 'object' => 10
+ LLSD getPendingDownloadTypes() const;
+ LLSD getPendingUploadTypes() const;
+
+ // download process callbacks
+ static void downloadCompleteCallback(const S32 result, void *user_data);
+ static void downloadEstateAssetCompleteCallback(const S32 result, void *user_data);
+ static void downloadInvItemCompleteCallback(const S32 result, void *user_data);
+
+ // upload process callbacks
+ static void uploadCompleteCallback(const LLUUID&, void *user_data, S32 result);
+ static void processUploadComplete(LLMessageSystem *msg, void **this_handle);
+
+ // debugging
+ static const char* getErrorString( S32 status );
+
+ // deprecated file-based methods
+ void getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32), void *user_data, BOOL is_priority = FALSE);
+
+ /*
+ * AssetID version.
+ */
+ virtual void storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false);
+
+ /*
+ * TransactionID version
+ */
+ virtual void storeAssetData(
+ const char * filename,
+ const LLTransactionID &transaction_id,
+ LLAssetType::EType type,
+ LLStoreAssetCallback callback,
+ void *user_data,
+ bool temp_file = false,
+ bool is_priority = false);
+
+ static void legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status);
+ static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status);
+
+ // Temp assets are stored on sim nodes, they have agent ID and location data associated with them.
+ // This is a no-op for non-http asset systems
+ virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name);
+ virtual BOOL hasTempAssetData(const LLUUID& texture_id) const;
+ virtual std::string getTempAssetHostName(const LLUUID& texture_id) const;
+ virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const;
+ virtual void removeTempAssetData(const LLUUID& asset_id);
+ virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id);
+ // Pass LLUUID::null for all
+ virtual void dumpTempAssetData(const LLUUID& avatar_id) const;
+ virtual void clearTempAssetData();
+
+ // add extra methods to handle metadata
+
+protected:
+ void _cleanupRequests(BOOL all, S32 error);
+ void _callUploadCallbacks(const LLUUID &uuid, const LLAssetType::EType asset_type, BOOL success);
+
+ virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
+ void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
+ void *user_data, BOOL duplicate,
+ BOOL is_priority);
+
+private:
+ void _init(LLMessageSystem *msg,
+ LLXferManager *xfer,
+ LLVFS *vfs,
+ const LLHost &upstream_host);
+ LLSD getPendingTypes(const std::list<LLAssetRequest*>& requests) const;
+
+};
+
+////////////////////////////////////////////////////////////////////////
+// Wrappers to replicate deprecated API
+////////////////////////////////////////////////////////////////////////
+
+class LLLegacyAssetRequest
+{
+public:
+ void (*mDownCallback)(const char *, const LLUUID&, void *, S32);
+ LLAssetStorage::LLStoreAssetCallback mUpCallback;
+
+ void *mUserData;
+};
+
+extern LLAssetStorage *gAssetStorage;
+extern const LLUUID CATEGORIZE_LOST_AND_FOUND_ID;
+#endif
diff --git a/indra/llmessage/llbuffer.cpp b/indra/llmessage/llbuffer.cpp
new file mode 100644
index 0000000000..009387598b
--- /dev/null
+++ b/indra/llmessage/llbuffer.cpp
@@ -0,0 +1,746 @@
+/**
+ * @file llbuffer.cpp
+ * @author Phoenix
+ * @date 2005-09-20
+ * @brief Implementation of the segments, buffers, and buffer arrays.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llbuffer.h"
+
+#include "llmath.h"
+#include "llmemtype.h"
+#include "llstl.h"
+
+/**
+ * LLSegment
+ */
+LLSegment::LLSegment() :
+ mChannel(0),
+ mData(NULL),
+ mSize(0)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLSegment::LLSegment(S32 channel, U8* data, S32 data_len) :
+ mChannel(channel),
+ mData(data),
+ mSize(data_len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLSegment::~LLSegment()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+bool LLSegment::isOnChannel(S32 channel) const
+{
+ return (mChannel == channel);
+}
+
+S32 LLSegment::getChannel() const
+{
+ return mChannel;
+}
+
+void LLSegment::setChannel(S32 channel)
+{
+ mChannel = channel;
+}
+
+
+U8* LLSegment::data() const
+{
+ return mData;
+}
+
+S32 LLSegment::size() const
+{
+ return mSize;
+}
+
+
+/**
+ * LLHeapBuffer
+ */
+LLHeapBuffer::LLHeapBuffer()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ const S32 DEFAULT_HEAP_BUFFER_SIZE = 16384;
+ allocate(DEFAULT_HEAP_BUFFER_SIZE);
+}
+
+LLHeapBuffer::LLHeapBuffer(S32 size)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ allocate(size);
+}
+
+LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if((len > 0) && src)
+ {
+ allocate(len);
+ if(mBuffer)
+ {
+ memcpy(mBuffer, src, len);
+ }
+ }
+ else
+ {
+ mBuffer = NULL;
+ mSize = 0;
+ mNextFree = NULL;
+ }
+}
+
+// virtual
+LLHeapBuffer::~LLHeapBuffer()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ delete[] mBuffer;
+ mBuffer = NULL;
+ mSize = 0;
+ mNextFree = NULL;
+}
+
+// virtual
+//S32 LLHeapBuffer::bytesLeft() const
+//{
+// return (mSize - (mNextFree - mBuffer));
+//}
+
+// virtual
+bool LLHeapBuffer::createSegment(
+ S32 channel,
+ S32 size,
+ LLSegment& segment)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ // get actual size of the segment.
+ S32 actual_size = llmin(size, (mSize - S32(mNextFree - mBuffer)));
+
+ // bail if we cannot build a valid segment
+ if(actual_size <= 0)
+ {
+ return false;
+ }
+
+ // Yay, we're done.
+ segment = LLSegment(channel, mNextFree, actual_size);
+ mNextFree += actual_size;
+ return true;
+}
+
+void LLHeapBuffer::allocate(S32 size)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ mBuffer = new U8[size];
+ if(mBuffer)
+ {
+ mSize = size;
+ mNextFree = mBuffer;
+ }
+}
+
+
+/**
+ * LLBufferArray
+ */
+LLBufferArray::LLBufferArray() :
+ mNextBaseChannel(0)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLBufferArray::~LLBufferArray()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::for_each(mBuffers.begin(), mBuffers.end(), DeletePointer());
+}
+
+// static
+LLChannelDescriptors LLBufferArray::makeChannelConsumer(
+ const LLChannelDescriptors& channels)
+{
+ LLChannelDescriptors rv(channels.out());
+ return rv;
+}
+
+LLChannelDescriptors LLBufferArray::nextChannel()
+{
+ LLChannelDescriptors rv(mNextBaseChannel++);
+ return rv;
+}
+
+bool LLBufferArray::append(S32 channel, const U8* src, S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::vector<LLSegment> segments;
+ if(copyIntoBuffers(channel, src, len, segments))
+ {
+ mSegments.insert(mSegments.end(), segments.begin(), segments.end());
+ return true;
+ }
+ return false;
+}
+
+bool LLBufferArray::prepend(S32 channel, const U8* src, S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::vector<LLSegment> segments;
+ if(copyIntoBuffers(channel, src, len, segments))
+ {
+ mSegments.insert(mSegments.begin(), segments.begin(), segments.end());
+ return true;
+ }
+ return false;
+}
+
+bool LLBufferArray::insertAfter(
+ segment_iterator_t segment,
+ S32 channel,
+ const U8* src,
+ S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::vector<LLSegment> segments;
+ if(mSegments.end() != segment)
+ {
+ ++segment;
+ }
+ if(copyIntoBuffers(channel, src, len, segments))
+ {
+ mSegments.insert(segment, segments.begin(), segments.end());
+ return true;
+ }
+ return false;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ segment_iterator_t end = mSegments.end();
+ segment_iterator_t it = getSegment(address);
+ if(it == end)
+ {
+ return end;
+ }
+
+ // We have the location and the segment.
+ U8* base = (*it).data();
+ S32 size = (*it).size();
+ if(address == (base + size))
+ {
+ // No need to split, since this is the last byte of the
+ // segment. We do not want to have zero length segments, since
+ // that will only incur processing overhead with no advantage.
+ return it;
+ }
+ S32 channel = (*it).getChannel();
+ LLSegment segment1(channel, base, (address - base) + 1);
+ *it = segment1;
+ segment_iterator_t rv = it;
+ ++it;
+ LLSegment segment2(channel, address + 1, size - (address - base) - 1);
+ mSegments.insert(it, segment2);
+ return rv;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
+{
+ return mSegments.begin();
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::endSegment()
+{
+ return mSegments.end();
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::constructSegmentAfter(
+ U8* address,
+ LLSegment& segment)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ segment_iterator_t rv = mSegments.begin();
+ segment_iterator_t end = mSegments.end();
+ if(!address)
+ {
+ if(rv != end)
+ {
+ segment = (*rv);
+ }
+ }
+ else
+ {
+ // we have an address - find the segment it is in.
+ for( ; rv != end; ++rv)
+ {
+ if((address >= (*rv).data())
+ && (address < ((*rv).data() + (*rv).size())))
+ {
+ if((++address) < ((*rv).data() + (*rv).size()))
+ {
+ // it's in this segment - construct an appropriate
+ // sub-segment.
+ segment = LLSegment(
+ (*rv).getChannel(),
+ address,
+ (*rv).size() - (address - (*rv).data()));
+ }
+ else
+ {
+ ++rv;
+ if(rv != end)
+ {
+ segment = (*rv);
+ }
+ }
+ break;
+ }
+ }
+ }
+ if(rv == end)
+ {
+ segment = LLSegment();
+ }
+ return rv;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::getSegment(U8* address)
+{
+ segment_iterator_t end = mSegments.end();
+ if(!address)
+ {
+ return end;
+ }
+ segment_iterator_t it = mSegments.begin();
+ for( ; it != end; ++it)
+ {
+ if((address >= (*it).data())&&(address < (*it).data() + (*it).size()))
+ {
+ // found it.
+ return it;
+ }
+ }
+ return end;
+}
+
+LLBufferArray::const_segment_iterator_t LLBufferArray::getSegment(
+ U8* address) const
+{
+ const_segment_iterator_t end = mSegments.end();
+ if(!address)
+ {
+ return end;
+ }
+ const_segment_iterator_t it = mSegments.begin();
+ for( ; it != end; ++it)
+ {
+ if((address >= (*it).data())
+ && (address < (*it).data() + (*it).size()))
+ {
+ // found it.
+ return it;
+ }
+ }
+ return end;
+}
+
+/*
+U8* LLBufferArray::getAddressAfter(U8* address)
+{
+ U8* rv = NULL;
+ segment_iterator_t it = getSegment(address);
+ segment_iterator_t end = mSegments.end();
+ if(it != end)
+ {
+ if(++address < ((*it).data() + (*it).size()))
+ {
+ // it's in the same segment
+ rv = address;
+ }
+ else
+ {
+ // it's in the next segment
+ if(++it != end)
+ {
+ rv = (*it).data();
+ }
+ }
+ }
+ return rv;
+}
+*/
+
+S32 LLBufferArray::countAfter(S32 channel, U8* start) const
+{
+ S32 count = 0;
+ S32 offset = 0;
+ const_segment_iterator_t it;
+ const_segment_iterator_t end = mSegments.end();
+ if(start)
+ {
+ it = getSegment(start);
+ if(it == end)
+ {
+ return count;
+ }
+ if(++start < ((*it).data() + (*it).size()))
+ {
+ // it's in the same segment
+ offset = start - (*it).data();
+ }
+ else if(++it == end)
+ {
+ // it's in the next segment
+ return count;
+ }
+ }
+ else
+ {
+ it = mSegments.begin();
+ }
+ while(it != end)
+ {
+ if((*it).isOnChannel(channel))
+ {
+ count += (*it).size() - offset;
+ }
+ offset = 0;
+ ++it;
+ }
+ return count;
+}
+
+U8* LLBufferArray::readAfter(
+ S32 channel,
+ U8* start,
+ U8* dest,
+ S32& len) const
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ U8* rv = start;
+ if(!dest || len <= 0)
+ {
+ return rv;
+ }
+ S32 bytes_left = len;
+ len = 0;
+ S32 bytes_to_copy = 0;
+ const_segment_iterator_t it;
+ const_segment_iterator_t end = mSegments.end();
+ if(start)
+ {
+ it = getSegment(start);
+ if(it == end)
+ {
+ return rv;
+ }
+ if((++start < ((*it).data() + (*it).size()))
+ && (*it).isOnChannel(channel))
+ {
+ // copy the data out of this segment
+ S32 bytes_in_segment = (*it).size() - (start - (*it).data());
+ bytes_to_copy = llmin(bytes_left, bytes_in_segment);
+ memcpy(dest, start, bytes_to_copy); /*Flawfinder: ignore*/
+ len += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ rv = start + bytes_to_copy - 1;
+ ++it;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ else
+ {
+ it = mSegments.begin();
+ }
+ while(bytes_left && (it != end))
+ {
+ if(!((*it).isOnChannel(channel)))
+ {
+ ++it;
+ continue;
+ }
+ bytes_to_copy = llmin(bytes_left, (*it).size());
+ memcpy(dest + len, (*it).data(), bytes_to_copy); /*Flawfinder: ignore*/
+ len += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ rv = (*it).data() + bytes_to_copy - 1;
+ ++it;
+ }
+ return rv;
+}
+
+U8* LLBufferArray::seek(
+ S32 channel,
+ U8* start,
+ S32 delta) const
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ const_segment_iterator_t it;
+ const_segment_iterator_t end = mSegments.end();
+ U8* rv = start;
+ if(0 == delta)
+ {
+ if((U8*)npos == start)
+ {
+ // someone is looking for end of data.
+ segment_list_t::const_reverse_iterator rit = mSegments.rbegin();
+ segment_list_t::const_reverse_iterator rend = mSegments.rend();
+ while(rit != rend)
+ {
+ if(!((*rit).isOnChannel(channel)))
+ {
+ ++rit;
+ continue;
+ }
+ rv = (*rit).data() + (*rit).size();
+ break;
+ }
+ }
+ else if(start)
+ {
+ // This is sort of a weird case - check if zero bytes away
+ // from current position is on channel and return start if
+ // that is true. Otherwise, return NULL.
+ it = getSegment(start);
+ if((it == end) || !(*it).isOnChannel(channel))
+ {
+ rv = NULL;
+ }
+ }
+ else
+ {
+ // Start is NULL, so return the very first byte on the
+ // channel, or NULL.
+ it = mSegments.begin();
+ while((it != end) && !(*it).isOnChannel(channel))
+ {
+ ++it;
+ }
+ if(it != end)
+ {
+ rv = (*it).data();
+ }
+ }
+ return rv;
+ }
+ if(start)
+ {
+ it = getSegment(start);
+ if((it != end) && (*it).isOnChannel(channel))
+ {
+ if(delta > 0)
+ {
+ S32 bytes_in_segment = (*it).size() - (start - (*it).data());
+ S32 local_delta = llmin(delta, bytes_in_segment);
+ rv += local_delta;
+ delta -= local_delta;
+ ++it;
+ }
+ else
+ {
+ S32 bytes_in_segment = start - (*it).data();
+ S32 local_delta = llmin(llabs(delta), bytes_in_segment);
+ rv -= local_delta;
+ delta += local_delta;
+ }
+ }
+ }
+ else if(delta < 0)
+ {
+ // start is NULL, and delta indicates seeking backwards -
+ // return NULL.
+ return NULL;
+ }
+ else
+ {
+ // start is NULL and delta > 0
+ it = mSegments.begin();
+ }
+ if(delta > 0)
+ {
+ // At this point, we have an iterator into the segments, and
+ // are seeking forward until delta is zero or we run out
+ while(delta && (it != end))
+ {
+ if(!((*it).isOnChannel(channel)))
+ {
+ ++it;
+ continue;
+ }
+ if(delta <= (*it).size())
+ {
+ // it's in this segment
+ rv = (*it).data() + delta;
+ }
+ delta -= (*it).size();
+ ++it;
+ }
+ if(delta && (it == end))
+ {
+ // Whoops - sought past end.
+ rv = NULL;
+ }
+ }
+ else //if(delta < 0)
+ {
+ // We are at the beginning of a segment, and need to search
+ // backwards.
+ segment_list_t::const_reverse_iterator rit(it);
+ segment_list_t::const_reverse_iterator rend = mSegments.rend();
+ while(delta && (rit != rend))
+ {
+ if(!((*rit).isOnChannel(channel)))
+ {
+ ++rit;
+ continue;
+ }
+ if(llabs(delta) <= (*rit).size())
+ {
+ // it's in this segment.
+ rv = (*rit).data() + (*rit).size() + delta;
+ delta = 0;
+ }
+ else
+ {
+ delta += (*rit).size();
+ }
+ ++rit;
+ }
+ if(delta && (rit == rend))
+ {
+ // sought past the beginning.
+ rv = NULL;
+ }
+ }
+ return rv;
+}
+
+bool LLBufferArray::takeContents(LLBufferArray& source)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::copy(
+ source.mBuffers.begin(),
+ source.mBuffers.end(),
+ std::back_insert_iterator<buffer_list_t>(mBuffers));
+ source.mBuffers.clear();
+ std::copy(
+ source.mSegments.begin(),
+ source.mSegments.end(),
+ std::back_insert_iterator<segment_list_t>(mSegments));
+ source.mSegments.clear();
+ source.mNextBaseChannel = 0;
+ return true;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::makeSegment(
+ S32 channel,
+ S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ // start at the end of the buffers, because it is the most likely
+ // to have free space.
+ LLSegment segment;
+ buffer_list_t::reverse_iterator it = mBuffers.rbegin();
+ buffer_list_t::reverse_iterator end = mBuffers.rend();
+ bool made_segment = false;
+ for(; it != end; ++it)
+ {
+ if((*it)->createSegment(channel, len, segment))
+ {
+ made_segment = true;
+ break;
+ }
+ }
+ segment_iterator_t send = mSegments.end();
+ if(!made_segment)
+ {
+ LLBuffer* buf = new LLHeapBuffer;
+ mBuffers.push_back(buf);
+ if(!buf->createSegment(channel, len, segment))
+ {
+ // failed. this should never happen.
+ return send;
+ }
+ }
+
+ // store and return the newly made segment
+ mSegments.insert(send, segment);
+ std::list<LLSegment>::reverse_iterator rv = mSegments.rbegin();
+ ++rv;
+ send = rv.base();
+ return send;
+}
+
+bool LLBufferArray::eraseSegment(const segment_iterator_t& iter)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ // *FIX: in theory, we could reclaim the memory. We are leaking a
+ // bit of buffered memory into an unusable but still referenced
+ // location.
+ (void)mSegments.erase(iter);
+ return true;
+}
+
+
+bool LLBufferArray::copyIntoBuffers(
+ S32 channel,
+ const U8* src,
+ S32 len,
+ std::vector<LLSegment>& segments)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if(!src || !len) return false;
+ S32 copied = 0;
+ LLSegment segment;
+ buffer_iterator_t it = mBuffers.begin();
+ buffer_iterator_t end = mBuffers.end();
+ for(; it != end;)
+ {
+ if(!(*it)->createSegment(channel, len, segment))
+ {
+ ++it;
+ continue;
+ }
+ segments.push_back(segment);
+ S32 bytes = llmin(segment.size(), len);
+ memcpy(segment.data(), src + copied, bytes); /* Flawfinder Ignore */
+ copied += bytes;
+ len -= bytes;
+ if(0 == len)
+ {
+ break;
+ }
+ }
+ while(len)
+ {
+ LLBuffer* buf = new LLHeapBuffer;
+ mBuffers.push_back(buf);
+ if(!buf->createSegment(channel, len, segment))
+ {
+ // this totally failed - bail. This is the weird corner
+ // case were we 'leak' memory. No worries about an actual
+ // leak - we will still reclaim the memory later, but this
+ // particular buffer array is hosed for some reason.
+ // This should never happen.
+ return false;
+ }
+ segments.push_back(segment);
+ memcpy(segment.data(), src + copied, segment.size());
+ copied += segment.size();
+ len -= segment.size();
+ }
+ return true;
+}
diff --git a/indra/llmessage/llbuffer.h b/indra/llmessage/llbuffer.h
new file mode 100644
index 0000000000..3d7f209123
--- /dev/null
+++ b/indra/llmessage/llbuffer.h
@@ -0,0 +1,493 @@
+/**
+ * @file llbuffer.h
+ * @author Phoenix
+ * @date 2005-09-20
+ * @brief Declaration of buffer and buffer arrays primarily used in I/O.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBUFFER_H
+#define LL_LLBUFFER_H
+
+/**
+ * Declaration of classes used for minimizing calls to new[],
+ * memcpy(), and delete[]. Typically, you would create an LLHeapArray,
+ * feed it data, modify and add segments as you process it, and feed
+ * it to a sink.
+ */
+
+#include <list>
+
+/**
+ * @class LLChannelDescriptors
+ * @brief A way simple interface to accesss channels inside a buffer
+ */
+class LLChannelDescriptors
+{
+public:
+ // enumeration for segmenting the channel information
+ enum { E_CHANNEL_COUNT = 3 };
+ LLChannelDescriptors() : mBaseChannel(0) {}
+ explicit LLChannelDescriptors(S32 base) : mBaseChannel(base) {}
+ S32 in() const { return mBaseChannel; }
+ S32 out() const { return mBaseChannel + 1; }
+ //S32 err() const { return mBaseChannel + 2; }
+protected:
+ S32 mBaseChannel;
+};
+
+
+/**
+ * @class LLSegment
+ * @brief A segment is a single, contiguous chunk of memory in a buffer
+ *
+ * Each segment represents a contiguous addressable piece of memory
+ * which is located inside a buffer. The segment is not responsible
+ * for allocation or deallcoation of the data. Each segment is a light
+ * weight object, and simple enough to copy around, use, and generate
+ * as necessary.
+ * This is the preferred interface for working with memory blocks,
+ * since it is the only way to safely, inexpensively, and directly
+ * access linear blocks of memory.
+ */
+class LLSegment
+{
+public:
+ LLSegment();
+ LLSegment(S32 channel, U8* data, S32 data_len);
+ ~LLSegment();
+
+ /**
+ * @brief Check if this segment is on the given channel.
+ *
+ */
+ bool isOnChannel(S32 channel) const;
+
+ /**
+ * @brief Get the channel
+ */
+ S32 getChannel() const;
+
+ /**
+ * @brief Set the channel
+ */
+ void setChannel(S32 channel);
+
+ /**
+ * @brief Return a raw pointer to the current data set.
+ *
+ * The pointer returned can be used for reading or even adjustment
+ * if you are a bit crazy up to size() bytes into memory.
+ * @return A potentially NULL pointer to the raw buffer data
+ */
+ U8* data() const;
+
+ /**
+ * @brief Return the size of the segment
+ */
+ S32 size() const;
+
+protected:
+ S32 mChannel;
+ U8* mData;
+ S32 mSize;
+};
+
+/**
+ * @class LLBuffer
+ * @brief Abstract base class for buffers
+ *
+ * This class declares the interface necessary for buffer arrays. A
+ * buffer is not necessarily a single contiguous memory chunk, so
+ * please do not circumvent the segment API.
+ */
+class LLBuffer
+{
+public:
+ /**
+ * @brief The buffer base class should have no responsibilities
+ * other than an interface.
+ */
+ virtual ~LLBuffer() {}
+
+ /**
+ * @brief Generate a segment for this buffer.
+ *
+ * The segment returned is always contiguous memory. This call can
+ * fail if no contiguous memory is available, eg, offset is past
+ * the end. The segment returned may be smaller than the requested
+ * size. The segment will never be larger than the requested size.
+ * @param channel The channel for the segment.
+ * @param offset The offset from zero in the buffer.
+ * @param size The requested size of the segment.
+ * @param segment[out] The out-value from the operation
+ * @return Returns true if a segment was found.
+ */
+ virtual bool createSegment(S32 channel, S32 size, LLSegment& segment) = 0;
+};
+
+/**
+ * @class LLHeapBuffer
+ * @brief A large contiguous buffer allocated on the heap with new[].
+ *
+ * This class is a simple buffer implementation which allocates chunks
+ * off the heap. Once a buffer is constructed, it's buffer has a fixed
+ * length.
+ */
+class LLHeapBuffer : public LLBuffer
+{
+public:
+ /**
+ * @brief Construct a heap buffer with a reasonable default size.
+ */
+ LLHeapBuffer();
+
+ /**
+ * @brief Construct a heap buffer with a specified size.
+ *
+ * @param size The minimum size of the buffer.
+ */
+ explicit LLHeapBuffer(S32 size);
+
+ /**
+ * @brief Construct a heap buffer of minimum size len, and copy from src.
+ *
+ * @param src The source of the data to be copied.
+ * @param len The minimum size of the buffer.
+ */
+ LLHeapBuffer(const U8* src, S32 len);
+
+ /**
+ * @brief Simple destruction.
+ */
+ virtual ~LLHeapBuffer();
+
+ /**
+ * @brief Get the number of bytes left in the buffer.
+ *
+ * @return Returns the number of bytes left.
+ */
+ //virtual S32 bytesLeft() const;
+
+ /**
+ * @brief Generate a segment for this buffer.
+ *
+ * The segment returned is always contiguous memory. This call can
+ * fail if no contiguous memory is available, eg, offset is past
+ * the end. The segment returned may be smaller than the requested
+ * size. It is up to the caller to delete the segment returned.
+ * @param channel The channel for the segment.
+ * @param offset The offset from zero in the buffer
+ * @param size The requested size of the segment
+ * @param segment[out] The out-value from the operation
+ * @return Returns true if a segment was found.
+ */
+ virtual bool createSegment(S32 channel, S32 size, LLSegment& segment);
+
+protected:
+ U8* mBuffer;
+ S32 mSize;
+ U8* mNextFree;
+
+private:
+ /**
+ * @brief Helper method to allocate a buffer and correctly set
+ * intertnal state of this buffer.
+ */
+ void allocate(S32 size);
+};
+
+/**
+ * @class LLBufferArray
+ * @brief Class to represent scattered memory buffers and in-order segments
+ * of that buffered data.
+ *
+ * NOTE: This class needs to have an iovec interface
+ */
+class LLBufferArray
+{
+public:
+ typedef std::vector<LLBuffer*> buffer_list_t;
+ typedef buffer_list_t::iterator buffer_iterator_t;
+ typedef std::list<LLSegment> segment_list_t;
+ typedef segment_list_t::const_iterator const_segment_iterator_t;
+ typedef segment_list_t::iterator segment_iterator_t;
+ enum { npos = 0xffffffff };
+
+ LLBufferArray();
+ ~LLBufferArray();
+
+ /* @name Channel methods
+ */
+ //@{
+ /**
+ * @brief Generate the a channel descriptor which consumes the
+ * output for the channel passed in.
+ */
+ static LLChannelDescriptors makeChannelConsumer(
+ const LLChannelDescriptors& channels);
+
+ /**
+ * @brief Generate the next channel descriptor for this buffer array.
+ *
+ * The channel descriptor interface is how the buffer array
+ * clients can know where to read and write data. Use this
+ * interface to get the 'next' channel set for usage. This is a
+ * bit of a simple hack until it's utility indicates it should be
+ * extended.
+ * @return Returns a valid channel descriptor set for input and output.
+ */
+ LLChannelDescriptors nextChannel();
+ //@}
+
+ /* @name Data methods
+ */
+ //@{
+
+ // These methods will be useful once there is any kind of buffer
+ // besides a heap buffer.
+ //bool append(EBufferChannel channel, LLBuffer* data);
+ //bool prepend(EBufferChannel channel, LLBuffer* data);
+ //bool insertAfter(
+ // segment_iterator_t segment,
+ // EBufferChannel channel,
+ // LLBuffer* data);
+
+ /**
+ * @brief Put data on a channel at the end of this buffer array.
+ *
+ * The data is copied from src into the buffer array. At least one
+ * new segment is created and put on the end of the array. This
+ * object will internally allocate new buffers if necessary.
+ * @param channel The channel for this data
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @return Returns true if the method worked.
+ */
+ bool append(S32 channel, const U8* src, S32 len);
+
+ /**
+ * @brief Put data on a channel at the front of this buffer array.
+ *
+ * The data is copied from src into the buffer array. At least one
+ * new segment is created and put in the front of the array. This
+ * object will internally allocate new buffers if necessary.
+ * @param channel The channel for this data
+
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @return Returns true if the method worked.
+ */
+ bool prepend(S32 channel, const U8* src, S32 len);
+
+ /**
+ * @brief Insert data into a buffer array after a particular segment.
+ *
+ * The data is copied from src into the buffer array. At least one
+ * new segment is created and put in the array. This object will
+ * internally allocate new buffers if necessary.
+ * @param segment The segment in front of the new segments location
+ * @param channel The channel for this data
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @return Returns true if the method worked.
+ */
+ bool insertAfter(
+ segment_iterator_t segment,
+ S32 channel,
+ const U8* src,
+ S32 len);
+
+ /**
+ * @brief Count bytes in the buffer array on the specified channel
+ *
+ * @param channel The channel to count.
+ * @param start The start address in the array for counting. You
+ * can specify NULL to start at the beginning.
+ * @return Returns the number of bytes in the channel after start
+ */
+ S32 countAfter(S32 channel, U8* start) const;
+
+ /**
+ * @brief Read bytes in the buffer array on the specified channel
+ *
+ * You should prefer iterating over segments is possible since
+ * this method requires you to allocate large buffers - precisely
+ * what this class is trying to prevent. This method will skip
+ * any segments which are not on the given channel, so this method
+ * would usually be used to read a channel and copy that to a log
+ * or a socket buffer or something.
+ * @param channel The channel to read.
+ * @param start The start address in the array for reading. You
+ * can specify NULL to start at the beginning.
+ * @param dest The destination of the data read. This must be at
+ * least len bytes long.
+ * @param len[in,out] <b>in</b> How many bytes to read. <b>out</b> How
+ * many bytes were read.
+ * @return Returns the address of the last read byte.
+ */
+ U8* readAfter(S32 channel, U8* start, U8* dest, S32& len) const;
+
+ /**
+ * @brief Find an address in a buffer array
+ *
+ * @param channel The channel to seek in.
+ * @param start The start address in the array for the seek
+ * operation. You can specify NULL to start the seek at the
+ * beginning, or pass in npos to start at the end.
+ * @param delta How many bytes to seek through the array.
+ * @return Returns the address of the last read byte.
+ */
+ U8* seek(S32 channel, U8* start, S32 delta) const;
+ //@}
+
+ /* @name Buffer interaction
+ */
+ //@{
+ /**
+ * @brief Take the contents of another buffer array
+ *
+ * This method simply strips the contents out of the source
+ * buffery array - segments, buffers, etc, and appends them to
+ * this instance. After this operation, the source is empty and
+ * ready for reuse.
+ * @param source The source buffer
+ * @return Returns true if the operation succeeded.
+ */
+ bool takeContents(LLBufferArray& source);
+ //@}
+
+ /* @name Segment methods
+ */
+ //@{
+ /**
+ * @brief Split a segments so that address is the last address of
+ * one segment, and the rest of the original segment becomes
+ * another segment on the same channel.
+ *
+ * After this method call,
+ * <code>getLastSegmentAddress(*getSegment(address)) ==
+ * address</code> should be true. This call will only create a new
+ * segment if the statement above is false before the call. Since
+ * you usually call splitAfter() to change a segment property, use
+ * getSegment() to perform those operations.
+ * @param address The address which will become the last address
+ * of the segment it is in.
+ * @return Returns an iterator to the segment which contains
+ * <code>address</code> which is <code>endSegment()</code> on
+ * failure.
+ */
+ segment_iterator_t splitAfter(U8* address);
+
+ /**
+ * @brief Get the first segment in the buffer array.
+ *
+ * @return Returns the segment if there is one.
+ */
+ segment_iterator_t beginSegment();
+
+ /**
+ * @brief Get the one-past-the-end segment in the buffer array
+ *
+ * @return Returns the iterator for an invalid segment location.
+ */
+ segment_iterator_t endSegment();
+
+ /**
+ * @brief Get the segment which holds the given address.
+ *
+ * As opposed to some methods, passing a NULL will result in
+ * returning the end segment.
+ * @param address An address in the middle of the sought segment.
+ * @return Returns the iterator for the segment or endSegment() on
+ * failure.
+ */
+ const_segment_iterator_t getSegment(U8* address) const;
+
+ /**
+ * @brief Get the segment which holds the given address.
+ *
+ * As opposed to some methods, passing a NULL will result in
+ * returning the end segment.
+ * @param address An address in the middle of the sought segment.
+ * @return Returns the iterator for the segment or endSegment() on
+ * failure.
+ */
+ segment_iterator_t getSegment(U8* address);
+
+ /**
+ * @brief Get a segment iterator after address, and a constructed
+ * segment to represent the next linear block of memory.
+ *
+ * This method is a helper by giving you the largest segment
+ * possible in the out-value param after the address provided. The
+ * iterator will be useful for iteration, while the segment can be
+ * used for direct access to memory after address if the return
+ * values isnot end. Passing in NULL will return beginSegment()
+ * which may be endSegment(). The segment returned will only be
+ * zero length if the return value equals end.
+ * This is really just a helper method, since all the information
+ * returned could be constructed through other methods.
+ * @param address An address in the middle of the sought segment.
+ * @param segment[out] segment to be used for reading or writing
+ * @return Returns an iterator which contains at least segment or
+ * endSegment() on failure.
+ */
+ segment_iterator_t constructSegmentAfter(U8* address, LLSegment& segment);
+
+ /**
+ * @brief Make a new segment at the end of buffer array
+ *
+ * This method will attempt to create a new and empty segment of
+ * the specified length. The segment created may be shorter than
+ * requested.
+ * @param channel[in] The channel for the newly created segment.
+ * @param length[in] The requested length of the segment.
+ * @return Returns an iterator which contains at least segment or
+ * endSegment() on failure.
+ */
+ segment_iterator_t makeSegment(S32 channel, S32 length);
+
+ /**
+ * @brief Erase the segment if it is in the buffer array.
+ *
+ * @param iter An iterator referring to the segment to erase.
+ * @return Returns true on success.
+ */
+ bool eraseSegment(const segment_iterator_t& iter);
+ //@}
+
+protected:
+ /**
+ * @brief Optimally put data in buffers, and reutrn segments.
+ *
+ * This is an internal function used to create buffers as
+ * necessary, and sequence the segments appropriately for the
+ * various ways to copy data from src into this.
+ * If this method fails, it may actually leak some space inside
+ * buffers, but I am not too worried about the slim possibility
+ * that we may have some 'dead' space which will be recovered when
+ * the buffer (which we will not lose) is deleted. Addressing this
+ * weakness will make the buffers almost as complex as a general
+ * memory management system.
+ * @param channel The channel for this data
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @param segments Out-value for the segments created.
+ * @return Returns true if the method worked.
+ */
+ bool copyIntoBuffers(
+ S32 channel,
+ const U8* src,
+ S32 len,
+ std::vector<LLSegment>& segments);
+
+protected:
+ S32 mNextBaseChannel;
+ buffer_list_t mBuffers;
+ segment_list_t mSegments;
+};
+
+#endif // LL_LLBUFFER_H
diff --git a/indra/llmessage/llbufferstream.cpp b/indra/llmessage/llbufferstream.cpp
new file mode 100644
index 0000000000..684548b408
--- /dev/null
+++ b/indra/llmessage/llbufferstream.cpp
@@ -0,0 +1,265 @@
+/**
+ * @file llbufferstream.cpp
+ * @author Phoenix
+ * @date 2005-10-10
+ * @brief Implementation of the buffer iostream classes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llbufferstream.h"
+
+#include "llbuffer.h"
+#include "llmemtype.h"
+
+static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4;
+
+/*
+ * LLBufferStreamBuf
+ */
+LLBufferStreamBuf::LLBufferStreamBuf(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer) :
+ mChannels(channels),
+ mBuffer(buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLBufferStreamBuf::~LLBufferStreamBuf()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ sync();
+}
+
+// virtual
+int LLBufferStreamBuf::underflow()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ //lldebugs << "LLBufferStreamBuf::underflow()" << llendl;
+ if(!mBuffer)
+ {
+ return EOF;
+ }
+ LLSegment segment;
+ LLBufferArray::segment_iterator_t it;
+ U8* last_pos = (U8*)gptr();
+ if(last_pos) --last_pos;
+
+ LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
+
+ // Get iterator to full segment containing last_pos
+ // and construct sub-segment starting at last_pos.
+ // Note: segment may != *it at this point
+ it = mBuffer->constructSegmentAfter(last_pos, segment);
+ if(it == end)
+ {
+ return EOF;
+ }
+
+ // Iterate through segments to find a non-empty segment on input channel.
+ while((!segment.isOnChannel(mChannels.in()) || (segment.size() == 0)))
+ {
+ ++it;
+ if(it == end)
+ {
+ return EOF;
+ }
+
+ segment = *it;
+ }
+
+ char* start = (char*)segment.data();
+ setg(start, start, start + segment.size());
+ return *gptr();
+}
+
+// virtual
+int LLBufferStreamBuf::overflow(int c)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if(!mBuffer)
+ {
+ return EOF;
+ }
+ if(EOF == c)
+ {
+ // if someone puts an EOF, I suppose we should sync and return
+ // success.
+ if(0 == sync())
+ {
+ return 1;
+ }
+ else
+ {
+ return EOF;
+ }
+ }
+
+ // since we got here, we have a buffer, and we have a character to
+ // put on it.
+ LLBufferArray::segment_iterator_t it;
+ it = mBuffer->makeSegment(mChannels.out(), DEFAULT_OUTPUT_SEGMENT_SIZE);
+ if(it != mBuffer->endSegment())
+ {
+ char* start = (char*)(*it).data();
+ (*start) = (char)(c);
+ setp(start + 1, start + (*it).size());
+ return c;
+ }
+ else
+ {
+ return EOF;
+ }
+}
+
+// virtual
+int LLBufferStreamBuf::sync()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ int return_value = -1;
+ if(!mBuffer)
+ {
+ return return_value;
+ }
+
+ // set the put pointer so that we force an overflow on the next
+ // write.
+ U8* address = (U8*)pptr();
+ setp(NULL, NULL);
+
+ // *NOTE: I bet we could just --address. Need to think about that.
+ address = mBuffer->seek(mChannels.out(), address, -1);
+ if(address)
+ {
+ LLBufferArray::segment_iterator_t it;
+ it = mBuffer->splitAfter(address);
+ LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
+ if(it != end)
+ {
+ ++it;
+ if(it != end)
+ {
+ mBuffer->eraseSegment(it);
+ }
+ return_value = 0;
+ }
+ }
+ else
+ {
+ // nothing was put on the buffer, so the sync() is a no-op.
+ return_value = 0;
+ }
+ return return_value;
+}
+
+// virtual
+#if( LL_WINDOWS || __GNUC__ > 2)
+LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff(
+ LLBufferStreamBuf::off_type off,
+ std::ios::seekdir way,
+ std::ios::openmode which)
+#else
+streampos LLBufferStreamBuf::seekoff(
+ streamoff off,
+ std::ios::seekdir way,
+ std::ios::openmode which)
+#endif
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if(!mBuffer
+ || ((way == std::ios::beg) && (off < 0))
+ || ((way == std::ios::end) && (off > 0)))
+ {
+ return -1;
+ }
+ U8* address = NULL;
+ if(which & std::ios::in)
+ {
+ U8* base_addr = NULL;
+ switch(way)
+ {
+ case std::ios::end:
+ base_addr = (U8*)LLBufferArray::npos;
+ break;
+ case std::ios::cur:
+ // get the current get pointer and adjust it for buffer
+ // array semantics.
+ base_addr = (U8*)gptr();
+ break;
+ case std::ios::beg:
+ default:
+ // NULL is fine
+ break;
+ }
+ address = mBuffer->seek(mChannels.in(), base_addr, off);
+ if(address)
+ {
+ LLBufferArray::segment_iterator_t iter;
+ iter = mBuffer->getSegment(address);
+ char* start = (char*)(*iter).data();
+ setg(start, (char*)address, start + (*iter).size());
+ }
+ else
+ {
+ address = (U8*)(-1);
+ }
+ }
+ if(which & std::ios::out)
+ {
+ U8* base_addr = NULL;
+ switch(way)
+ {
+ case std::ios::end:
+ base_addr = (U8*)LLBufferArray::npos;
+ break;
+ case std::ios::cur:
+ // get the current put pointer and adjust it for buffer
+ // array semantics.
+ base_addr = (U8*)pptr();
+ break;
+ case std::ios::beg:
+ default:
+ // NULL is fine
+ break;
+ }
+ address = mBuffer->seek(mChannels.out(), base_addr, off);
+ if(address)
+ {
+ LLBufferArray::segment_iterator_t iter;
+ iter = mBuffer->getSegment(address);
+ setp((char*)address, (char*)(*iter).data() + (*iter).size());
+ }
+ else
+ {
+ address = (U8*)(-1);
+ }
+ }
+
+#if( LL_WINDOWS || __GNUC__ > 2 )
+ S32 rv = (S32)(intptr_t)address;
+ return (pos_type)rv;
+#else
+ return (streampos)address;
+#endif
+}
+
+
+/*
+ * LLBufferStream
+ */
+LLBufferStream::LLBufferStream(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer) :
+ std::iostream(&mStreamBuf),
+ mStreamBuf(channels, buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLBufferStream::~LLBufferStream()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
diff --git a/indra/llmessage/llbufferstream.h b/indra/llmessage/llbufferstream.h
new file mode 100644
index 0000000000..8b972322ce
--- /dev/null
+++ b/indra/llmessage/llbufferstream.h
@@ -0,0 +1,134 @@
+/**
+ * @file llbufferstream.h
+ * @author Phoenix
+ * @date 2005-10-10
+ * @brief Classes to treat an LLBufferArray as a c++ iostream.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBUFFERSTREAM_H
+#define LL_LLBUFFERSTREAM_H
+
+#include <iosfwd>
+#include <iostream>
+#include "llbuffer.h"
+
+/**
+ * @class LLBufferStreamBuf
+ * @brief This implements the buffer wrapper for an istream
+ *
+ * The buffer array passed in is not owned by the stream buf object.
+ */
+class LLBufferStreamBuf : public std::streambuf
+{
+public:
+ LLBufferStreamBuf(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer);
+ virtual ~LLBufferStreamBuf();
+
+protected:
+#if( LL_WINDOWS || __GNUC__ > 2 )
+ typedef std::streambuf::pos_type pos_type;
+ typedef std::streambuf::off_type off_type;
+#endif
+
+ /* @name streambuf vrtual implementations
+ */
+ //@{
+ /*
+ * @brief called when we hit the end of input
+ *
+ * @return Returns the character at the current position or EOF.
+ */
+ virtual int underflow();
+
+ /*
+ * @brief called when we hit the end of output
+ *
+ * @param c The character to store at the current put position
+ * @return Returns EOF if the function failed. Any other value on success.
+ */
+ virtual int overflow(int c);
+
+ /*
+ * @brief synchronize the buffer
+ *
+ * @return Returns 0 on success or -1 on failure.
+ */
+ virtual int sync();
+
+ /*
+ * @brief Seek to an offset position in a stream.
+ *
+ * @param off Offset value relative to way paramter
+ * @param way The seek direction. One of ios::beg, ios::cur, and ios::end.
+ * @param which Which pointer to modify. One of ios::in, ios::out,
+ * or both masked together.
+ * @return Returns the new position or an invalid position on failure.
+ */
+#if( LL_WINDOWS || __GNUC__ > 2)
+ virtual pos_type seekoff(
+ off_type off,
+ std::ios::seekdir way,
+ std::ios::openmode which);
+#else
+ virtual streampos seekoff(
+ streamoff off,
+ std::ios::seekdir way,
+ std::ios::openmode which);
+#endif
+
+ /*
+ * @brief Get s sequence of characters from the input
+ *
+ * @param dst Pointer to a block of memory to accept the characters
+ * @param length Number of characters to be read
+ * @return Returns the number of characters read
+ */
+ //virtual streamsize xsgetn(char* dst, streamsize length);
+
+ /*
+ * @brief Write some characters to output
+ *
+ * @param src Pointer to a sequence of characters to be output
+ * @param length Number of characters to be put
+ * @return Returns the number of characters written
+ */
+ //virtual streamsize xsputn(char* src, streamsize length);
+ //@}
+
+protected:
+ // This channels we are working on.
+ LLChannelDescriptors mChannels;
+
+ // The buffer we work on
+ LLBufferArray* mBuffer;
+};
+
+
+/**
+ * @class LLBufferStream
+ * @brief This implements an istream based wrapper around an LLBufferArray.
+ *
+ * This class does not own the buffer array, and does not hold a
+ * shared pointer to it. Since the class itself is fairly ligthweight,
+ * just make one on the stack when needed and let it fall out of
+ * scope.
+ */
+class LLBufferStream : public std::iostream
+{
+public:
+ LLBufferStream(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer);
+ ~LLBufferStream();
+
+protected:
+ LLBufferStreamBuf mStreamBuf;
+};
+
+
+#endif // LL_LLBUFFERSTREAM_H
diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp
new file mode 100644
index 0000000000..075f4f01cf
--- /dev/null
+++ b/indra/llmessage/llcachename.cpp
@@ -0,0 +1,763 @@
+/**
+ * @file llcachename.cpp
+ * @brief A hierarchical cache of first and last names queried based on UUID.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcachename.h"
+
+// system includes
+#include <string.h> // strcpy
+#include <time.h>
+#include <algorithm>
+#include <functional>
+#include <map>
+
+// linden library includes
+#include "message.h"
+#include "llrand.h"
+#include "lldbstrings.h"
+#include "llframetimer.h"
+#include "llhost.h"
+#include "lluuid.h"
+
+// Constants
+const char* CN_WAITING = "(waiting)";
+const char* CN_NOBODY = "(nobody)";
+const char* CN_NONE = "(none)";
+const char* CN_HIPPOS = "(hippos)";
+const F32 HIPPO_PROBABILITY = 0.01f;
+
+// File version number
+const S32 CN_FILE_VERSION = 2;
+
+// Globals
+LLCacheName* gCacheName = NULL;
+
+/// ---------------------------------------------------------------------------
+/// class LLCacheNameEntry
+/// ---------------------------------------------------------------------------
+
+namespace {
+ class LLCacheNameEntry
+ {
+ public:
+ LLCacheNameEntry();
+
+ public:
+ bool mIsGroup;
+ U32 mCreateTime; // unix time_t
+ char mFirstName[DB_FIRST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/
+ char mLastName[DB_LAST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/
+ char mGroupName[DB_GROUP_NAME_BUF_SIZE]; /*Flawfinder: ignore*/
+ };
+
+ LLCacheNameEntry::LLCacheNameEntry()
+ {
+ mFirstName[0] = '\0';
+ mLastName[0] = '\0';
+ mGroupName[0] = '\0';
+ }
+
+
+ class PendingReply
+ {
+ public:
+ LLUUID mID;
+ LLCacheNameCallback mCallback;
+ LLHost mHost;
+ void* mData;
+ PendingReply(const LLUUID& id, LLCacheNameCallback callback, void* data = NULL)
+ : mID(id), mCallback(callback), mData(data)
+ { }
+
+ PendingReply(const LLUUID& id, const LLHost& host)
+ : mID(id), mCallback(0), mHost(host)
+ { }
+
+ void done() { mID.setNull(); }
+ bool isDone() const { return mID.isNull() != FALSE; }
+ };
+
+ class ReplySender
+ {
+ public:
+ ReplySender(LLMessageSystem* msg);
+ ~ReplySender();
+
+ void send(const LLUUID& id,
+ const LLCacheNameEntry& entry, const LLHost& host);
+
+ private:
+ void flush();
+
+ LLMessageSystem* mMsg;
+ bool mPending;
+ bool mCurrIsGroup;
+ LLHost mCurrHost;
+ };
+
+ ReplySender::ReplySender(LLMessageSystem* msg)
+ : mMsg(msg), mPending(false)
+ { }
+
+ ReplySender::~ReplySender()
+ {
+ flush();
+ }
+
+ void ReplySender::send(const LLUUID& id,
+ const LLCacheNameEntry& entry, const LLHost& host)
+ {
+ if (mPending)
+ {
+ if (mCurrIsGroup != entry.mIsGroup
+ || mCurrHost != host)
+ {
+ flush();
+ }
+ }
+
+ if (!mPending)
+ {
+ mPending = true;
+ mCurrIsGroup = entry.mIsGroup;
+ mCurrHost = host;
+
+ if(mCurrIsGroup)
+ mMsg->newMessageFast(_PREHASH_UUIDGroupNameReply);
+ else
+ mMsg->newMessageFast(_PREHASH_UUIDNameReply);
+ }
+
+ mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
+ mMsg->addUUIDFast(_PREHASH_ID, id);
+ if(mCurrIsGroup)
+ {
+ mMsg->addStringFast(_PREHASH_GroupName, entry.mGroupName);
+ }
+ else
+ {
+ mMsg->addStringFast(_PREHASH_FirstName, entry.mFirstName);
+ mMsg->addStringFast(_PREHASH_LastName, entry.mLastName);
+ }
+
+ if(mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
+ {
+ flush();
+ }
+ }
+
+ void ReplySender::flush()
+ {
+ if (mPending)
+ {
+ mMsg->sendReliable(mCurrHost);
+ mPending = false;
+ }
+ }
+
+
+ typedef std::vector<LLUUID> AskQueue;
+ typedef std::vector<PendingReply> ReplyQueue;
+ typedef std::map<LLUUID, LLCacheNameEntry*> Cache;
+ typedef std::vector<LLCacheNameCallback> Observers;
+};
+
+class LLCacheName::Impl
+{
+public:
+ LLMessageSystem* mMsg;
+ LLHost mUpstreamHost;
+
+ Cache mCache;
+ // the map of UUIDs to names
+
+ AskQueue mAskNameQueue;
+ AskQueue mAskGroupQueue;
+ // UUIDs to ask our upstream host about
+
+ ReplyQueue mReplyQueue;
+ // requests awaiting replies from us
+
+ Observers mObservers;
+
+ LLFrameTimer mProcessTimer;
+
+ Impl(LLMessageSystem* msg);
+ ~Impl();
+
+ void processPendingAsks();
+ void processPendingReplies();
+ void sendRequest(const char* msg_name, const AskQueue& queue);
+
+ // Message system callbacks.
+ void processUUIDRequest(LLMessageSystem* msg, bool isGroup);
+ void processUUIDReply(LLMessageSystem* msg, bool isGroup);
+
+ static void handleUUIDNameRequest(LLMessageSystem* msg, void** userdata);
+ static void handleUUIDNameReply(LLMessageSystem* msg, void** userdata);
+ static void handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userdata);
+ static void handleUUIDGroupNameReply(LLMessageSystem* msg, void** userdata);
+
+ void notifyObservers(const LLUUID& id, const char* first, const char* last, BOOL group);
+};
+
+
+/// --------------------------------------------------------------------------
+/// class LLCacheName
+/// ---------------------------------------------------------------------------
+
+LLCacheName::LLCacheName(LLMessageSystem* msg)
+ : impl(* new Impl(msg))
+ { }
+
+LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host)
+ : impl(* new Impl(msg))
+{
+ setUpstream(upstream_host);
+}
+
+LLCacheName::~LLCacheName()
+{
+ delete &impl;
+}
+
+LLCacheName::Impl::Impl(LLMessageSystem* msg)
+ : mMsg(msg), mUpstreamHost(LLHost::invalid)
+{
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this);
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDNameReply, handleUUIDNameReply, (void**)this);
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDGroupNameRequest, handleUUIDGroupNameRequest, (void**)this);
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDGroupNameReply, handleUUIDGroupNameReply, (void**)this);
+}
+
+
+LLCacheName::Impl::~Impl()
+{
+ for_each(mCache.begin(), mCache.end(), DeletePairedPointer());
+}
+
+
+void LLCacheName::setUpstream(const LLHost& upstream_host)
+{
+ impl.mUpstreamHost = upstream_host;
+}
+
+void LLCacheName::addObserver(LLCacheNameCallback callback)
+{
+ impl.mObservers.push_back(callback);
+}
+
+
+void LLCacheName::importFile(FILE* fp)
+{
+ S32 count = 0;
+
+ const S32 BUFFER_SIZE = 1024;
+ char buffer[BUFFER_SIZE]; /*Flawfinder: ignore*/
+
+ char id_string[MAX_STRING]; /*Flawfinder: ignore*/
+ char firstname[MAX_STRING]; /*Flawfinder: ignore*/
+ char lastname[MAX_STRING]; /*Flawfinder: ignore*/
+ U32 create_time;
+
+ // This is OK if the first line is actually a name. We just don't load it.
+ char* valid = fgets(buffer, BUFFER_SIZE, fp);
+ if (!valid) return;
+
+ char version_string[BUFFER_SIZE]; /*Flawfinder: ignore*/
+ S32 version = 0;
+ S32 match = sscanf(buffer, "%s %d", version_string, &version); // XXXTBD
+ if ( match != 2
+ || strcmp(version_string, "version")
+ || version != CN_FILE_VERSION)
+ {
+ llwarns << "Ignoring old cache name file format" << llendl;
+ return;
+ }
+
+ // We'll expire entries more than a week old
+ U32 now = (U32)time(NULL);
+ const U32 SECS_PER_DAY = 60 * 60 * 24;
+ U32 delete_before_time = now - (7 * SECS_PER_DAY);
+
+ while(!feof(fp))
+ {
+ valid = fgets(buffer, BUFFER_SIZE, fp);
+ if (!valid) break;
+
+ match = sscanf(buffer, "%s %u %s %s", // XXXTBD
+ id_string,
+ &create_time,
+ firstname,
+ lastname);
+ if (4 != match) continue;
+
+ LLUUID id(id_string);
+ if (id.isNull()) continue;
+
+ // undo trivial XOR
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ id.mData[i] ^= 0x33;
+ }
+
+ // Don't load entries that are more than a week old
+ if (create_time < delete_before_time) continue;
+
+ LLCacheNameEntry* entry = new LLCacheNameEntry();
+ entry->mIsGroup = false;
+ entry->mCreateTime = create_time;
+ LLString::copy(entry->mFirstName, firstname, DB_FIRST_NAME_BUF_SIZE);
+ LLString::copy(entry->mLastName, lastname, DB_LAST_NAME_BUF_SIZE);
+ impl.mCache[id] = entry;
+
+ count++;
+ }
+
+ llinfos << "LLCacheName loaded " << count << " names" << llendl;
+}
+
+
+void LLCacheName::exportFile(FILE* fp)
+{
+ fprintf(fp, "version\t%d\n", CN_FILE_VERSION);
+
+ for (Cache::iterator iter = impl.mCache.begin(),
+ end = impl.mCache.end();
+ iter != end; iter++)
+ {
+ LLCacheNameEntry* entry = iter->second;
+ // Only write entries for which we have valid data.
+ // HACK: Only write agent names. This makes the reader easier.
+ if ( entry->mFirstName[0]
+ && entry->mLastName[0])
+ {
+ LLUUID id = iter->first;
+
+ // Trivial XOR encoding
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ id.mData[i] ^= 0x33;
+ }
+
+ char id_string[UUID_STR_SIZE]; /*Flawfinder:ignore*/
+ id.toString(id_string);
+
+ // ...not a group name
+ fprintf(fp, "%s\t%u\t%s\t%s\n",
+ id_string,
+ entry->mCreateTime,
+ entry->mFirstName,
+ entry->mLastName);
+ }
+ }
+}
+
+
+BOOL LLCacheName::getName(const LLUUID& id, char* first, char* last)
+{
+ if(id.isNull())
+ {
+ // The function signature needs to change to pass in the
+ // length of first and last.
+ strcpy(first, CN_NOBODY);
+ last[0] = '\0';
+ return FALSE;
+ }
+
+ LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
+ if (entry)
+ {
+ // The function signature needs to change to pass in the
+ // length of first and last.
+ strcpy(first, entry->mFirstName);
+ strcpy(last, entry->mLastName);
+ return TRUE;
+ }
+ else
+ {
+ //The function signature needs to change to pass in the
+ //length of first and last.
+ strcpy(first,(frand(1.0f) < HIPPO_PROBABILITY)
+ ? CN_HIPPOS
+ : CN_WAITING);
+ strcpy(last, "");
+
+ impl.mAskNameQueue.push_back(id);
+ return FALSE;
+ }
+
+}
+
+
+
+BOOL LLCacheName::getGroupName(const LLUUID& id, char* group)
+{
+ if(id.isNull())
+ {
+ // The function signature needs to change to pass in the
+ // length of first and last.
+ strcpy(group, CN_NONE);
+ return FALSE;
+ }
+
+ LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id);
+ if (entry && !entry->mGroupName[0])
+ {
+ // COUNTER-HACK to combat James' HACK in exportFile()...
+ // this group name was loaded from a name cache that did not
+ // bother to save the group name ==> we must ask for it
+ lldebugs << "LLCacheName queuing HACK group request: " << id << llendl;
+ entry = NULL;
+ }
+
+ if (entry)
+ {
+ // The function signature needs to change to pass in the length
+ // of group.
+ strcpy(group, entry->mGroupName);
+ return TRUE;
+ }
+ else
+ {
+ // The function signature needs to change to pass in the length
+ // of first and last.
+ strcpy(group, CN_WAITING);
+
+ impl.mAskGroupQueue.push_back(id);
+ return FALSE;
+ }
+}
+
+// TODO: Make the cache name callback take a SINGLE std::string,
+// not a separate first and last name.
+void LLCacheName::get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data)
+{
+ if(id.isNull())
+ {
+ callback(id, CN_NOBODY, "", is_group, user_data);
+ }
+
+ LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
+ if (entry)
+ {
+ if (!entry->mIsGroup)
+ {
+ callback(id, entry->mFirstName, entry->mLastName, entry->mIsGroup, user_data);
+ }
+ else
+ {
+ callback(id, entry->mGroupName, "", entry->mIsGroup, user_data);
+ }
+ }
+ else
+ {
+ if (!is_group)
+ {
+ impl.mAskNameQueue.push_back(id);
+ }
+ else
+ {
+ impl.mAskGroupQueue.push_back(id);
+ }
+ impl.mReplyQueue.push_back(PendingReply(id, callback, user_data));
+ }
+}
+
+void LLCacheName::processPending()
+{
+ const F32 SECS_BETWEEN_PROCESS = 0.1f;
+ if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
+ {
+ return;
+ }
+
+ if(!impl.mUpstreamHost.isOk())
+ {
+ lldebugs << "LLCacheName::processPending() - bad upstream host."
+ << llendl;
+ return;
+ }
+
+ impl.processPendingAsks();
+ impl.processPendingReplies();
+}
+
+void LLCacheName::deleteEntriesOlderThan(S32 secs)
+{
+ U32 now = (U32)time(NULL);
+ U32 expire_time = now - secs;
+ for(Cache::iterator iter = impl.mCache.begin(); iter != impl.mCache.end(); )
+ {
+ Cache::iterator curiter = iter++;
+ LLCacheNameEntry* entry = curiter->second;
+ if (entry->mCreateTime < expire_time)
+ {
+ delete entry;
+ impl.mCache.erase(curiter);
+ }
+ }
+}
+
+
+void LLCacheName::dump()
+{
+ for (Cache::iterator iter = impl.mCache.begin(),
+ end = impl.mCache.end();
+ iter != end; iter++)
+ {
+ LLCacheNameEntry* entry = iter->second;
+ if (entry->mIsGroup)
+ {
+ llinfos
+ << iter->first << " = (group) "
+ << entry->mGroupName
+ << " @ " << entry->mCreateTime
+ << llendl;
+ }
+ else
+ {
+ llinfos
+ << iter->first << " = "
+ << entry->mFirstName << " " << entry->mLastName
+ << " @ " << entry->mCreateTime
+ << llendl;
+ }
+ }
+}
+
+void LLCacheName::Impl::processPendingAsks()
+{
+ sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
+ sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
+ mAskNameQueue.clear();
+ mAskGroupQueue.clear();
+}
+
+void LLCacheName::Impl::processPendingReplies()
+{
+ ReplyQueue::iterator it = mReplyQueue.begin();
+ ReplyQueue::iterator end = mReplyQueue.end();
+
+ // First call all the callbacks, because they might send messages.
+ for(; it != end; ++it)
+ {
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, it->mID);
+ if(!entry) continue;
+
+ if (it->mCallback)
+ {
+ if (!entry->mIsGroup)
+ {
+ (it->mCallback)(it->mID,
+ entry->mFirstName, entry->mLastName,
+ FALSE, it->mData);
+ }
+ else {
+ (it->mCallback)(it->mID,
+ entry->mGroupName, "",
+ TRUE, it->mData);
+ }
+ }
+ }
+
+ // Forward on all replies, if needed.
+ ReplySender sender(mMsg);
+ for (it = mReplyQueue.begin(); it != end; ++it)
+ {
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, it->mID);
+ if(!entry) continue;
+
+ if (it->mHost.isOk())
+ {
+ sender.send(it->mID, *entry, it->mHost);
+ }
+
+ it->done();
+ }
+
+ mReplyQueue.erase(
+ remove_if(mReplyQueue.begin(), mReplyQueue.end(),
+ std::mem_fun_ref(&PendingReply::isDone)),
+ mReplyQueue.end());
+}
+
+
+void LLCacheName::Impl::sendRequest(
+ const char* msg_name,
+ const AskQueue& queue)
+{
+ if(queue.empty())
+ {
+ return;
+ }
+
+ bool start_new_message = true;
+ AskQueue::const_iterator it = queue.begin();
+ AskQueue::const_iterator end = queue.end();
+ for(; it != end; ++it)
+ {
+ if(start_new_message)
+ {
+ start_new_message = false;
+ mMsg->newMessageFast(msg_name);
+ }
+ mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
+ mMsg->addUUIDFast(_PREHASH_ID, (*it));
+
+ if(mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
+ {
+ start_new_message = true;
+ mMsg->sendReliable(mUpstreamHost);
+ }
+ }
+ if(!start_new_message)
+ {
+ mMsg->sendReliable(mUpstreamHost);
+ }
+}
+
+void LLCacheName::Impl::notifyObservers(const LLUUID& id,
+ const char* first, const char* last, BOOL is_group)
+{
+ for (Observers::const_iterator i = mObservers.begin(),
+ end = mObservers.end();
+ i != end;
+ ++i)
+ {
+ (**i)(id, first, last, is_group, NULL);
+ }
+}
+
+
+void LLCacheName::Impl::processUUIDRequest(LLMessageSystem* msg, bool isGroup)
+{
+ // You should only get this message if the cache is at the simulator
+ // level, hence having an upstream provider.
+ if (!mUpstreamHost.isOk())
+ {
+ llwarns << "LLCacheName - got UUID name/group request, but no upstream provider!" << llendl;
+ return;
+ }
+
+ LLHost fromHost = msg->getSender();
+ ReplySender sender(msg);
+
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
+ if(entry)
+ {
+ if (isGroup != entry->mIsGroup)
+ {
+ llwarns << "LLCacheName - Asked for "
+ << (isGroup ? "group" : "user") << " name, "
+ << "but found "
+ << (entry->mIsGroup ? "group" : "user")
+ << ": " << id << llendl;
+ }
+ else
+ {
+ // ...it's in the cache, so send it as the reply
+ sender.send(id, *entry, fromHost);
+ }
+ }
+ else
+ {
+ if (isGroup)
+ {
+ mAskGroupQueue.push_back(id);
+ }
+ else
+ {
+ mAskNameQueue.push_back(id);
+ }
+
+ mReplyQueue.push_back(PendingReply(id, fromHost));
+ }
+ }
+}
+
+
+
+void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup)
+{
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
+ if (!entry)
+ {
+ entry = new LLCacheNameEntry;
+ mCache[id] = entry;
+ }
+
+ entry->mIsGroup = isGroup;
+ entry->mCreateTime = (U32)time(NULL);
+ if (!isGroup)
+ {
+ msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, DB_FIRST_NAME_BUF_SIZE, entry->mFirstName, i);
+ msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName, DB_LAST_NAME_BUF_SIZE, entry->mLastName, i);
+ }
+ else
+ {
+ msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, entry->mGroupName, i);
+ }
+
+ if (!isGroup)
+ {
+ notifyObservers(id,
+ entry->mFirstName, entry->mLastName,
+ FALSE);
+ }
+ else
+ {
+ notifyObservers(id, entry->mGroupName, "", TRUE);
+ }
+ }
+}
+
+
+
+// static call back functions
+
+void LLCacheName::Impl::handleUUIDNameReply(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDReply(msg, false);
+}
+
+void LLCacheName::Impl::handleUUIDNameRequest(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDRequest(msg, false);
+}
+
+void LLCacheName::Impl::handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDRequest(msg, true);
+}
+
+void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDReply(msg, true);
+}
+
+
+
+
diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h
new file mode 100644
index 0000000000..ec9c467d8b
--- /dev/null
+++ b/indra/llmessage/llcachename.h
@@ -0,0 +1,90 @@
+/**
+ * @file llcachename.h
+ * @brief A cache of names from UUIDs.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCACHENAME_H
+#define LL_LLCACHENAME_H
+
+// Forward declarations
+#include <stdio.h>
+
+class LLMessageSystem;
+class LLHost;
+class LLUUID;
+
+// agent_id/group_id, first_name, last_name, is_group, user_data
+typedef void (*LLCacheNameCallback)(const LLUUID&, const char*, const char*, BOOL, void*);
+
+// Here's the theory:
+// If you request a name that isn't in the cache, it returns "waiting"
+// and requests the data. After the data arrives, you get that on
+// subsequent calls.
+// If the data hasn't been updated in an hour, it requests it again,
+// but keeps giving you the old value until new data arrives.
+// If you haven't requested the data in an hour, it releases it.
+class LLCacheName
+{
+public:
+ LLCacheName(LLMessageSystem* msg);
+ LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host);
+ ~LLCacheName();
+
+ // registers the upstream host
+ // for viewers, this is the currently connected simulator
+ // for simulators, this is the data server
+ void setUpstream(const LLHost& upstream_host);
+
+ void addObserver(LLCacheNameCallback callback);
+ void removeObserver(LLCacheNameCallback callback);
+
+ // storing cache on disk; for viewer, in name.cache
+ void importFile(FILE* fp);
+ void exportFile(FILE* fp);
+
+ // If available, copies the first and last name into the strings provided.
+ // first must be at least DB_FIRST_NAME_BUF_SIZE characters.
+ // last must be at least DB_LAST_NAME_BUF_SIZE characters.
+ // If not available, copies the string "waiting".
+ // Returns TRUE iff available.
+ BOOL getName(const LLUUID& id, char* first, char* last);
+
+ // If available, this method copies the group name into the string
+ // provided. The caller must allocate at least
+ // DB_GROUP_NAME_BUF_SIZE characters. If not available, this
+ // method copies the string "waiting". Returns TRUE iff available.
+ BOOL getGroupName(const LLUUID& id, char* group);
+
+ // Call the callback with the group or avatar name.
+ // If the data is currently available, may call the callback immediatly
+ // otherwise, will request the data, and will call the callback when
+ // available. There is no garuntee the callback will ever be called.
+ void get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data = NULL);
+
+ // LEGACY
+ void getName(const LLUUID& id, LLCacheNameCallback callback, void* user_data = NULL)
+ { get(id, FALSE, callback, user_data); }
+
+ // This method needs to be called from time to time to send out
+ // requests.
+ void processPending();
+
+ // Expire entries created more than "secs" seconds ago.
+ void deleteEntriesOlderThan(S32 secs);
+
+ // Debugging
+ void dump();
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+
+extern LLCacheName* gCacheName;
+
+#endif
diff --git a/indra/llmessage/llchainio.cpp b/indra/llmessage/llchainio.cpp
new file mode 100644
index 0000000000..c7795f1792
--- /dev/null
+++ b/indra/llmessage/llchainio.cpp
@@ -0,0 +1,70 @@
+/**
+ * @file llchainio.cpp
+ * @author Phoenix
+ * @date 2005-08-04
+ * @brief Implementaiton of the chain factory.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llchainio.h"
+
+#include "lliopipe.h"
+#include "llioutil.h"
+
+
+/**
+ * LLDeferredChain
+ */
+// static
+bool LLDeferredChain::addToPump(
+ LLPumpIO* pump,
+ F32 in_seconds,
+ const LLPumpIO::chain_t& deferred_chain,
+ F32 chain_timeout)
+{
+ if(!pump) return false;
+ LLPumpIO::chain_t sleep_chain;
+ sleep_chain.push_back(LLIOPipe::ptr_t(new LLIOSleep(in_seconds)));
+ sleep_chain.push_back(
+ LLIOPipe::ptr_t(new LLIOAddChain(deferred_chain, chain_timeout)));
+
+ // give it a litle bit of padding.
+ pump->addChain(sleep_chain, in_seconds + 10.0f);
+ return true;
+}
+
+/**
+ * LLChainIOFactory
+ */
+LLChainIOFactory::LLChainIOFactory()
+{
+}
+
+// virtual
+LLChainIOFactory::~LLChainIOFactory()
+{
+}
+
+#if 0
+bool LLChainIOFactory::build(LLIOPipe* in, LLIOPipe* out) const
+{
+ if(!in || !out)
+ {
+ return false;
+ }
+ LLIOPipe* first = NULL;
+ LLIOPipe* last = NULL;
+ if(build_impl(first, last) && first && last)
+ {
+ in->connect(first);
+ last->connect(out);
+ return true;
+ }
+ LLIOPipe::ptr_t foo(first);
+ LLIOPipe::ptr_t bar(last);
+ return false;
+}
+#endif
diff --git a/indra/llmessage/llchainio.h b/indra/llmessage/llchainio.h
new file mode 100644
index 0000000000..f07432da05
--- /dev/null
+++ b/indra/llmessage/llchainio.h
@@ -0,0 +1,117 @@
+/**
+ * @file llchainio.h
+ * @author Phoenix
+ * @date 2005-08-04
+ * @brief This class declares the interface for constructing io chains.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHAINIO_H
+#define LL_LLCHAINIO_H
+
+#include "llpumpio.h"
+
+/**
+ * @class LLDeferredChain
+ * @brief This class allows easy addition of a chain which will sleep
+ * and then process another chain.
+ */
+class LLDeferredChain
+{
+public:
+ /**
+ * @brief Add a chain to a pump in a finite # of seconds
+ *
+ * @prarm pump The pump to work on.
+ * @prarm in_seconds The number of seconds from now when chain should start.
+ * @prarm chain The chain to add in in_seconds seconds.
+ * @prarm chain_timeout timeout for chain on the pump.
+ * @return Returns true if the operation was queued.
+ */
+ static bool addToPump(
+ LLPumpIO* pump,
+ F32 in_seconds,
+ const LLPumpIO::chain_t& chain,
+ F32 chain_timeout);
+};
+
+/**
+ * @class LLChainIOFactory
+ * @brief This class is an abstract base class for building io chains.
+ *
+ * This declares an abstract base class for a chain factory. The
+ * factory is used to connect an input pipe to the first pipe in the
+ * chain, and an output pipe to the last pipe in the chain. This will
+ * allow easy construction for buffer based io like services to for
+ * API centered IO while abstracting the input and output to simple
+ * data passing.
+ * To use this class, you should derive a class which implements the
+ * <code>build</code> method.
+ */
+class LLChainIOFactory
+{
+public:
+ // Constructor
+ LLChainIOFactory();
+
+ // Destructor
+ virtual ~LLChainIOFactory();
+
+ /**
+ * @brief Build the chian with in as the first and end as the last
+ *
+ * The caller of the LLChainIOFactory is responsible for managing
+ * the memory of the in pipe. All of the chains generated by the
+ * factory will be ref counted as usual, so the caller will also
+ * need to break the links in the chain.
+ * @param chain The chain which will have new pipes appended
+ * @param context A context for use by this factory if you choose
+ * @retrun Returns true if the call was successful.
+ */
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const = 0;
+
+protected:
+};
+
+/**
+ * @class LLSimpleIOFactory
+ * @brief Basic implementation for making a factory that returns a
+ * 'chain' of one object
+ */
+template<class Pipe>
+class LLSimpleIOFactory : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ chain.push_back(LLIOPipe::ptr_t(new Pipe));
+ return true;
+ }
+};
+
+/**
+ * @class LLCloneIOFactory
+ * @brief Implementation for a facory which copies a particular pipe.
+ */
+template<class Pipe>
+class LLCloneIOFactory : public LLChainIOFactory
+{
+public:
+ LLCloneIOFactory(Pipe* original) :
+ mHandle(original),
+ mOriginal(original) {}
+
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ chain.push_back(LLIOPipe::ptr_t(new Pipe(*mOriginal)));
+ return true;
+ }
+
+protected:
+ LLIOPipe::ptr_t mHandle;
+ Pipe* mOriginal;
+};
+
+#endif // LL_LLCHAINIO_H
diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp
new file mode 100644
index 0000000000..d3ef92e4a7
--- /dev/null
+++ b/indra/llmessage/llcircuit.cpp
@@ -0,0 +1,1382 @@
+/**
+ * @file llcircuit.cpp
+ * @brief Class to track UDP endpoints for the message system.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if LL_WINDOWS
+
+#include <process.h>
+
+#else
+
+#if LL_LINUX
+#include <dlfcn.h> // RTLD_LAZY
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#endif
+
+
+#if !defined(USE_CIRCUIT_LIST)
+#include <algorithm>
+#endif
+#include <sstream>
+#include <iterator>
+#include <stack>
+
+#include "llcircuit.h"
+
+#include "message.h"
+#include "llrand.h"
+#include "llstl.h"
+#include "lltransfermanager.h"
+
+const F32 PING_INTERVAL = 5.f; // seconds
+const S32 PING_START_BLOCK = 3; // How many pings behind we have to be to consider ourself blocked.
+const S32 PING_RELEASE_BLOCK = 2; // How many pings behind we have to be to consider ourself unblocked.
+
+const F32 TARGET_PERIOD_LENGTH = 5.f; // seconds
+const F32 LL_DUPLICATE_SUPPRESSION_TIMEOUT = 60.f; //seconds - this can be long, as time-based cleanup is
+ // only done when wrapping packetids, now...
+
+LLCircuitData::LLCircuitData(const LLHost &host, TPACKETID in_id)
+: mHost (host),
+ mWrapID(0),
+ mPacketsOutID(0),
+ mPacketsInID(in_id),
+ mHighestPacketID(in_id),
+ mTrusted(FALSE),
+ mbAllowTimeout(TRUE),
+ mbAlive(TRUE),
+ mBlocked(FALSE),
+ mPingTime(0.0),
+ mLastPingSendTime(0.0),
+ mLastPingReceivedTime(0.0),
+ mNextPingSendTime(0.0),
+ mPingsInTransit(0),
+ mLastPingID(0),
+ mPingDelay(INITIAL_PING_VALUE_MSEC),
+ mPingDelayAveraged((F32)INITIAL_PING_VALUE_MSEC),
+ mUnackedPacketCount(0),
+ mUnackedPacketBytes(0),
+ mLocalEndPointID(),
+ mPacketsOut(0),
+ mPacketsIn(0),
+ mPacketsLost(0),
+ mBytesIn(0),
+ mBytesOut(0),
+ mLastPeriodLength(-1.f),
+ mBytesInLastPeriod(0),
+ mBytesOutLastPeriod(0),
+ mBytesInThisPeriod(0),
+ mBytesOutThisPeriod(0),
+ mPeakBPSIn(0),
+ mPeakBPSOut(0),
+ mPeriodTime(0.0),
+ mExistenceTimer(),
+ mCurrentResendCount(0)
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds(TRUE);
+ F32 distribution_offset = frand(1.0f);
+
+ mPingTime = mt_sec;
+ mLastPingSendTime = mt_sec + PING_INTERVAL * distribution_offset;
+ mLastPingReceivedTime = mt_sec;
+ mNextPingSendTime = mLastPingSendTime + 0.95*PING_INTERVAL + frand(0.1f*PING_INTERVAL);
+ mPeriodTime = mt_sec;
+
+ mTimeoutCallback = NULL;
+ mTimeoutUserData = NULL;
+
+ mLocalEndPointID.generate();
+}
+
+
+LLCircuitData::~LLCircuitData()
+{
+ LLReliablePacket *packetp = NULL;
+
+ // Clean up all pending transfers.
+ gTransferManager.cleanupConnection(mHost);
+
+ // remove all pending reliable messages on this circuit
+ std::vector<TPACKETID> doomed;
+ reliable_iter iter;
+ reliable_iter end = mUnackedPackets.end();
+ for(iter = mUnackedPackets.begin(); iter != end; ++iter)
+ {
+ packetp = iter->second;
+ gMessageSystem->mFailedResendPackets++;
+ if(gMessageSystem->mVerboseLog)
+ {
+ doomed.push_back(packetp->mPacketID);
+ }
+ if (packetp->mCallback)
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_CIRCUIT_GONE);
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ delete packetp;
+ }
+
+ // remove all pending final retry reliable messages on this circuit
+ end = mFinalRetryPackets.end();
+ for(iter = mFinalRetryPackets.begin(); iter != end; ++iter)
+ {
+ packetp = iter->second;
+ gMessageSystem->mFailedResendPackets++;
+ if(gMessageSystem->mVerboseLog)
+ {
+ doomed.push_back(packetp->mPacketID);
+ }
+ if (packetp->mCallback)
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_CIRCUIT_GONE);
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ delete packetp;
+ }
+
+ // log aborted reliable packets for this circuit.
+ if(gMessageSystem->mVerboseLog && !doomed.empty())
+ {
+ std::ostringstream str;
+ std::ostream_iterator<TPACKETID> append(str, " ");
+ str << "MSG: -> " << mHost << "\tABORTING RELIABLE:\t";
+ std::copy(doomed.begin(), doomed.end(), append);
+ llinfos << str.str().c_str() << llendl;
+ }
+}
+
+
+void LLCircuitData::ackReliablePacket(TPACKETID packet_num)
+{
+ reliable_iter iter;
+ LLReliablePacket *packetp;
+
+ iter = mUnackedPackets.find(packet_num);
+ if (iter != mUnackedPackets.end())
+ {
+ packetp = iter->second;
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << packetp->mHost << "\tRELIABLE ACKED:\t"
+ << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+ if (packetp->mCallback)
+ {
+ if (packetp->mTimeout < 0.f) // negative timeout will always return timeout even for successful ack, for debugging
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_TCP_TIMEOUT);
+ }
+ else
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_NOERR);
+ }
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ // Cleanup
+ delete packetp;
+ mUnackedPackets.erase(iter);
+ return;
+ }
+
+ iter = mFinalRetryPackets.find(packet_num);
+ if (iter != mFinalRetryPackets.end())
+ {
+ packetp = iter->second;
+ // llinfos << "Packet " << packet_num << " removed from the pending list" << llendl;
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << packetp->mHost << "\tRELIABLE ACKED:\t"
+ << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+ if (packetp->mCallback)
+ {
+ if (packetp->mTimeout < 0.f) // negative timeout will always return timeout even for successful ack, for debugging
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_TCP_TIMEOUT);
+ }
+ else
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_NOERR);
+ }
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ // Cleanup
+ delete packetp;
+ mFinalRetryPackets.erase(iter);
+ }
+ else
+ {
+ // Couldn't find this packet on either of the unacked lists.
+ // maybe it's a duplicate ack?
+ }
+}
+
+
+
+S32 LLCircuitData::resendUnackedPackets(const F64 now)
+{
+ S32 resent_packets = 0;
+ LLReliablePacket *packetp;
+
+
+ //
+ // Theoretically we should search through the list for the packet with the oldest
+ // packet ID, as otherwise when we WRAP we will resend reliable packets out of order.
+ // Since resends are ALREADY out of order, and wrapping is highly rare (16+million packets),
+ // I'm not going to worry about this for now - djs
+ //
+
+ reliable_iter iter;
+ BOOL have_resend_overflow = FALSE;
+ for (iter = mUnackedPackets.begin(); iter != mUnackedPackets.end();)
+ {
+ packetp = iter->second;
+
+ // Only check overflow if we haven't had one yet.
+ if (!have_resend_overflow)
+ {
+ have_resend_overflow = mThrottles.checkOverflow(TC_RESEND, 0);
+ }
+
+ if (have_resend_overflow)
+ {
+ // We've exceeded our bandwidth for resends.
+ // Time to stop trying to send them.
+
+ // If we have too many unacked packets, we need to start dropping expired ones.
+ if (mUnackedPacketBytes > 512000)
+ {
+ if (now > packetp->mExpirationTime)
+ {
+ // This circuit has overflowed. Do not retry. Do not pass go.
+ packetp->mRetries = 0;
+ // Remove it from this list and add it to the final list.
+ mUnackedPackets.erase(iter++);
+ mFinalRetryPackets[packetp->mPacketID] = packetp;
+ }
+ else
+ {
+ ++iter;
+ }
+ // Move on to the next unacked packet.
+ continue;
+ }
+
+ if (mUnackedPacketBytes > 256000 && !(getPacketsOut() % 1024))
+ {
+ // Warn if we've got a lot of resends waiting.
+ llwarns << mHost << " has " << mUnackedPacketBytes
+ << " bytes of reliable messages waiting" << llendl;
+ }
+ // Stop resending. There are less than 512000 unacked packets.
+ break;
+ }
+
+ if (now > packetp->mExpirationTime)
+ {
+ packetp->mRetries--;
+
+ // retry
+ mCurrentResendCount++;
+
+ gMessageSystem->mResentPackets++;
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << packetp->mHost
+ << "\tRESENDING RELIABLE:\t" << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+
+ packetp->mBuffer[0] |= LL_RESENT_FLAG; // tag packet id as being a resend
+
+ gMessageSystem->mPacketRing.sendPacket(packetp->mSocket,
+ (char *)packetp->mBuffer, packetp->mBufferLength,
+ packetp->mHost);
+
+ mThrottles.throttleOverflow(TC_RESEND, packetp->mBufferLength * 8.f);
+
+ // The new method, retry time based on ping
+ if (packetp->mPingBasedRetry)
+ {
+ packetp->mExpirationTime = now + llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, (LL_RELIABLE_TIMEOUT_FACTOR * getPingDelayAveraged()));
+ }
+ else
+ {
+ // custom, constant retry time
+ packetp->mExpirationTime = now + packetp->mTimeout;
+ }
+
+ if (!packetp->mRetries)
+ {
+ // Last resend, remove it from this list and add it to the final list.
+ mUnackedPackets.erase(iter++);
+ mFinalRetryPackets[packetp->mPacketID] = packetp;
+ }
+ else
+ {
+ // Don't remove it yet, it still gets to try to resend at least once.
+ ++iter;
+ }
+ resent_packets++;
+ }
+ else
+ {
+ // Don't need to do anything with this packet, keep iterating.
+ ++iter;
+ }
+ }
+
+
+ for (iter = mFinalRetryPackets.begin(); iter != mFinalRetryPackets.end();)
+ {
+ packetp = iter->second;
+ if (now > packetp->mExpirationTime)
+ {
+ // fail (too many retries)
+ //llinfos << "Packet " << packetp->mPacketID << " removed from the pending list: exceeded retry limit" << llendl;
+ //if (packetp->mMessageName)
+ //{
+ // llinfos << "Packet name " << packetp->mMessageName << llendl;
+ //}
+ gMessageSystem->mFailedResendPackets++;
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << packetp->mHost << "\tABORTING RELIABLE:\t"
+ << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+
+ if (packetp->mCallback)
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_TCP_TIMEOUT);
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ mFinalRetryPackets.erase(iter++);
+ delete packetp;
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ return mUnackedPacketCount;
+}
+
+
+LLCircuit::LLCircuit() : mLastCircuit(NULL)
+{
+}
+
+LLCircuit::~LLCircuit()
+{
+ // delete pointers in the map.
+ std::for_each(mCircuitData.begin(),
+ mCircuitData.end(),
+ llcompose1(
+ DeletePointerFunctor<LLCircuitData>(),
+ llselect2nd<circuit_data_map::value_type>()));
+}
+
+LLCircuitData *LLCircuit::addCircuitData(const LLHost &host, TPACKETID in_id)
+{
+ // This should really validate if one already exists
+ llinfos << "LLCircuit::addCircuitData for " << host << llendl;
+ LLCircuitData *tempp = new LLCircuitData(host, in_id);
+ mCircuitData.insert(circuit_data_map::value_type(host, tempp));
+ mPingSet.insert(tempp);
+
+ mLastCircuit = tempp;
+ return tempp;
+}
+
+void LLCircuit::removeCircuitData(const LLHost &host)
+{
+ llinfos << "LLCircuit::removeCircuitData for " << host << llendl;
+ mLastCircuit = NULL;
+ circuit_data_map::iterator it = mCircuitData.find(host);
+ if(it != mCircuitData.end())
+ {
+ LLCircuitData *cdp = it->second;
+ mCircuitData.erase(it);
+
+ LLCircuit::ping_set_t::iterator psit = mPingSet.find(cdp);
+ if (psit != mPingSet.end())
+ {
+ mPingSet.erase(psit);
+ }
+ else
+ {
+ llwarns << "Couldn't find entry for next ping in ping set!" << llendl;
+ }
+
+ // Clean up from optimization maps
+ mUnackedCircuitMap.erase(host);
+ mSendAckMap.erase(host);
+ delete cdp;
+ }
+
+ // This also has to happen AFTER we nuke the circuit, because various
+ // callbacks for the circuit may result in messages being sent to
+ // this circuit, and the setting of mLastCircuit. We don't check
+ // if the host matches, but we don't really care because mLastCircuit
+ // is an optimization, and this happens VERY rarely.
+ mLastCircuit = NULL;
+}
+
+void LLCircuitData::setAlive(BOOL b_alive)
+{
+ if (mbAlive != b_alive)
+ {
+ mPacketsOutID = 0;
+ mPacketsInID = 0;
+ mbAlive = b_alive;
+ }
+ if (b_alive)
+ {
+ mLastPingReceivedTime = LLMessageSystem::getMessageTimeSeconds();
+ mPingsInTransit = 0;
+ mBlocked = FALSE;
+ }
+}
+
+
+void LLCircuitData::setAllowTimeout(BOOL allow)
+{
+ mbAllowTimeout = allow;
+
+ if (allow)
+ {
+ // resuming circuit
+ // make sure it's alive
+ setAlive(TRUE);
+ }
+}
+
+
+// Reset per-period counters if necessary.
+void LLCircuitData::checkPeriodTime()
+{
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ F64 period_length = mt_sec - mPeriodTime;
+ if ( period_length > TARGET_PERIOD_LENGTH)
+ {
+ F32 bps_in = (F32)(mBytesInThisPeriod * 8.f / period_length);
+ if (bps_in > mPeakBPSIn)
+ {
+ mPeakBPSIn = bps_in;
+ }
+
+ F32 bps_out = (F32)(mBytesOutThisPeriod * 8.f / period_length);
+ if (bps_out > mPeakBPSOut)
+ {
+ mPeakBPSOut = bps_out;
+ }
+
+ mBytesInLastPeriod = mBytesInThisPeriod;
+ mBytesOutLastPeriod = mBytesOutThisPeriod;
+ mBytesInThisPeriod = 0;
+ mBytesOutThisPeriod = 0;
+ mLastPeriodLength = (F32)period_length;
+
+ mPeriodTime = mt_sec;
+ }
+}
+
+
+void LLCircuitData::addBytesIn(S32 bytes)
+{
+ mBytesIn += bytes;
+ mBytesInThisPeriod += bytes;
+}
+
+
+void LLCircuitData::addBytesOut(S32 bytes)
+{
+ mBytesOut += bytes;
+ mBytesOutThisPeriod += bytes;
+}
+
+
+void LLCircuitData::addReliablePacket(S32 mSocket, U8 *buf_ptr, S32 buf_len, LLReliablePacketParams *params)
+{
+ LLReliablePacket *packet_info;
+
+ packet_info = new LLReliablePacket(mSocket, buf_ptr, buf_len, params);
+
+ mUnackedPacketCount++;
+ mUnackedPacketBytes += packet_info->mBufferLength;
+
+ if (params && params->mRetries)
+ {
+ mUnackedPackets[packet_info->mPacketID] = packet_info;
+ }
+ else
+ {
+ mFinalRetryPackets[packet_info->mPacketID] = packet_info;
+ }
+}
+
+
+void LLCircuit::resendUnackedPackets(S32& unacked_list_length, S32& unacked_list_size)
+{
+ F64 now = LLMessageSystem::getMessageTimeSeconds();
+ unacked_list_length = 0;
+ unacked_list_size = 0;
+
+ LLCircuitData* circ;
+ circuit_data_map::iterator end = mUnackedCircuitMap.end();
+ for(circuit_data_map::iterator it = mUnackedCircuitMap.begin(); it != end; ++it)
+ {
+ circ = (*it).second;
+ unacked_list_length += circ->resendUnackedPackets(now);
+ unacked_list_size += circ->getUnackedPacketBytes();
+ }
+}
+
+
+BOOL LLCircuitData::isDuplicateResend(TPACKETID packetnum)
+{
+ return (mRecentlyReceivedReliablePackets.find(packetnum) != mRecentlyReceivedReliablePackets.end());
+}
+
+
+void LLCircuit::dumpResends()
+{
+ circuit_data_map::iterator end = mCircuitData.end();
+ for(circuit_data_map::iterator it = mCircuitData.begin(); it != end; ++it)
+ {
+ (*it).second->dumpResendCountAndReset();
+ }
+}
+
+LLCircuitData* LLCircuit::findCircuit(const LLHost& host) const
+{
+ // An optimization on finding the previously found circuit.
+ if (mLastCircuit && (mLastCircuit->mHost == host))
+ {
+ return mLastCircuit;
+ }
+
+ circuit_data_map::const_iterator it = mCircuitData.find(host);
+ if(it == mCircuitData.end())
+ {
+ return NULL;
+ }
+ mLastCircuit = it->second;
+ return mLastCircuit;
+}
+
+
+BOOL LLCircuit::isCircuitAlive(const LLHost& host) const
+{
+ LLCircuitData *cdp = findCircuit(host);
+ if(cdp)
+ {
+ return cdp->mbAlive;
+ }
+
+ return FALSE;
+}
+
+void LLCircuitData::setTimeoutCallback(void (*callback_func)(const LLHost &host, void *user_data), void *user_data)
+{
+ mTimeoutCallback = callback_func;
+ mTimeoutUserData = user_data;
+}
+
+void LLCircuitData::checkPacketInID(TPACKETID id, BOOL receive_resent)
+{
+ // Done as floats so we don't have to worry about running out of room
+ // with U32 getting poked into an S32.
+ F32 delta = (F32)mHighestPacketID - (F32)id;
+ if (delta > (0.5f*LL_MAX_OUT_PACKET_ID))
+ {
+ // We've almost definitely wrapped, reset the mLastPacketID to be low again.
+ mHighestPacketID = id;
+ }
+ else if (delta < (-0.5f*LL_MAX_OUT_PACKET_ID))
+ {
+ // This is almost definitely an old packet coming in after a wrap, ignore it.
+ }
+ else
+ {
+ mHighestPacketID = llmax(mHighestPacketID, id);
+ }
+
+
+ // Have we received anything on this circuit yet?
+ if (0 == mPacketsIn)
+ {
+ // Must be first packet from unclosed circuit.
+ mPacketsIn++;
+ setPacketInID((id + 1) % LL_MAX_OUT_PACKET_ID);
+
+ return;
+ }
+
+ mPacketsIn++;
+
+
+ // now, check to see if we've got a gap
+ if ((mPacketsInID == id))
+ {
+ // nope! bump and wrap the counter, then return
+ mPacketsInID++;
+ mPacketsInID = (mPacketsInID) % LL_MAX_OUT_PACKET_ID;
+ }
+ else if (id < mWrapID)
+ {
+ // id < mWrapID will happen if the first few packets are out of order. . .
+ // at that point we haven't marked anything "potentially lost" and
+ // the out-of-order packet will cause a full wrap marking all the IDs "potentially lost"
+
+ // do nothing
+ }
+ else
+ {
+ // we have a gap! if that id is in the map, remove it from the map, leave mCurrentCircuit->mPacketsInID
+ // alone
+ // otherwise, walk from mCurrentCircuit->mPacketsInID to id with wrapping, adding the values to the map
+ // and setting mPacketsInID to id + 1 % LL_MAX_OUT_PACKET_ID
+
+ if (mPotentialLostPackets.find(id) != mPotentialLostPackets.end())
+ {
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tRECOVERING LOST:\t" << id;
+ llinfos << str.str().c_str() << llendl;
+ }
+ // llinfos << "removing potential lost: " << id << llendl;
+ mPotentialLostPackets.erase(id);
+ }
+ else if (!receive_resent) // don't freak out over out-of-order reliable resends
+ {
+ U64 time = LLMessageSystem::getMessageTimeUsecs();
+ TPACKETID index = mPacketsInID;
+ S32 gap_count = 0;
+ if ((index < id) && ((id - index) < 16))
+ {
+ while (index != id)
+ {
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tPACKET GAP:\t"
+ << index;
+ llinfos << str.str().c_str() << llendl;
+ }
+
+// llinfos << "adding potential lost: " << index << llendl;
+ mPotentialLostPackets[index] = time;
+ index++;
+ index = index % LL_MAX_OUT_PACKET_ID;
+ gap_count++;
+ }
+ }
+ else
+ {
+ llinfos << "packet_out_of_order - got packet " << id << " expecting " << index << " from " << mHost << llendl;
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tPACKET GAP:\t"
+ << id << " expected " << index;
+ llinfos << str.str().c_str() << llendl;
+ }
+ }
+
+ mPacketsInID = id + 1;
+ mPacketsInID = (mPacketsInID) % LL_MAX_OUT_PACKET_ID;
+
+ if (gap_count > 128)
+ {
+ llwarns << "Packet loss gap filler running amok!" << llendl;
+ }
+ else if (gap_count > 16)
+ {
+ llwarns << "Sustaining large amounts of packet loss!" << llendl;
+ }
+
+ }
+ }
+}
+
+
+void LLCircuit::updateWatchDogTimers(LLMessageSystem *msgsys)
+{
+ F64 cur_time = LLMessageSystem::getMessageTimeSeconds();
+ S32 count = mPingSet.size();
+ S32 cur = 0;
+
+ // Only process each circuit once at most, stop processing if no circuits
+ while((cur < count) && !mPingSet.empty())
+ {
+ cur++;
+
+ LLCircuit::ping_set_t::iterator psit = mPingSet.begin();
+ LLCircuitData *cdp = *psit;
+
+ if (!cdp->mbAlive)
+ {
+ // We suspect that this case should never happen, given how
+ // the alive status is set.
+ // Skip over dead circuits, just add the ping interval and push it to the back
+ // Always remember to remove it from the set before changing the sorting
+ // key (mNextPingSendTime)
+ mPingSet.erase(psit);
+ cdp->mNextPingSendTime = cur_time + PING_INTERVAL;
+ mPingSet.insert(cdp);
+ continue;
+ }
+ else
+ {
+ // Check to see if this needs a ping
+ if (cur_time < cdp->mNextPingSendTime)
+ {
+ // This circuit doesn't need a ping, break out because
+ // we have a sorted list, thus no more circuits need pings
+ break;
+ }
+
+ // Update watchdog timers
+ if (cdp->updateWatchDogTimers(msgsys))
+ {
+ // Randomize our pings a bit by doing some up to 5% early or late
+ F64 dt = 0.95f*PING_INTERVAL + frand(0.1f*PING_INTERVAL);
+
+ // Remove it, and reinsert it with the new next ping time.
+ // Always remove before changing the sorting key.
+ mPingSet.erase(psit);
+ cdp->mNextPingSendTime = cur_time + dt;
+ mPingSet.insert(cdp);
+
+ // Update our throttles
+ cdp->mThrottles.dynamicAdjust();
+
+ // Update some stats, this is not terribly important
+ cdp->checkPeriodTime();
+ }
+ else
+ {
+ // This mPingSet.erase isn't necessary, because removing the circuit will
+ // remove the ping set.
+ //mPingSet.erase(psit);
+ removeCircuitData(cdp->mHost);
+ }
+ }
+ }
+}
+
+
+BOOL LLCircuitData::updateWatchDogTimers(LLMessageSystem *msgsys)
+{
+ F64 cur_time = LLMessageSystem::getMessageTimeSeconds();
+ mLastPingSendTime = cur_time;
+
+ if (!checkCircuitTimeout())
+ {
+ // Pass this back to the calling LLCircuit, this circuit needs to be cleaned up.
+ return FALSE;
+ }
+
+ // WARNING!
+ // Duplicate suppression can FAIL if packets are delivered out of
+ // order, although it's EXTREMELY unlikely. It would require
+ // that the ping get delivered out of order enough that the ACK
+ // for the packet that it was out of order with was received BEFORE
+ // the ping was sent.
+
+ // Find the current oldest reliable packetID
+ // This is to handle the case if we actually manage to wrap our
+ // packet IDs - the oldest will actually have a higher packet ID
+ // than the current.
+ BOOL wrapped = FALSE;
+ reliable_iter iter;
+ iter = mUnackedPackets.upper_bound(getPacketOutID());
+ if (iter == mUnackedPackets.end())
+ {
+ // Nothing AFTER this one, so we want the lowest packet ID
+ // then.
+ iter = mUnackedPackets.begin();
+ wrapped = TRUE;
+ }
+
+ TPACKETID packet_id = 0;
+
+ // Check against the "final" packets
+ BOOL wrapped_final = FALSE;
+ reliable_iter iter_final;
+ iter_final = mFinalRetryPackets.upper_bound(getPacketOutID());
+ if (iter_final == mFinalRetryPackets.end())
+ {
+ iter_final = mFinalRetryPackets.begin();
+ wrapped_final = TRUE;
+ }
+
+ //llinfos << mHost << " - unacked count " << mUnackedPackets.size() << llendl;
+ //llinfos << mHost << " - final count " << mFinalRetryPackets.size() << llendl;
+ if (wrapped != wrapped_final)
+ {
+ // One of the "unacked" or "final" lists hasn't wrapped. Whichever one
+ // hasn't has the oldest packet.
+ if (!wrapped)
+ {
+ // Hasn't wrapped, so the one on the
+ // unacked packet list is older
+ packet_id = iter->first;
+ //llinfos << mHost << ": nowrapped unacked" << llendl;
+ }
+ else
+ {
+ packet_id = iter_final->first;
+ //llinfos << mHost << ": nowrapped final" << llendl;
+ }
+ }
+ else
+ {
+ // They both wrapped, we can just use the minimum of the two.
+ if ((iter == mUnackedPackets.end()) && (iter_final == mFinalRetryPackets.end()))
+ {
+ // Wow! No unacked packets at all!
+ // Send the ID of the last packet we sent out.
+ // This will flush all of the destination's
+ // unacked packets, theoretically.
+ //llinfos << mHost << ": No unacked!" << llendl;
+ packet_id = getPacketOutID();
+ }
+ else
+ {
+ BOOL had_unacked = FALSE;
+ if (iter != mUnackedPackets.end())
+ {
+ // Unacked list has the lowest so far
+ packet_id = iter->first;
+ had_unacked = TRUE;
+ //llinfos << mHost << ": Unacked" << llendl;
+ }
+
+ if (iter_final != mFinalRetryPackets.end())
+ {
+ // Use the lowest of the unacked list and the final list
+ if (had_unacked)
+ {
+ // Both had a packet, use the lowest.
+ packet_id = llmin(packet_id, iter_final->first);
+ //llinfos << mHost << ": Min of unacked/final" << llendl;
+ }
+ else
+ {
+ // Only the final had a packet, use it.
+ packet_id = iter_final->first;
+ //llinfos << mHost << ": Final!" << llendl;
+ }
+ }
+ }
+ }
+
+ // Send off the another ping.
+ pingTimerStart();
+ msgsys->newMessageFast(_PREHASH_StartPingCheck);
+ msgsys->nextBlock(_PREHASH_PingID);
+ msgsys->addU8Fast(_PREHASH_PingID, nextPingID());
+ msgsys->addU32Fast(_PREHASH_OldestUnacked, packet_id);
+ msgsys->sendMessage(mHost);
+
+ // Also do lost packet accounting.
+ // Check to see if anything on our lost list is old enough to
+ // be considered lost
+
+ LLCircuitData::packet_time_map::iterator it;
+ U64 timeout = (U64)(1000000.0*llmin(LL_MAX_LOST_TIMEOUT, getPingDelayAveraged() * LL_LOST_TIMEOUT_FACTOR));
+
+ U64 mt_usec = LLMessageSystem::getMessageTimeUsecs();
+ for (it = mPotentialLostPackets.begin(); it != mPotentialLostPackets.end(); )
+ {
+ U64 delta_t_usec = mt_usec - (*it).second;
+ if (delta_t_usec > timeout)
+ {
+ // let's call this one a loss!
+ mPacketsLost++;
+ gMessageSystem->mDroppedPackets++;
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tLOST PACKET:\t"
+ << (*it).first;
+ llinfos << str.str().c_str() << llendl;
+ }
+ mPotentialLostPackets.erase((*(it++)).first);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ return TRUE;
+}
+
+
+void LLCircuitData::clearDuplicateList(TPACKETID oldest_id)
+{
+ // purge old data from the duplicate suppression queue
+
+ // we want to KEEP all x where oldest_id <= x <= last incoming packet, and delete everything else.
+
+ //llinfos << mHost << ": clearing before oldest " << oldest_id << llendl;
+ //llinfos << "Recent list before: " << mRecentlyReceivedReliablePackets.size() << llendl;
+ if (oldest_id < mHighestPacketID)
+ {
+ // Clean up everything with a packet ID less than oldest_id.
+ packet_time_map::iterator pit_start;
+ packet_time_map::iterator pit_end;
+ pit_start = mRecentlyReceivedReliablePackets.begin();
+ pit_end = mRecentlyReceivedReliablePackets.lower_bound(oldest_id);
+ mRecentlyReceivedReliablePackets.erase(pit_start, pit_end);
+ }
+
+ // Do timeout checks on everything with an ID > mHighestPacketID.
+ // This should be empty except for wrapping IDs. Thus, this should be
+ // highly rare.
+ U64 mt_usec = LLMessageSystem::getMessageTimeUsecs();
+
+ packet_time_map::iterator pit;
+ for(pit = mRecentlyReceivedReliablePackets.upper_bound(mHighestPacketID);
+ pit != mRecentlyReceivedReliablePackets.end(); )
+ {
+ // Validate that the packet ID seems far enough away
+ if ((pit->first - mHighestPacketID) < 100)
+ {
+ llwarns << "Probably incorrectly timing out non-wrapped packets!" << llendl;
+ }
+ U64 delta_t_usec = mt_usec - (*pit).second;
+ F64 delta_t_sec = delta_t_usec * SEC_PER_USEC;
+ if (delta_t_sec > LL_DUPLICATE_SUPPRESSION_TIMEOUT)
+ {
+ // enough time has elapsed we're not likely to get a duplicate on this one
+ llinfos << "Clearing " << pit->first << " from recent list" << llendl;
+ mRecentlyReceivedReliablePackets.erase(pit++);
+ }
+ else
+ {
+ ++pit;
+ }
+ }
+ //llinfos << "Recent list after: " << mRecentlyReceivedReliablePackets.size() << llendl;
+}
+
+BOOL LLCircuitData::checkCircuitTimeout()
+{
+ F64 time_since_last_ping = LLMessageSystem::getMessageTimeSeconds() - mLastPingReceivedTime;
+
+ // Nota Bene: This needs to be turned off if you are debugging multiple simulators
+ if (time_since_last_ping > PING_INTERVAL_MAX)
+ {
+ llwarns << "LLCircuitData::checkCircuitTimeout for " << mHost << " last ping " << time_since_last_ping << " seconds ago." <<llendl;
+ setAlive(FALSE);
+ if (mTimeoutCallback)
+ {
+ llwarns << "LLCircuitData::checkCircuitTimeout for " << mHost << " calling callback." << llendl;
+ mTimeoutCallback(mHost, mTimeoutUserData);
+ }
+ if (!isAlive())
+ {
+ // The callback didn't try and resurrect the circuit. We should kill it.
+ llwarns << "LLCircuitData::checkCircuitTimeout for " << mHost << " still dead, dropping." << llendl;
+ return FALSE;
+ }
+ }
+ else if (time_since_last_ping > PING_INTERVAL_ALARM)
+ {
+ //llwarns << "Unresponsive circuit: " << mHost << ": " << time_since_last_ping << " seconds since last ping."<< llendl;
+ }
+ return TRUE;
+}
+
+// Call this method when a reliable message comes in - this will
+// correctly place the packet in the correct list to be acked later.
+BOOL LLCircuitData::collectRAck(TPACKETID packet_num)
+{
+ if (mAcks.empty())
+ {
+ // First extra ack, we need to add ourselves to the list of circuits that need to send acks
+ gMessageSystem->mCircuitInfo.mSendAckMap[mHost] = this;
+ }
+
+ mAcks.push_back(packet_num);
+ return TRUE;
+}
+
+// this method is called during the message system processAcks() to
+// send out any acks that did not get sent already.
+void LLCircuit::sendAcks()
+{
+ LLCircuitData* cd;
+ circuit_data_map::iterator end = mSendAckMap.end();
+ for(circuit_data_map::iterator it = mSendAckMap.begin(); it != end; ++it)
+ {
+ cd = (*it).second;
+
+ S32 count = (S32)cd->mAcks.size();
+ if(count > 0)
+ {
+ // send the packet acks
+ S32 acks_this_packet = 0;
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(acks_this_packet == 0)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_PacketAck);
+ }
+ gMessageSystem->nextBlockFast(_PREHASH_Packets);
+ gMessageSystem->addU32Fast(_PREHASH_ID, cd->mAcks[i]);
+ ++acks_this_packet;
+ if(acks_this_packet > 250)
+ {
+ gMessageSystem->sendMessage(cd->mHost);
+ acks_this_packet = 0;
+ }
+ }
+ if(acks_this_packet > 0)
+ {
+ gMessageSystem->sendMessage(cd->mHost);
+ }
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << cd->mHost << "\tPACKET ACKS:\t";
+ std::ostream_iterator<TPACKETID> append(str, " ");
+ std::copy(cd->mAcks.begin(), cd->mAcks.end(), append);
+ llinfos << str.str().c_str() << llendl;
+ }
+
+ // empty out the acks list
+ cd->mAcks.clear();
+ }
+ }
+
+ // All acks have been sent, clear the map
+ mSendAckMap.clear();
+}
+
+
+std::ostream& operator<<(std::ostream& s, LLCircuitData& circuit)
+{
+ F32 age = circuit.mExistenceTimer.getElapsedTimeF32();
+
+ using namespace std;
+ s << "Circuit " << circuit.mHost << " ";
+ s << circuit.mRemoteID << " ";
+ s << (circuit.mbAlive ? "Alive" : "Not Alive") << " ";
+ s << (circuit.mbAllowTimeout ? "Timeout Allowed" : "Timeout Not Allowed");
+ s << endl;
+
+ s << " Packets Lost: " << circuit.mPacketsLost;
+ s << " Measured Ping: " << circuit.mPingDelay;
+ s << " Averaged Ping: " << circuit.mPingDelayAveraged;
+ s << endl;
+
+ s << "Global In/Out " << S32(age) << " sec";
+ s << " KBytes: " << circuit.mBytesIn / 1024 << "/" << circuit.mBytesOut / 1024;
+ s << " Kbps: ";
+ s << S32(circuit.mBytesIn * 8.f / circuit.mExistenceTimer.getElapsedTimeF32() / 1024.f);
+ s << "/";
+ s << S32(circuit.mBytesOut * 8.f / circuit.mExistenceTimer.getElapsedTimeF32() / 1024.f);
+ s << " Packets: " << circuit.mPacketsIn << "/" << circuit.mPacketsOut;
+ s << endl;
+
+ s << "Recent In/Out " << S32(circuit.mLastPeriodLength) << " sec";
+ s << " KBytes: ";
+ s << circuit.mBytesInLastPeriod / 1024;
+ s << "/";
+ s << circuit.mBytesOutLastPeriod / 1024;
+ s << " Kbps: ";
+ s << S32(circuit.mBytesInLastPeriod * 8.f / circuit.mLastPeriodLength / 1024.f);
+ s << "/";
+ s << S32(circuit.mBytesOutLastPeriod * 8.f / circuit.mLastPeriodLength / 1024.f);
+ s << " Peak Kbps: ";
+ s << S32(circuit.mPeakBPSIn / 1024.f);
+ s << "/";
+ s << S32(circuit.mPeakBPSOut / 1024.f);
+ s << endl;
+
+ return s;
+}
+
+const char* LLCircuitData::getInfoString() const
+{
+ std::ostringstream info;
+ info << "Circuit: " << mHost << std::endl
+ << (mbAlive ? "Alive" : "Not Alive") << std::endl
+ << "Age: " << mExistenceTimer.getElapsedTimeF32() << std::endl;
+ return info.str().c_str();
+}
+
+void LLCircuitData::dumpResendCountAndReset()
+{
+ if (mCurrentResendCount)
+ {
+ llinfos << "Circuit: " << mHost << " resent " << mCurrentResendCount << " packets" << llendl;
+ mCurrentResendCount = 0;
+ }
+}
+
+std::ostream& operator<<(std::ostream& s, LLCircuit &circuit)
+{
+ s << "Circuit Info:" << std::endl;
+ LLCircuit::circuit_data_map::iterator end = circuit.mCircuitData.end();
+ LLCircuit::circuit_data_map::iterator it;
+ for(it = circuit.mCircuitData.begin(); it != end; ++it)
+ {
+ s << *((*it).second) << std::endl;
+ }
+ return s;
+}
+
+const char* LLCircuit::getInfoString() const
+{
+ std::ostringstream info;
+ info << "Circuit Info:" << std::endl;
+ LLCircuit::circuit_data_map::const_iterator end = mCircuitData.end();
+ LLCircuit::circuit_data_map::const_iterator it;
+ for(it = mCircuitData.begin(); it != end; ++it)
+ {
+ info << (*it).second->getInfoString() << std::endl;
+ }
+ return info.str().c_str();
+}
+
+void LLCircuit::getCircuitRange(
+ const LLHost& key,
+ LLCircuit::circuit_data_map::iterator& first,
+ LLCircuit::circuit_data_map::iterator& end)
+{
+ end = mCircuitData.end();
+ first = mCircuitData.upper_bound(key);
+}
+
+TPACKETID LLCircuitData::nextPacketOutID()
+{
+ mPacketsOut++;
+
+ TPACKETID id;
+
+ id = (mPacketsOutID + 1) % LL_MAX_OUT_PACKET_ID;
+
+ if (id < mPacketsOutID)
+ {
+ // we just wrapped on a circuit, reset the wrap ID to zero
+ mWrapID = 0;
+ }
+ mPacketsOutID = id;
+ return id;
+}
+
+
+void LLCircuitData::setPacketInID(TPACKETID id)
+{
+ id = id % LL_MAX_OUT_PACKET_ID;
+ mPacketsInID = id;
+ mRecentlyReceivedReliablePackets.clear();
+
+ mWrapID = id;
+}
+
+
+void LLCircuitData::pingTimerStop(const U8 ping_id)
+{
+ F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
+
+ // Nota Bene: no averaging of ping times until we get a feel for how this works
+ F64 time = mt_secs - mPingTime;
+ if (time == 0.0)
+ {
+ // Ack, we got our ping response on the same frame! Sigh, let's get a real time otherwise
+ // all of our ping calculations will be skewed.
+ mt_secs = LLMessageSystem::getMessageTimeSeconds(TRUE);
+ }
+ mLastPingReceivedTime = mt_secs;
+
+ // If ping is longer than 1 second, we'll get sequence deltas in the ping.
+ // Approximate by assuming each ping counts for 1 second (slightly low, probably)
+ S32 delta_ping = (S32)mLastPingID - (S32) ping_id;
+ if (delta_ping < 0)
+ {
+ delta_ping += 256;
+ }
+
+ U32 msec = (U32) ((delta_ping*PING_INTERVAL + time) * 1000.f);
+ setPingDelay(msec);
+
+ mPingsInTransit = delta_ping;
+ if (mBlocked && (mPingsInTransit <= PING_RELEASE_BLOCK))
+ {
+ mBlocked = FALSE;
+ }
+}
+
+
+void LLCircuitData::pingTimerStart()
+{
+ mPingTime = LLMessageSystem::getMessageTimeSeconds();
+ mPingsInTransit++;
+
+ if (!mBlocked && (mPingsInTransit > PING_START_BLOCK))
+ {
+ mBlocked = TRUE;
+ }
+}
+
+
+U32 LLCircuitData::getPacketsIn() const
+{
+ return mPacketsIn;
+}
+
+
+S32 LLCircuitData::getBytesIn() const
+{
+ return mBytesIn;
+}
+
+
+S32 LLCircuitData::getBytesOut() const
+{
+ return mBytesOut;
+}
+
+
+U32 LLCircuitData::getPacketsOut() const
+{
+ return mPacketsOut;
+}
+
+
+TPACKETID LLCircuitData::getPacketOutID() const
+{
+ return mPacketsOutID;
+}
+
+
+U32 LLCircuitData::getPacketsLost() const
+{
+ return mPacketsLost;
+}
+
+
+BOOL LLCircuitData::isAlive() const
+{
+ return mbAlive;
+}
+
+
+BOOL LLCircuitData::isBlocked() const
+{
+ return mBlocked;
+}
+
+
+BOOL LLCircuitData::getAllowTimeout() const
+{
+ return mbAllowTimeout;
+}
+
+
+U32 LLCircuitData::getPingDelay() const
+{
+ return mPingDelay;
+}
+
+
+F32 LLCircuitData::getPingInTransitTime()
+{
+ // This may be inaccurate in the case of a circuit that was "dead" and then revived,
+ // but only until the first round trip ping is sent - djs
+ F32 time_since_ping_was_sent = 0;
+
+ if (mPingsInTransit)
+ {
+ time_since_ping_was_sent = (F32)((mPingsInTransit*PING_INTERVAL - 1) + (LLMessageSystem::getMessageTimeSeconds() - mPingTime))*1000.f;
+ }
+
+ return time_since_ping_was_sent;
+}
+
+
+void LLCircuitData::setPingDelay(U32 ping)
+{
+ mPingDelay = ping;
+ mPingDelayAveraged = llmax((F32)ping, getPingDelayAveraged());
+ mPingDelayAveraged = ((1.f - LL_AVERAGED_PING_ALPHA) * mPingDelayAveraged)
+ + (LL_AVERAGED_PING_ALPHA * (F32) ping);
+ mPingDelayAveraged = llclamp(mPingDelayAveraged,
+ LL_AVERAGED_PING_MIN,
+ LL_AVERAGED_PING_MAX);
+}
+
+
+F32 LLCircuitData::getPingDelayAveraged()
+{
+ return llmin(llmax(getPingInTransitTime(), mPingDelayAveraged), LL_AVERAGED_PING_MAX);
+}
+
+
+BOOL LLCircuitData::getTrusted() const
+{
+ return mTrusted;
+}
+
+
+void LLCircuitData::setTrusted(BOOL t)
+{
+ mTrusted = t;
+}
+
+F32 LLCircuitData::getAgeInSeconds() const
+{
+ return mExistenceTimer.getElapsedTimeF32();
+}
diff --git a/indra/llmessage/llcircuit.h b/indra/llmessage/llcircuit.h
new file mode 100644
index 0000000000..003734df29
--- /dev/null
+++ b/indra/llmessage/llcircuit.h
@@ -0,0 +1,315 @@
+/**
+ * @file llcircuit.h
+ * @brief Provides a method for tracking network circuit information
+ * for the UDP message system
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCIRCUIT_H
+#define LL_LLCIRCUIT_H
+
+#include <map>
+#include <vector>
+
+#include "llerror.h"
+#include "linked_lists.h"
+
+#include "lltimer.h"
+#include "timing.h"
+#include "net.h"
+#include "llhost.h"
+#include "llpacketack.h"
+#include "lluuid.h"
+#include "llthrottle.h"
+
+//
+// Constants
+//
+const F32 PING_INTERVAL_MAX = 100.f;
+const F32 PING_INTERVAL_ALARM = 50.f;
+
+
+const F32 LL_AVERAGED_PING_ALPHA = 0.2f; // relaxation constant on ping running average
+const F32 LL_AVERAGED_PING_MAX = 2000; // msec
+const F32 LL_AVERAGED_PING_MIN = 100; // msec // IW: increased to avoid retransmits when a process is slow
+
+const U32 INITIAL_PING_VALUE_MSEC = 1000; // initial value for the ping delay, or for ping delay for an unknown circuit
+
+const TPACKETID LL_MAX_OUT_PACKET_ID = 0x01000000;
+const U8 LL_PACKET_ID_SIZE = 4;
+
+const S32 LL_MAX_RESENT_PACKETS_PER_FRAME = 100;
+const S32 LL_MAX_ACKED_PACKETS_PER_FRAME = 200;
+
+//
+// Prototypes and Predefines
+//
+class LLMessageSystem;
+class LLEncodedDatagramService;
+
+//
+// Classes
+//
+
+
+class LLCircuitData
+{
+public:
+ LLCircuitData(const LLHost &host, TPACKETID in_id);
+ ~LLCircuitData();
+
+ S32 resendUnackedPackets(const F64 now);
+ void clearDuplicateList(TPACKETID oldest_id);
+
+
+ void dumpResendCountAndReset(); // Used for tracking how many resends are being done on a circuit.
+
+
+
+ // Public because stupid message system callbacks uses it.
+ void pingTimerStart();
+ void pingTimerStop(const U8 ping_id);
+ void ackReliablePacket(TPACKETID packet_num);
+
+ // remote computer information
+ const LLUUID& getRemoteID() const { return mRemoteID; }
+ const LLUUID& getRemoteSessionID() const { return mRemoteSessionID; }
+ void setRemoteID(const LLUUID& id) { mRemoteID = id; }
+ void setRemoteSessionID(const LLUUID& id) { mRemoteSessionID = id; }
+
+ void setTrusted(BOOL t);
+
+ // The local end point ID is used when establishing a trusted circuit.
+ // no matching set function for getLocalEndPointID()
+ // mLocalEndPointID should only ever be setup in the LLCircuitData constructor
+ const LLUUID& getLocalEndPointID() const { return mLocalEndPointID; }
+
+ U32 getPingDelay() const;
+ S32 getPingsInTransit() const { return mPingsInTransit; }
+
+ // ACCESSORS
+ BOOL isAlive() const;
+ BOOL isBlocked() const;
+ BOOL getAllowTimeout() const;
+ F32 getPingDelayAveraged();
+ F32 getPingInTransitTime();
+ U32 getPacketsIn() const;
+ S32 getBytesIn() const;
+ S32 getBytesOut() const;
+ U32 getPacketsOut() const;
+ U32 getPacketsLost() const;
+ TPACKETID getPacketOutID() const;
+ BOOL getTrusted() const;
+ F32 getAgeInSeconds() const;
+ S32 getUnackedPacketCount() const { return mUnackedPacketCount; }
+ S32 getUnackedPacketBytes() const { return mUnackedPacketBytes; }
+ F64 getNextPingSendTime() const { return mNextPingSendTime; }
+
+ LLThrottleGroup &getThrottleGroup() { return mThrottles; }
+
+ class less
+ {
+ public:
+ bool operator()(const LLCircuitData* lhs, const LLCircuitData* rhs) const
+ {
+ if (lhs->getNextPingSendTime() < rhs->getNextPingSendTime())
+ {
+ return true;
+ }
+ else if (lhs->getNextPingSendTime() > rhs->getNextPingSendTime())
+ {
+ return false;
+ }
+ else return lhs > rhs;
+ }
+ };
+
+ //
+ // Debugging stuff (not necessary for operation)
+ //
+ void checkPeriodTime(); // Reset per-period counters if necessary.
+ friend std::ostream& operator<<(std::ostream& s, LLCircuitData &circuit);
+ const char* getInfoString() const;
+
+ friend class LLCircuit;
+ friend class LLMessageSystem;
+ friend class LLEncodedDatagramService;
+ friend void crash_on_spaceserver_timeout (const LLHost &host, void *); // HACK, so it has access to setAlive() so it can send a final shutdown message.
+protected:
+ TPACKETID nextPacketOutID();
+ void setPacketInID(TPACKETID id);
+ void checkPacketInID(TPACKETID id, BOOL receive_resent);
+ void setPingDelay(U32 ping);
+ BOOL checkCircuitTimeout(); // Return FALSE if the circuit is dead and should be cleaned up
+
+ void addBytesIn(S32 bytes);
+ void addBytesOut(S32 bytes);
+
+ U8 nextPingID() { mLastPingID++; return mLastPingID; }
+
+ BOOL updateWatchDogTimers(LLMessageSystem *msgsys); // Return FALSE if the circuit is dead and should be cleaned up
+
+ void addReliablePacket(S32 mSocket, U8 *buf_ptr, S32 buf_len, LLReliablePacketParams *params);
+ BOOL isDuplicateResend(TPACKETID packetnum);
+ // Call this method when a reliable message comes in - this will
+ // correctly place the packet in the correct list to be acked
+ // later. RAack = requested ack
+ BOOL collectRAck(TPACKETID packet_num);
+
+
+ void setTimeoutCallback(void (*callback_func)(const LLHost &host, void *user_data), void *user_data);
+
+
+
+ void setAlive(BOOL b_alive);
+ void setAllowTimeout(BOOL allow);
+
+protected:
+ // Identification for this circuit.
+ LLHost mHost;
+ LLUUID mRemoteID;
+ LLUUID mRemoteSessionID;
+
+ LLThrottleGroup mThrottles;
+
+ TPACKETID mWrapID;
+
+ // Current packet IDs of incoming/outgoing packets
+ // Used for packet sequencing/packet loss detection.
+ TPACKETID mPacketsOutID;
+ TPACKETID mPacketsInID;
+ TPACKETID mHighestPacketID;
+
+
+ // Callback and data to run in the case of a circuit timeout.
+ // Used primarily to try and reconnect to servers if they crash/die.
+ void (*mTimeoutCallback)(const LLHost &host, void *user_data);
+ void *mTimeoutUserData;
+
+ BOOL mTrusted; // Is this circuit trusted?
+ BOOL mbAllowTimeout; // Machines can "pause" circuits, forcing them not to be dropped
+
+ BOOL mbAlive; // Indicates whether a circuit is "alive", i.e. responded to pings
+
+ BOOL mBlocked; // Blocked is true if the circuit is hosed, i.e. far behind on pings
+
+ // Not sure what the difference between this and mLastPingSendTime is
+ F64 mPingTime; // Time at which a ping was sent.
+
+ F64 mLastPingSendTime; // Time we last sent a ping
+ F64 mLastPingReceivedTime; // Time we last received a ping
+ F64 mNextPingSendTime; // Time to try and send the next ping
+ S32 mPingsInTransit; // Number of pings in transit
+ U8 mLastPingID; // ID of the last ping that we sent out
+
+
+ // Used for determining the resend time for reliable resends.
+ U32 mPingDelay; // raw ping delay
+ F32 mPingDelayAveraged; // averaged ping delay (fast attack/slow decay)
+
+ typedef std::map<TPACKETID, U64> packet_time_map;
+
+ packet_time_map mPotentialLostPackets;
+ packet_time_map mRecentlyReceivedReliablePackets;
+ std::vector<TPACKETID> mAcks;
+
+ typedef std::map<TPACKETID, LLReliablePacket *> reliable_map;
+ typedef reliable_map::iterator reliable_iter;
+
+ reliable_map mUnackedPackets;
+ reliable_map mFinalRetryPackets;
+
+ S32 mUnackedPacketCount;
+ S32 mUnackedPacketBytes;
+
+
+ LLUUID mLocalEndPointID;
+
+ //
+ // These variables are being used for statistical and debugging purpose ONLY,
+ // as far as I can tell.
+ //
+
+ U32 mPacketsOut;
+ U32 mPacketsIn;
+ S32 mPacketsLost;
+ S32 mBytesIn;
+ S32 mBytesOut;
+
+ F32 mLastPeriodLength; // seconds
+ S32 mBytesInLastPeriod;
+ S32 mBytesOutLastPeriod;
+ S32 mBytesInThisPeriod;
+ S32 mBytesOutThisPeriod;
+ F32 mPeakBPSIn; // bits per second, max of all period bps
+ F32 mPeakBPSOut; // bits per second, max of all period bps
+ F64 mPeriodTime;
+ LLTimer mExistenceTimer; // initialized when circuit created, used to track bandwidth numbers
+
+ S32 mCurrentResendCount; // Number of resent packets since last spam
+};
+
+
+// Actually a singleton class -- the global messagesystem
+// has a single LLCircuit member.
+class LLCircuit
+{
+public:
+ // CREATORS
+ LLCircuit();
+ ~LLCircuit();
+
+ // ACCESSORS
+ LLCircuitData* findCircuit(const LLHost& host) const;
+ BOOL isCircuitAlive(const LLHost& host) const;
+
+ // MANIPULATORS
+ LLCircuitData *addCircuitData(const LLHost &host, TPACKETID in_id);
+ void removeCircuitData(const LLHost &host);
+
+ void updateWatchDogTimers(LLMessageSystem *msgsys);
+ void resendUnackedPackets(S32& unacked_list_length, S32& unacked_list_size);
+
+ // this method is called during the message system processAcks()
+ // to send out any acks that did not get sent already.
+ void sendAcks();
+
+ friend std::ostream& operator<<(std::ostream& s, LLCircuit &circuit);
+ const char* getInfoString() const;
+
+ void dumpResends();
+
+ typedef std::map<LLHost, LLCircuitData*> circuit_data_map;
+
+ /**
+ * @brief This method gets an iterator range starting after key in
+ * the circuit data map.
+ *
+ * @param key The the host before first.
+ * @param first[out] The first matching value after key. This
+ * value will equal end if there are no entries.
+ * @param end[out] The end of the iteration sequence.
+ */
+ void getCircuitRange(
+ const LLHost& key,
+ circuit_data_map::iterator& first,
+ circuit_data_map::iterator& end);
+
+ // Lists that optimize how many circuits we need to traverse a frame
+ // HACK - this should become protected eventually, but stupid !@$@# message system/circuit classes are jumbling things up.
+ circuit_data_map mUnackedCircuitMap; // Map of circuits with unacked data
+ circuit_data_map mSendAckMap; // Map of circuits which need to send acks
+protected:
+ circuit_data_map mCircuitData;
+
+ typedef std::set<LLCircuitData *, LLCircuitData::less> ping_set_t; // Circuits sorted by next ping time
+ ping_set_t mPingSet;
+
+ // This variable points to the last circuit data we found to
+ // optimize the many, many times we call findCircuit. This may be
+ // set in otherwise const methods, so it is declared mutable.
+ mutable LLCircuitData* mLastCircuit;
+};
+#endif
diff --git a/indra/llmessage/llclassifiedflags.cpp b/indra/llmessage/llclassifiedflags.cpp
new file mode 100644
index 0000000000..d84071e2eb
--- /dev/null
+++ b/indra/llmessage/llclassifiedflags.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file llclassifiedflags.cpp
+ * @brief
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+// llclassifiedflags.cpp
+//
+// Some exported symbols and functions for dealing with classified flags.
+//
+// Copyright 2005, Linden Research, Inc
+//*****************************************************************************
+
+#include "linden_common.h"
+
+#include "llclassifiedflags.h"
+
+ClassifiedFlags pack_classified_flags(BOOL is_mature, BOOL auto_renew)
+{
+ U8 rv = 0;
+ if(is_mature) rv |= CLASSIFIED_FLAG_MATURE;
+ if(auto_renew) rv |= CLASSIFIED_FLAG_AUTO_RENEW;
+ return rv;
+}
+
+bool is_cf_mature(ClassifiedFlags flags)
+{
+ return ((flags & CLASSIFIED_FLAG_MATURE) != 0);
+}
+
+// Deprecated, but leaving commented out because someday we might
+// want to let users enable/disable classifieds. JC
+//bool is_cf_enabled(ClassifiedFlags flags)
+//{
+// return ((flags & CLASSIFIED_FLAG_ENABLED) == CLASSIFIED_FLAG_ENABLED);
+//}
+
+bool is_cf_update_time(ClassifiedFlags flags)
+{
+ return ((flags & CLASSIFIED_FLAG_UPDATE_TIME) != 0);
+}
+
+bool is_cf_auto_renew(ClassifiedFlags flags)
+{
+ return ((flags & CLASSIFIED_FLAG_AUTO_RENEW) != 0);
+}
diff --git a/indra/llmessage/llclassifiedflags.h b/indra/llmessage/llclassifiedflags.h
new file mode 100644
index 0000000000..1949eb54d0
--- /dev/null
+++ b/indra/llmessage/llclassifiedflags.h
@@ -0,0 +1,31 @@
+/**
+ * @file llclassifiedflags.h
+ * @brief Flags used in the classifieds.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLASSIFIEDFLAGS_H
+#define LL_LLCLASSIFIEDFLAGS_H
+
+typedef U8 ClassifiedFlags;
+
+const U8 CLASSIFIED_FLAG_NONE = 1 << 0;
+const U8 CLASSIFIED_FLAG_MATURE = 1 << 1;
+//const U8 CLASSIFIED_FLAG_ENABLED = 1 << 2; // see llclassifiedflags.cpp
+//const U8 CLASSIFIED_FLAG_HAS_PRICE= 1 << 3; // deprecated
+const U8 CLASSIFIED_FLAG_UPDATE_TIME= 1 << 4;
+const U8 CLASSIFIED_FLAG_AUTO_RENEW = 1 << 5;
+
+const U8 CLASSIFIED_QUERY_FILTER_MATURE = 1 << 1;
+const U8 CLASSIFIED_QUERY_FILTER_ENABLED = 1 << 2;
+const U8 CLASSIFIED_QUERY_FILTER_PRICE = 1 << 3;
+
+ClassifiedFlags pack_classified_flags(BOOL is_mature, BOOL auto_renew);
+bool is_cf_mature(ClassifiedFlags flags);
+//bool is_cf_enabled(ClassifiedFlags flags);
+bool is_cf_update_time(ClassifiedFlags flags);
+bool is_cf_auto_renew(ClassifiedFlags flags);
+
+#endif
diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp
new file mode 100644
index 0000000000..bc9f4f3486
--- /dev/null
+++ b/indra/llmessage/lldatapacker.cpp
@@ -0,0 +1,1866 @@
+/**
+ * @file lldatapacker.cpp
+ * @brief Data packer implementation.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <string.h>
+
+#include "linden_common.h"
+
+#include "lldatapacker.h"
+#include "llerror.h"
+
+#include "message.h"
+
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "lluuid.h"
+
+// *NOTE: there are functions below which use sscanf and rely on this
+// particular value of DP_BUFSIZE. Search for '511' (DP_BUFSIZE - 1)
+// to find them if you change this number.
+const S32 DP_BUFSIZE = 512;
+
+static char DUMMY_BUFFER[128]; /*Flawfinder: ignore*/
+
+LLDataPacker::LLDataPacker() : mPassFlags(0), mWriteEnabled(FALSE)
+{
+}
+
+BOOL LLDataPacker::packFixed(const F32 value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits)
+{
+ BOOL success = TRUE;
+ S32 unsigned_bits = int_bits + frac_bits;
+ S32 total_bits = unsigned_bits;
+
+ if (is_signed)
+ {
+ total_bits++;
+ }
+
+ S32 min_val;
+ U32 max_val;
+ if (is_signed)
+ {
+ min_val = 1 << int_bits;
+ min_val *= -1;
+ }
+ else
+ {
+ min_val = 0;
+ }
+ max_val = 1 << int_bits;
+
+ // Clamp to be within range
+ F32 fixed_val = llclamp(value, (F32)min_val, (F32)max_val);
+ if (is_signed)
+ {
+ fixed_val += max_val;
+ }
+ fixed_val *= 1 << frac_bits;
+
+ if (total_bits <= 8)
+ {
+ packU8((U8)fixed_val, name);
+ }
+ else if (total_bits <= 16)
+ {
+ packU16((U16)fixed_val, name);
+ }
+ else if (total_bits <= 31)
+ {
+ packU32((U32)fixed_val, name);
+ }
+ else
+ {
+ llerrs << "Using fixed-point packing of " << total_bits << " bits, why?!" << llendl;
+ }
+ return success;
+}
+
+BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits)
+{
+ //BOOL success = TRUE;
+ //llinfos << "unpackFixed:" << name << " int:" << int_bits << " frac:" << frac_bits << llendl;
+ BOOL ok = FALSE;
+ S32 unsigned_bits = int_bits + frac_bits;
+ S32 total_bits = unsigned_bits;
+
+ if (is_signed)
+ {
+ total_bits++;
+ }
+
+ S32 min_val;
+ U32 max_val;
+ if (is_signed)
+ {
+ min_val = 1 << int_bits;
+ min_val *= -1;
+ }
+ max_val = 1 << int_bits;
+
+ F32 fixed_val;
+ if (total_bits <= 8)
+ {
+ U8 fixed_8;
+ ok = unpackU8(fixed_8, name);
+ fixed_val = (F32)fixed_8;
+ }
+ else if (total_bits <= 16)
+ {
+ U16 fixed_16;
+ ok = unpackU16(fixed_16, name);
+ fixed_val = (F32)fixed_16;
+ }
+ else if (total_bits <= 31)
+ {
+ U32 fixed_32;
+ ok = unpackU32(fixed_32, name);
+ fixed_val = (F32)fixed_32;
+ }
+ else
+ {
+ fixed_val = 0;
+ llerrs << "Bad bit count: " << total_bits << llendl;
+ }
+
+ //llinfos << "Fixed_val:" << fixed_val << llendl;
+
+ fixed_val /= (F32)(1 << frac_bits);
+ if (is_signed)
+ {
+ fixed_val -= max_val;
+ }
+ value = fixed_val;
+ //llinfos << "Value: " << value << llendl;
+ return ok;
+}
+
+//---------------------------------------------------------------------------
+// LLDataPackerBinaryBuffer implementation
+//---------------------------------------------------------------------------
+
+BOOL LLDataPackerBinaryBuffer::packString(const char *value, const char *name)
+{
+ BOOL success = TRUE;
+ S32 length = (S32)strlen(value) + 1; /*Flawfinder: ignore*/
+
+ success &= verifyLength(length, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value, MVT_VARIABLE, length);
+ }
+ mCurBufferp += length;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackString(char *value, const char *name)
+{
+ BOOL success = TRUE;
+ S32 length = (S32)strlen((char *)mCurBufferp) + 1; /*Flawfinder: ignore*/
+
+ success &= verifyLength(length, name);
+
+ htonmemcpy(value, mCurBufferp, MVT_VARIABLE, length);
+ mCurBufferp += length;
+ return success;
+}
+
+BOOL LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(size + 4, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &size, MVT_S32, 4);
+ }
+ mCurBufferp += 4;
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value, MVT_VARIABLE, size);
+ }
+ mCurBufferp += size;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(4, name);
+ htonmemcpy(&size, mCurBufferp, MVT_S32, 4);
+ mCurBufferp += 4;
+ success &= verifyLength(size, name);
+ if (success)
+ {
+ htonmemcpy(value, mCurBufferp, MVT_VARIABLE, size);
+ mCurBufferp += size;
+ }
+ else
+ {
+ llwarns << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << llendl;
+ success = FALSE;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(size, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value, MVT_VARIABLE, size);
+ }
+ mCurBufferp += size;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(size, name);
+ htonmemcpy(value, mCurBufferp, MVT_VARIABLE, size);
+ mCurBufferp += size;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packU8(const U8 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U8), name);
+
+ if (mWriteEnabled)
+ {
+ *mCurBufferp = value;
+ }
+ mCurBufferp++;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackU8(U8 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U8), name);
+
+ value = *mCurBufferp;
+ mCurBufferp++;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packU16(const U16 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U16), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_U16, 2);
+ }
+ mCurBufferp += 2;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackU16(U16 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U16), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_U16, 2);
+ mCurBufferp += 2;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packU32(const U32 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U32), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_U32, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackU32(U32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U32), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_U32, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packS32(const S32 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(S32), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_S32, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackS32(S32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(S32), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_S32, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packF32(const F32 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(F32), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_F32, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackF32(F32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(F32), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_F32, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packColor4(const LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);
+ }
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackColor4(LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packColor4U(const LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(4, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackColor4U(LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(4, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+
+BOOL LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(8, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value.mV[0], MVT_F32, 4);
+ htonmemcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4);
+ }
+ mCurBufferp += 8;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackVector2(LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(8, name);
+
+ htonmemcpy(&value.mV[0], mCurBufferp, MVT_F32, 4);
+ htonmemcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4);
+ mCurBufferp += 8;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packVector3(const LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(12, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_LLVector3, 12);
+ }
+ mCurBufferp += 12;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackVector3(LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(12, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_LLVector3, 12);
+ mCurBufferp += 12;
+ return success;
+}
+
+BOOL LLDataPackerBinaryBuffer::packVector4(const LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);
+ }
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackVector4(LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
+ mCurBufferp += 16;
+ return success;
+}
+
+BOOL LLDataPackerBinaryBuffer::packUUID(const LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mData, MVT_LLUUID, 16);
+ }
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackUUID(LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ htonmemcpy(value.mData, mCurBufferp, MVT_LLUUID, 16);
+ mCurBufferp += 16;
+ return success;
+}
+
+const LLDataPackerBinaryBuffer& LLDataPackerBinaryBuffer::operator=(const LLDataPackerBinaryBuffer &a)
+{
+ if (a.getBufferSize() > getBufferSize())
+ {
+ // We've got problems, ack!
+ llerrs << "Trying to do an assignment with not enough room in the target." << llendl;
+ }
+ memcpy(mBufferp, a.mBufferp, a.getBufferSize());
+ return *this;
+}
+
+void LLDataPackerBinaryBuffer::dumpBufferToLog()
+{
+ llwarns << "Binary Buffer Dump, size: " << mBufferSize << llendl;
+ char line_buffer[256]; /*Flawfinder: ignore*/
+ S32 i;
+ S32 cur_line_pos = 0;
+
+ S32 cur_line = 0;
+ for (i = 0; i < mBufferSize; i++)
+ {
+ snprintf(line_buffer + cur_line_pos*3, sizeof(line_buffer) - cur_line_pos*3, "%02x ", mBufferp[i]); /*Flawfinder: ignore*/
+ cur_line_pos++;
+ if (cur_line_pos >= 16)
+ {
+ cur_line_pos = 0;
+ llwarns << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << llendl;
+ cur_line++;
+ }
+ }
+ if (cur_line_pos)
+ {
+ llwarns << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << llendl;
+ }
+}
+
+//---------------------------------------------------------------------------
+// LLDataPackerAsciiBuffer implementation
+//---------------------------------------------------------------------------
+BOOL LLDataPackerAsciiBuffer::packString(const char *value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", value); /*Flawfinder: ignore*/
+ }
+ else
+ {
+ numCopied = (S32)strlen(value) + 1; /*Flawfinder: ignore*/
+ }
+
+ // snprintf returns number of bytes that would have been written
+ // had the output not being truncated. In that case, it will
+ // return >= passed in size value. so a check needs to be added
+ // to detect truncation, and if there is any, only account for the
+ // actual number of bytes written..and not what could have been
+ // written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ // *NOTE: I believe we need to mark a failure bit at this point.
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::unpackString(char *value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+ // XXXCHECK: Can result in buffer overrun. Need to pass in size for "value"
+ strcpy(value, valuestr); /*Flawfinder: ignore*/
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packBinaryData(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%010d ", size); /*Flawfinder: ignore*/
+
+ // snprintf returns number of bytes that would have been
+ // written had the output not being truncated. In that case,
+ // it will retuen >= passed in size value. so a check needs
+ // to be added to detect truncation, and if there is any, only
+ // account for the actual number of bytes written..and not
+ // what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+
+
+ S32 i;
+ BOOL bBufferFull = FALSE;
+ for (i = 0; i < size && !bBufferFull; i++)
+ {
+ numCopied = snprintf(mCurBufferp, getBufferSize()-getCurrentSize(), "%02x ", value[i]); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ bBufferFull = TRUE;
+ }
+ mCurBufferp += numCopied;
+ }
+
+ if (!bBufferFull)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ }
+ }
+ else
+ {
+ // why +10 ?? XXXCHECK
+ numCopied = 10 + 1; // size plus newline
+ numCopied += size;
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ }
+
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+ sscanf(valuestr,"%010d", &size);
+ cur_pos += 11;
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ if (mWriteEnabled)
+ {
+ S32 i;
+ int numCopied = 0;
+ BOOL bBufferFull = FALSE;
+ for (i = 0; i < size && !bBufferFull; i++)
+ {
+ numCopied = snprintf(mCurBufferp, getBufferSize()-getCurrentSize(), "%02x ", value[i]); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ bBufferFull = TRUE;
+ }
+ mCurBufferp += numCopied;
+
+ }
+ if (!bBufferFull)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ }
+ }
+ else
+ {
+ int numCopied = 2 * size + 1; //hex bytes plus newline
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+
+BOOL LLDataPackerAsciiBuffer::packU8(const U8 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d\n", value); /*Flawfinder: ignore*/
+ }
+ else
+ {
+ // just do the write to a temp buffer to get the length
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%d\n", value); /* Flawfinder: ignore */
+ }
+
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackU8(U8 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::packU16(const U16 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d\n", value); /*Flawfinder: ignore*/
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%d\n", value); /* Flawfinder: ignore */
+ }
+
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackU16(U16 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packU32(const U32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%u\n", value); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%u\n", value); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackU32(U32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%u", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packS32(const S32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d\n", value); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%d\n", value); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackS32(S32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%d", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packF32(const F32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g\n", value); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%g\n", value); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackF32(F32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packColor4(const LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackColor4(LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::packColor4U(const LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d %d %d %d\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%d %d %d %d\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackColor4U(LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 r, g, b, a;
+
+ sscanf(valuestr,"%d %d %d %d", &r, &g, &b, &a);
+ value.mV[0] = r;
+ value.mV[1] = g;
+ value.mV[2] = b;
+ value.mV[3] = a;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packVector2(const LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g\n", value.mV[0], value.mV[1]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g\n", value.mV[0], value.mV[1]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackVector2(LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g", &value.mV[0], &value.mV[1]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packVector3(const LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g %g\n", value.mV[0], value.mV[1], value.mV[2]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g %g\n", value.mV[0], value.mV[1], value.mV[2]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackVector3(LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g", &value.mV[0], &value.mV[1], &value.mV[2]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::packVector4(const LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackVector4(LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packUUID(const LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ char tmp_str[64]; /* Flawfinder: ignore */
+ value.toString(tmp_str);
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", tmp_str); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = 64 + 1; // UUID + newline
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ success = FALSE;
+ }
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackUUID(LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char tmp_str[64]; /* Flawfinder: ignore */
+ sscanf(valuestr, "%63s", tmp_str);
+ value.set(tmp_str);
+
+ return success;
+}
+
+void LLDataPackerAsciiBuffer::dump()
+{
+ llinfos << "Buffer: " << mBufferp << llendl;
+}
+
+void LLDataPackerAsciiBuffer::writeIndentedName(const char *name)
+{
+ if (mIncludeNames)
+ {
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\t", name); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = (S32)strlen(name) + 1; //name + tab /* Flawfinder: ignore */
+ }
+
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ }
+}
+
+BOOL LLDataPackerAsciiBuffer::getValueStr(const char *name, char *out_value, S32 value_len)
+{
+ BOOL success = TRUE;
+ char buffer[DP_BUFSIZE]; /* Flawfinder: ignore */
+ char keyword[DP_BUFSIZE]; /* Flawfinder: ignore */
+ char value[DP_BUFSIZE]; /* Flawfinder: ignore */
+
+ buffer[0] = '\0';
+ keyword[0] = '\0';
+ value[0] = '\0';
+
+ if (mIncludeNames)
+ {
+ // Read both the name and the value, and validate the name.
+ sscanf(mCurBufferp, "%511[^\n]", buffer);
+ // Skip the \n
+ mCurBufferp += (S32)strlen(buffer) + 1;
+
+ sscanf(buffer, "%511s %511[^\n]", keyword, value);
+
+ if (strcmp(keyword, name))
+ {
+ llwarns << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ // Just the value exists
+ sscanf(mCurBufferp, "%511[^\n]", value);
+ // Skip the \n
+ mCurBufferp += (S32)strlen(value) + 1; /* Flawfinder: ignore */
+ }
+
+ S32 in_value_len = (S32)strlen(value)+1; /* Flawfinder: ignore */
+ S32 min_len = llmin(in_value_len, value_len);
+ memcpy(out_value, value, min_len); /* Flawfinder: ignore */
+ out_value[min_len-1] = 0;
+
+ return success;
+}
+
+//---------------------------------------------------------------------------
+// LLDataPackerAsciiFile implementation
+//---------------------------------------------------------------------------
+BOOL LLDataPackerAsciiFile::packString(const char *value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%s\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value << "\n";
+ }
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::unpackString(char *value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+ strncpy(value, valuestr,DP_BUFSIZE);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packBinaryData(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ if (mFP)
+ {
+ fprintf(mFP, "%010d ", size);
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ fprintf(mFP, "%02x ", value[i]);
+ }
+ fprintf(mFP, "\n");
+ }
+ else if (mOutputStream)
+ {
+ char buffer[32]; /* Flawfinder: ignore */
+ snprintf(buffer,sizeof(buffer), "%010d ", size); /* Flawfinder: ignore */
+ *mOutputStream << buffer;
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ snprintf(buffer, sizeof(buffer), "%02x ", value[i]); /* Flawfinder: ignore */
+ *mOutputStream << buffer;
+ }
+ *mOutputStream << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackBinaryData(U8 *value, S32 &size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+ sscanf(valuestr,"%010d", &size);
+ cur_pos += 11;
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ if (mFP)
+ {
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ fprintf(mFP, "%02x ", value[i]);
+ }
+ fprintf(mFP, "\n");
+ }
+ else if (mOutputStream)
+ {
+ char buffer[32]; /*Flawfinder: ignore*/
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ snprintf(buffer, sizeof(buffer), "%02x ", value[i]); /*Flawfinder: ignore*/
+ *mOutputStream << buffer;
+ }
+ *mOutputStream << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+
+BOOL LLDataPackerAsciiFile::packU8(const U8 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d\n", value);
+ }
+ else if (mOutputStream)
+ {
+ // We have to cast this to an integer because streams serialize
+ // bytes as bytes - not as text.
+ *mOutputStream << (S32)value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackU8(U8 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::packU16(const U16 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackU16(U16 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packU32(const U32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%u\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackU32(U32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%u", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packS32(const S32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackS32(S32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%d", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packF32(const F32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackF32(F32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packColor4(const LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << " " << value.mV[2] << " " << value.mV[3] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackColor4(LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::packColor4U(const LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d %d %d %d\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << (S32)(value.mV[0]) << " " << (S32)(value.mV[1]) << " " << (S32)(value.mV[2]) << " " << (S32)(value.mV[3]) << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackColor4U(LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 r, g, b, a;
+
+ sscanf(valuestr,"%d %d %d %d", &r, &g, &b, &a);
+ value.mV[0] = r;
+ value.mV[1] = g;
+ value.mV[2] = b;
+ value.mV[3] = a;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packVector2(const LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g\n", value.mV[0], value.mV[1]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackVector2(LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g", &value.mV[0], &value.mV[1]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packVector3(const LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g %g\n", value.mV[0], value.mV[1], value.mV[2]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << " " << value.mV[2] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackVector3(LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g", &value.mV[0], &value.mV[1], &value.mV[2]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::packVector4(const LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << " " << value.mV[2] << " " << value.mV[3] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackVector4(LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packUUID(const LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ char tmp_str[64]; /*Flawfinder: ignore */
+ value.toString(tmp_str);
+ if (mFP)
+ {
+ fprintf(mFP,"%s\n", tmp_str);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << tmp_str << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackUUID(LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char tmp_str[64]; /*Flawfinder: ignore */
+ sscanf(valuestr,"%63s",tmp_str);
+ value.set(tmp_str);
+
+ return success;
+}
+
+
+void LLDataPackerAsciiFile::writeIndentedName(const char *name)
+{
+ char indent_buf[64]; /*Flawfinder: ignore*/
+
+ S32 i;
+ for(i = 0; i < mIndent; i++)
+ {
+ indent_buf[i] = '\t';
+ }
+ indent_buf[i] = 0;
+ if (mFP)
+ {
+ fprintf(mFP,"%s%s\t",indent_buf, name);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << indent_buf << name << "\t";
+ }
+}
+
+BOOL LLDataPackerAsciiFile::getValueStr(const char *name, char *out_value, S32 value_len)
+{
+ BOOL success = FALSE;
+ char buffer[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ char keyword[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ char value[DP_BUFSIZE]; /*Flawfinder: ignore*/
+
+ buffer[0] = '\0';
+ keyword[0] = '\0';
+ value[0] = '\0';
+
+ if (mFP)
+ {
+ fpos_t last_pos;
+ fgetpos(mFP, &last_pos);
+ fgets(buffer, DP_BUFSIZE, mFP);
+
+ sscanf(buffer, "%511s %511[^\n]", keyword, value);
+
+ if (!keyword[0])
+ {
+ llwarns << "Data packer could not get the keyword!" << llendl;
+ fsetpos(mFP, &last_pos);
+ return FALSE;
+ }
+ if (strcmp(keyword, name))
+ {
+ llwarns << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << llendl;
+ fsetpos(mFP, &last_pos);
+ return FALSE;
+ }
+
+ S32 in_value_len = (S32)strlen(value)+1; /*Flawfinder: ignore*/
+ S32 min_len = llmin(in_value_len, value_len);
+ memcpy(out_value, value, min_len); /*Flawfinder: ignore*/
+ out_value[min_len-1] = 0;
+ success = TRUE;
+ }
+ else if (mInputStream)
+ {
+ mInputStream->getline(buffer, DP_BUFSIZE);
+
+ sscanf(buffer, "%511s %511[^\n]", keyword, value);
+ if (!keyword[0])
+ {
+ llwarns << "Data packer could not get the keyword!" << llendl;
+ return FALSE;
+ }
+ if (strcmp(keyword, name))
+ {
+ llwarns << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << llendl;
+ return FALSE;
+ }
+
+ S32 in_value_len = (S32)strlen(value)+1; /*Flawfinder: ignore*/
+ S32 min_len = llmin(in_value_len, value_len);
+ memcpy(out_value, value, min_len); /*Flawfinder: ignore*/
+ out_value[min_len-1] = 0;
+ success = TRUE;
+ }
+
+ return success;
+}
diff --git a/indra/llmessage/lldatapacker.h b/indra/llmessage/lldatapacker.h
new file mode 100644
index 0000000000..10ca35d2c7
--- /dev/null
+++ b/indra/llmessage/lldatapacker.h
@@ -0,0 +1,398 @@
+/**
+ * @file lldatapacker.h
+ * @brief Data packer declaration for tightly storing binary data.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDATAPACKER_H
+#define LL_LLDATAPACKER_H
+
+#include <stdio.h>
+#include <iostream>
+
+#include "llerror.h"
+
+class LLColor4;
+class LLColor4U;
+class LLVector2;
+class LLVector3;
+class LLVector4;
+class LLUUID;
+
+class LLDataPacker
+{
+public:
+ virtual ~LLDataPacker() {}
+
+ virtual void reset() { llerrs << "Using unimplemented datapacker reset!" << llendl; };
+ virtual void dumpBufferToLog() { llerrs << "dumpBufferToLog not implemented for this type!" << llendl; }
+
+ virtual BOOL hasNext() const = 0;
+
+ virtual BOOL packString(const char *value, const char *name) = 0;
+ virtual BOOL unpackString(char *value, const char *name) = 0;
+
+ virtual BOOL packBinaryData(const U8 *value, S32 size, const char *name) = 0;
+ virtual BOOL unpackBinaryData(U8 *value, S32 &size, const char *name) = 0;
+
+ // Constant size binary data packing
+ virtual BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name) = 0;
+ virtual BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name) = 0;
+
+ virtual BOOL packU8(const U8 value, const char *name) = 0;
+ virtual BOOL unpackU8(U8 &value, const char *name) = 0;
+
+ virtual BOOL packU16(const U16 value, const char *name) = 0;
+ virtual BOOL unpackU16(U16 &value, const char *name) = 0;
+
+ virtual BOOL packU32(const U32 value, const char *name) = 0;
+ virtual BOOL unpackU32(U32 &value, const char *name) = 0;
+
+ virtual BOOL packS32(const S32 value, const char *name) = 0;
+ virtual BOOL unpackS32(S32 &value, const char *name) = 0;
+
+ virtual BOOL packF32(const F32 value, const char *name) = 0;
+ virtual BOOL unpackF32(F32 &value, const char *name) = 0;
+
+ // Packs a float into an integer, using the given size
+ // and picks the right U* data type to pack into.
+ BOOL packFixed(const F32 value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits);
+ BOOL unpackFixed(F32 &value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits);
+
+ virtual BOOL packColor4(const LLColor4 &value, const char *name) = 0;
+ virtual BOOL unpackColor4(LLColor4 &value, const char *name) = 0;
+
+ virtual BOOL packColor4U(const LLColor4U &value, const char *name) = 0;
+ virtual BOOL unpackColor4U(LLColor4U &value, const char *name) = 0;
+
+ virtual BOOL packVector2(const LLVector2 &value, const char *name) = 0;
+ virtual BOOL unpackVector2(LLVector2 &value, const char *name) = 0;
+
+ virtual BOOL packVector3(const LLVector3 &value, const char *name) = 0;
+ virtual BOOL unpackVector3(LLVector3 &value, const char *name) = 0;
+
+ virtual BOOL packVector4(const LLVector4 &value, const char *name) = 0;
+ virtual BOOL unpackVector4(LLVector4 &value, const char *name) = 0;
+
+ virtual BOOL packUUID(const LLUUID &value, const char *name) = 0;
+ virtual BOOL unpackUUID(LLUUID &value, const char *name) = 0;
+ U32 getPassFlags() const { return mPassFlags; }
+ void setPassFlags(U32 flags) { mPassFlags = flags; }
+protected:
+ LLDataPacker();
+protected:
+ U32 mPassFlags;
+ BOOL mWriteEnabled; // disable this to do things like determine filesize without actually copying data
+};
+
+class LLDataPackerBinaryBuffer : public LLDataPacker
+{
+public:
+ LLDataPackerBinaryBuffer(U8 *bufferp, S32 size)
+ : LLDataPacker(),
+ mBufferp(bufferp),
+ mCurBufferp(bufferp),
+ mBufferSize(size)
+ {
+ mWriteEnabled = TRUE;
+ }
+
+ LLDataPackerBinaryBuffer()
+ : LLDataPacker(),
+ mBufferp(NULL),
+ mCurBufferp(NULL),
+ mBufferSize(0)
+ {
+ }
+
+ /*virtual*/ BOOL packString(const char *value, const char *name);
+ /*virtual*/ BOOL unpackString(char *value, const char *name);
+
+ /*virtual*/ BOOL packBinaryData(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryData(U8 *value, S32 &size, const char *name);
+
+ // Constant size binary data packing
+ /*virtual*/ BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name);
+
+ /*virtual*/ BOOL packU8(const U8 value, const char *name);
+ /*virtual*/ BOOL unpackU8(U8 &value, const char *name);
+
+ /*virtual*/ BOOL packU16(const U16 value, const char *name);
+ /*virtual*/ BOOL unpackU16(U16 &value, const char *name);
+
+ /*virtual*/ BOOL packU32(const U32 value, const char *name);
+ /*virtual*/ BOOL unpackU32(U32 &value, const char *name);
+
+ /*virtual*/ BOOL packS32(const S32 value, const char *name);
+ /*virtual*/ BOOL unpackS32(S32 &value, const char *name);
+
+ /*virtual*/ BOOL packF32(const F32 value, const char *name);
+ /*virtual*/ BOOL unpackF32(F32 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4(const LLColor4 &value, const char *name);
+ /*virtual*/ BOOL unpackColor4(LLColor4 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4U(const LLColor4U &value, const char *name);
+ /*virtual*/ BOOL unpackColor4U(LLColor4U &value, const char *name);
+
+ /*virtual*/ BOOL packVector2(const LLVector2 &value, const char *name);
+ /*virtual*/ BOOL unpackVector2(LLVector2 &value, const char *name);
+
+ /*virtual*/ BOOL packVector3(const LLVector3 &value, const char *name);
+ /*virtual*/ BOOL unpackVector3(LLVector3 &value, const char *name);
+
+ /*virtual*/ BOOL packVector4(const LLVector4 &value, const char *name);
+ /*virtual*/ BOOL unpackVector4(LLVector4 &value, const char *name);
+
+ /*virtual*/ BOOL packUUID(const LLUUID &value, const char *name);
+ /*virtual*/ BOOL unpackUUID(LLUUID &value, const char *name);
+
+ S32 getCurrentSize() const { return (S32)(mCurBufferp - mBufferp); }
+ S32 getBufferSize() const { return mBufferSize; }
+ void reset() { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }
+ void freeBuffer() { delete [] mBufferp; mBufferp = mCurBufferp = NULL; mBufferSize = 0; mWriteEnabled = FALSE; }
+ void assignBuffer(U8 *bufferp, S32 size)
+ {
+ mBufferp = bufferp;
+ mCurBufferp = bufferp;
+ mBufferSize = size;
+ mWriteEnabled = TRUE;
+ }
+ const LLDataPackerBinaryBuffer& operator=(const LLDataPackerBinaryBuffer &a);
+
+ /*virtual*/ BOOL hasNext() const { return getCurrentSize() < getBufferSize(); }
+
+ /*virtual*/ void dumpBufferToLog();
+protected:
+ inline BOOL verifyLength(const S32 data_size, const char *name);
+
+ U8 *mBufferp;
+ U8 *mCurBufferp;
+ S32 mBufferSize;
+};
+
+inline BOOL LLDataPackerBinaryBuffer::verifyLength(const S32 data_size, const char *name)
+{
+ if (mWriteEnabled && (mCurBufferp - mBufferp) > mBufferSize - data_size)
+ {
+ llwarns << "Buffer overflow in BinaryBuffer length verify, field name " << name << "!" << llendl;
+ llwarns << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLDataPackerAsciiBuffer : public LLDataPacker
+{
+public:
+ LLDataPackerAsciiBuffer(char* bufferp, S32 size)
+ {
+ mBufferp = bufferp;
+ mCurBufferp = bufferp;
+ mBufferSize = size;
+ mPassFlags = 0;
+ mIncludeNames = FALSE;
+ mWriteEnabled = TRUE;
+ }
+
+ LLDataPackerAsciiBuffer()
+ {
+ mBufferp = NULL;
+ mCurBufferp = NULL;
+ mBufferSize = 0;
+ mPassFlags = 0;
+ mIncludeNames = FALSE;
+ mWriteEnabled = FALSE;
+ }
+
+ /*virtual*/ BOOL packString(const char *value, const char *name);
+ /*virtual*/ BOOL unpackString(char *value, const char *name);
+
+ /*virtual*/ BOOL packBinaryData(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryData(U8 *value, S32 &size, const char *name);
+
+ // Constant size binary data packing
+ /*virtual*/ BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name);
+
+ /*virtual*/ BOOL packU8(const U8 value, const char *name);
+ /*virtual*/ BOOL unpackU8(U8 &value, const char *name);
+
+ /*virtual*/ BOOL packU16(const U16 value, const char *name);
+ /*virtual*/ BOOL unpackU16(U16 &value, const char *name);
+
+ /*virtual*/ BOOL packU32(const U32 value, const char *name);
+ /*virtual*/ BOOL unpackU32(U32 &value, const char *name);
+
+ /*virtual*/ BOOL packS32(const S32 value, const char *name);
+ /*virtual*/ BOOL unpackS32(S32 &value, const char *name);
+
+ /*virtual*/ BOOL packF32(const F32 value, const char *name);
+ /*virtual*/ BOOL unpackF32(F32 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4(const LLColor4 &value, const char *name);
+ /*virtual*/ BOOL unpackColor4(LLColor4 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4U(const LLColor4U &value, const char *name);
+ /*virtual*/ BOOL unpackColor4U(LLColor4U &value, const char *name);
+
+ /*virtual*/ BOOL packVector2(const LLVector2 &value, const char *name);
+ /*virtual*/ BOOL unpackVector2(LLVector2 &value, const char *name);
+
+ /*virtual*/ BOOL packVector3(const LLVector3 &value, const char *name);
+ /*virtual*/ BOOL unpackVector3(LLVector3 &value, const char *name);
+
+ /*virtual*/ BOOL packVector4(const LLVector4 &value, const char *name);
+ /*virtual*/ BOOL unpackVector4(LLVector4 &value, const char *name);
+
+ /*virtual*/ BOOL packUUID(const LLUUID &value, const char *name);
+ /*virtual*/ BOOL unpackUUID(LLUUID &value, const char *name);
+
+ void setIncludeNames(BOOL b) { mIncludeNames = b; }
+
+ // Include the trailing NULL so it's always a valid string
+ S32 getCurrentSize() const { return (S32)(mCurBufferp - mBufferp) + 1; }
+
+ S32 getBufferSize() const { return mBufferSize; }
+ /*virtual*/ void reset() { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }
+
+ /*virtual*/ BOOL hasNext() const { return getCurrentSize() < getBufferSize(); }
+
+ inline void freeBuffer();
+ inline void assignBuffer(char* bufferp, S32 size);
+ void dump();
+
+protected:
+ void writeIndentedName(const char *name);
+ BOOL getValueStr(const char *name, char *out_value, const S32 value_len);
+
+protected:
+ inline BOOL verifyLength(const S32 data_size, const char *name);
+
+ char *mBufferp;
+ char *mCurBufferp;
+ S32 mBufferSize;
+ BOOL mIncludeNames; // useful for debugging, print the name of each field
+};
+
+inline void LLDataPackerAsciiBuffer::freeBuffer()
+{
+ delete [] mBufferp;
+ mBufferp = mCurBufferp = NULL;
+ mBufferSize = 0;
+ mWriteEnabled = FALSE;
+}
+
+inline void LLDataPackerAsciiBuffer::assignBuffer(char* bufferp, S32 size)
+{
+ mBufferp = bufferp;
+ mCurBufferp = bufferp;
+ mBufferSize = size;
+ mWriteEnabled = TRUE;
+}
+
+inline BOOL LLDataPackerAsciiBuffer::verifyLength(const S32 data_size, const char *name)
+{
+ if (mWriteEnabled && (mCurBufferp - mBufferp) > mBufferSize - data_size)
+ {
+ llwarns << "Buffer overflow in AsciiBuffer length verify, field name " << name << "!" << llendl;
+ llwarns << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLDataPackerAsciiFile : public LLDataPacker
+{
+public:
+ LLDataPackerAsciiFile(FILE *fp, const S32 indent = 2)
+ : LLDataPacker(),
+ mIndent(indent),
+ mFP(fp),
+ mOutputStream(NULL),
+ mInputStream(NULL)
+ {
+ }
+
+ LLDataPackerAsciiFile(std::ostream& output_stream, const S32 indent = 2)
+ : LLDataPacker(),
+ mIndent(indent),
+ mFP(NULL),
+ mOutputStream(&output_stream),
+ mInputStream(NULL)
+ {
+ mWriteEnabled = TRUE;
+ }
+
+ LLDataPackerAsciiFile(std::istream& input_stream, const S32 indent = 2)
+ : LLDataPacker(),
+ mIndent(indent),
+ mFP(NULL),
+ mOutputStream(NULL),
+ mInputStream(&input_stream)
+ {
+ }
+
+ /*virtual*/ BOOL packString(const char *value, const char *name);
+ /*virtual*/ BOOL unpackString(char *value, const char *name);
+
+ /*virtual*/ BOOL packBinaryData(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryData(U8 *value, S32 &size, const char *name);
+
+ /*virtual*/ BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name);
+
+ /*virtual*/ BOOL packU8(const U8 value, const char *name);
+ /*virtual*/ BOOL unpackU8(U8 &value, const char *name);
+
+ /*virtual*/ BOOL packU16(const U16 value, const char *name);
+ /*virtual*/ BOOL unpackU16(U16 &value, const char *name);
+
+ /*virtual*/ BOOL packU32(const U32 value, const char *name);
+ /*virtual*/ BOOL unpackU32(U32 &value, const char *name);
+
+ /*virtual*/ BOOL packS32(const S32 value, const char *name);
+ /*virtual*/ BOOL unpackS32(S32 &value, const char *name);
+
+ /*virtual*/ BOOL packF32(const F32 value, const char *name);
+ /*virtual*/ BOOL unpackF32(F32 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4(const LLColor4 &value, const char *name);
+ /*virtual*/ BOOL unpackColor4(LLColor4 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4U(const LLColor4U &value, const char *name);
+ /*virtual*/ BOOL unpackColor4U(LLColor4U &value, const char *name);
+
+ /*virtual*/ BOOL packVector2(const LLVector2 &value, const char *name);
+ /*virtual*/ BOOL unpackVector2(LLVector2 &value, const char *name);
+
+ /*virtual*/ BOOL packVector3(const LLVector3 &value, const char *name);
+ /*virtual*/ BOOL unpackVector3(LLVector3 &value, const char *name);
+
+ /*virtual*/ BOOL packVector4(const LLVector4 &value, const char *name);
+ /*virtual*/ BOOL unpackVector4(LLVector4 &value, const char *name);
+
+ /*virtual*/ BOOL packUUID(const LLUUID &value, const char *name);
+ /*virtual*/ BOOL unpackUUID(LLUUID &value, const char *name);
+protected:
+ void writeIndentedName(const char *name);
+ BOOL getValueStr(const char *name, char *out_value, const S32 value_len);
+
+ /*virtual*/ BOOL hasNext() const { return true; }
+
+protected:
+ S32 mIndent;
+ FILE *mFP;
+ std::ostream* mOutputStream;
+ std::istream* mInputStream;
+};
+
+#endif // LL_LLDATAPACKER
+
diff --git a/indra/llmessage/lldbstrings.h b/indra/llmessage/lldbstrings.h
new file mode 100644
index 0000000000..73aca880ef
--- /dev/null
+++ b/indra/llmessage/lldbstrings.h
@@ -0,0 +1,208 @@
+/**
+ * @file lldbstrings.h
+ * @brief Database String Lengths.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDBSTRINGS_H
+#define LL_LLDBSTRINGS_H
+
+/**
+ * Defines the length of strings that are stored in the database (and
+ * the size of the buffer large enough to hold each one)
+ */
+
+// asset.name varchar(63)
+// -also-
+// user_inventory_item.name varchar(63)
+// -also-
+// user_inventory_folder.name varchar(63) was CAT_NAME_SIZE
+// Must be >= DB_FULL_NAME_STR_LEN so that calling cards work
+const S32 DB_INV_ITEM_NAME_STR_LEN = 63; // was MAX_ASSET_NAME_LENGTH
+const S32 DB_INV_ITEM_NAME_BUF_SIZE = 64; // was ITEM_NAME_SIZE
+
+// asset.description varchar(127)
+// -also-
+// user_inventory_item.description varchar(127)
+const S32 DB_INV_ITEM_DESC_STR_LEN = 127; // was MAX_ASSET_DESCRIPTION_LENGTH
+const S32 DB_INV_ITEM_DESC_BUF_SIZE = 128; // was ITEM_DESC_SIZE
+
+// groups.name varchar(35)
+const S32 DB_GROUP_NAME_STR_LEN = 35;
+const S32 DB_GROUP_NAME_BUF_SIZE = 36;
+const S32 DB_GROUP_NAME_MIN_LEN = 4;
+
+//group_roles.name
+const U32 DB_GROUP_ROLE_NAME_STR_LEN = 20;
+const U32 DB_GROUP_ROLE_NAME_BUF_SIZE = DB_GROUP_ROLE_NAME_STR_LEN + 1;
+
+//group_roles.title
+const U32 DB_GROUP_ROLE_TITLE_STR_LEN = 20;
+const U32 DB_GROUP_ROLE_TITLE_BUF_SIZE = DB_GROUP_ROLE_TITLE_STR_LEN + 1;
+
+
+// group.charter text
+const S32 DB_GROUP_CHARTER_STR_LEN = 511;
+const S32 DB_GROUP_CHARTER_BUF_SIZE = 512;
+
+// group.officer_title varchar(20)
+// -also-
+// group.member_title varchar(20)
+const S32 DB_GROUP_TITLE_STR_LEN = 20;
+const S32 DB_GROUP_TITLE_BUF_SIZE = 21;
+
+// Since chat and im both dump into the database text message log,
+// they derive their max size from the same constant.
+const S32 MAX_MSG_STR_LEN = 1023;
+const S32 MAX_MSG_BUF_SIZE = 1024;
+
+// instant_message.message text
+const S32 DB_IM_MSG_STR_LEN = MAX_MSG_STR_LEN;
+const S32 DB_IM_MSG_BUF_SIZE = MAX_MSG_BUF_SIZE;
+
+// groupnotices
+const S32 DB_GROUP_NOTICE_SUBJ_STR_LEN = 63;
+const S32 DB_GROUP_NOTICE_SUBJ_STR_SIZE = 64;
+const S32 DB_GROUP_NOTICE_MSG_STR_LEN = MAX_MSG_STR_LEN - DB_GROUP_NOTICE_SUBJ_STR_LEN;
+const S32 DB_GROUP_NOTICE_MSG_STR_SIZE = MAX_MSG_BUF_SIZE - DB_GROUP_NOTICE_SUBJ_STR_SIZE;
+
+// log_text_message.message text
+const S32 DB_CHAT_MSG_STR_LEN = MAX_MSG_STR_LEN;
+const S32 DB_CHAT_MSG_BUF_SIZE = MAX_MSG_BUF_SIZE;
+
+// money_stipend.description varchar(254)
+const S32 DB_STIPEND_DESC_STR_LEN = 254;
+const S32 DB_STIPEND_DESC_BUF_SIZE = 255;
+
+// script_email_message.from_email varchar(78)
+const S32 DB_EMAIL_FROM_STR_LEN = 78;
+const S32 DB_EMAIL_FROM_BUF_SIZE = 79;
+
+// script_email_message.subject varchar(72)
+const S32 DB_EMAIL_SUBJECT_STR_LEN = 72;
+const S32 DB_EMAIL_SUBJECT_BUF_SIZE = 73;
+
+// system_globals.motd varchar(254)
+const S32 DB_MOTD_STR_LEN = 254;
+const S32 DB_MOTD_BUF_SIZE = 255;
+
+// Must be <= user_inventory_item.name so that calling cards work
+// First name + " " + last name...or a system assigned "from" name
+// instant_message.from_agent_name varchar(63)
+// -also-
+// user_mute.mute_agent_name varchar(63)
+const S32 DB_FULL_NAME_STR_LEN = 63;
+const S32 DB_FULL_NAME_BUF_SIZE = 64; // was USER_NAME_SIZE
+
+// user.username varchar(31)
+const S32 DB_FIRST_NAME_STR_LEN = 31;
+const S32 DB_FIRST_NAME_BUF_SIZE = 32; // was MAX_FIRST_NAME
+
+// user_last_name.name varchar(31)
+const S32 DB_LAST_NAME_STR_LEN = 31;
+const S32 DB_LAST_NAME_BUF_SIZE = 32; // was MAX_LAST_NAME
+
+// user.password varchar(100)
+const S32 DB_USER_PASSWORD_STR_LEN = 100;
+const S32 DB_USER_PASSWORD_BUF_SIZE = 101; // was MAX_PASSWORD
+
+// user.email varchar(254)
+const S32 DB_USER_EMAIL_ADDR_STR_LEN = 254;
+const S32 DB_USER_EMAIL_ADDR_BUF_SIZE = 255;
+
+// user.about text
+const S32 DB_USER_ABOUT_STR_LEN = 511;
+const S32 DB_USER_ABOUT_BUF_SIZE = 512;
+
+// user.fl_about_text text
+// Must be 255 not 256 as gets packed into message Variable 1
+const S32 DB_USER_FL_ABOUT_STR_LEN = 254;
+const S32 DB_USER_FL_ABOUT_BUF_SIZE = 255;
+
+// user.profile_url text
+// Must be 255 not 256 as gets packed into message Variable 1
+const S32 DB_USER_PROFILE_URL_STR_LEN = 254;
+const S32 DB_USER_PROFILE_URL_BUF_SIZE = 255;
+
+// user.want_to varchar(254)
+const S32 DB_USER_WANT_TO_STR_LEN = 254;
+const S32 DB_USER_WANT_TO_BUF_SIZE = 255;
+
+// user.skills varchar(254)
+const S32 DB_USER_SKILLS_STR_LEN = 254;
+const S32 DB_USER_SKILLS_BUF_SIZE = 255;
+
+// user_nv.name varchar(128)
+const S32 DB_NV_NAME_STR_LEN = 128;
+const S32 DB_NV_NAME_BUF_SIZE = 129;
+
+// votes.vote_text varchar(254)
+const S32 DB_VOTE_TEXT_STR_LEN = 254;
+const S32 DB_VOTE_TEXT_BUF_SIZE = 255;
+
+// vpte type text varchar(9)
+const S32 DB_VOTE_TYPE_STR_LEN = 9;
+const S32 DB_VOTE_TYPE_BUF_SIZE = 10;
+
+// vote result text
+const S32 DB_VOTE_RESULT_BUF_LEN = 8;
+const S32 DB_VOTE_RESULT_BUF_SIZE = 9;
+
+// user_start_location.location_name varchar(254)
+const S32 DB_START_LOCATION_STR_LEN = 254;
+const S32 DB_START_LOCATION_BUF_SIZE = 255;
+
+// money_tax_assessment.sim varchar(100)
+//const S32 DB_SIM_NAME_STR_LEN = 100;
+//const S32 DB_SIM_NAME_BUF_SIZE = 101;
+
+// born on date date
+const S32 DB_BORN_STR_LEN = 15;
+const S32 DB_BORN_BUF_SIZE = 16;
+
+// place.name
+const S32 DB_PLACE_NAME_LEN = 63;
+const S32 DB_PLACE_NAME_SIZE = 64;
+const S32 DB_PARCEL_NAME_LEN = 63;
+const S32 DB_PARCEL_NAME_SIZE = 64;
+
+// place.desc
+const S32 DB_PLACE_DESC_LEN = 255;
+const S32 DB_PLACE_DESC_SIZE = 256;
+const S32 DB_PARCEL_DESC_LEN = 255;
+const S32 DB_PARCEL_DESC_SIZE = 256;
+const S32 DB_PARCEL_MUSIC_URL_LEN = 255;
+const S32 DB_PARCEL_MEDIA_URL_LEN = 255;
+const S32 DB_PARCEL_MUSIC_URL_SIZE = 256;
+
+// date time that is easily human readable
+const S32 DB_DATETIME_STR_LEN = 35;
+const S32 DB_DATETIME_BUF_SIZE = 36;
+
+// date time that isn't easily human readable
+const S32 DB_TERSE_DATETIME_STR_LEN = 15;
+const S32 DB_TERSE_DATETIME_BUF_SIZE = 16;
+
+// indra.simulator constants
+const S32 DB_SIM_NAME_STR_LEN = 35;
+const S32 DB_SIM_NAME_BUF_SIZE = 36;
+const S32 DB_HOST_NAME_STR_LEN = 100;
+const S32 DB_HOST_NAME_BUF_SIZE = 101;
+const S32 DB_ESTATE_NAME_STR_LEN = 63;
+const S32 DB_ESTATE_NAME_BUF_SIZE = DB_ESTATE_NAME_STR_LEN + 1;
+
+// user_note.note
+const S32 DB_USER_NOTE_LEN = 1023;
+const S32 DB_USER_NOTE_SIZE = 1024;
+
+// pick.name
+const S32 DB_PICK_NAME_LEN = 63;
+const S32 DB_PICK_NAME_SIZE = 64;
+
+// pick.desc
+const S32 DB_PICK_DESC_LEN = 1023;
+const S32 DB_PICK_DESC_SIZE = 1024;
+
+#endif // LL_LLDBSTRINGS_H
diff --git a/indra/llmessage/lldispatcher.cpp b/indra/llmessage/lldispatcher.cpp
new file mode 100644
index 0000000000..8ba051765e
--- /dev/null
+++ b/indra/llmessage/lldispatcher.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file lldispatcher.cpp
+ * @brief Implementation of the dispatcher object.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lldispatcher.h"
+
+#include <algorithm>
+#include "llstl.h"
+#include "message.h"
+
+///----------------------------------------------------------------------------
+/// Class lldispatcher
+///----------------------------------------------------------------------------
+
+
+LLDispatcher::LLDispatcher()
+{
+}
+
+LLDispatcher::~LLDispatcher()
+{
+}
+
+bool LLDispatcher::isHandlerPresent(const key_t& name) const
+{
+ if(mHandlers.find(name) != mHandlers.end())
+ {
+ return true;
+ }
+ return false;
+}
+
+void LLDispatcher::copyAllHandlerNames(keys_t& names) const
+{
+ // copy the names onto the vector we are given
+ std::transform(
+ mHandlers.begin(),
+ mHandlers.end(),
+ std::back_insert_iterator<keys_t>(names),
+ llselect1st<dispatch_map_t::value_type>());
+}
+
+bool LLDispatcher::dispatch(
+ const key_t& name,
+ const LLUUID& invoice,
+ const sparam_t& strings) const
+{
+ dispatch_map_t::const_iterator it = mHandlers.find(name);
+ if(it != mHandlers.end())
+ {
+ LLDispatchHandler* func = (*it).second;
+ return (*func)(this, name, invoice, strings);
+ }
+ return false;
+}
+
+LLDispatchHandler* LLDispatcher::addHandler(
+ const key_t& name, LLDispatchHandler* func)
+{
+ dispatch_map_t::iterator it = mHandlers.find(name);
+ LLDispatchHandler* old_handler = NULL;
+ if(it != mHandlers.end())
+ {
+ old_handler = (*it).second;
+ mHandlers.erase(it);
+ }
+ if(func)
+ {
+ // only non-null handlers so that we don't have to worry about
+ // it later.
+ mHandlers.insert(dispatch_map_t::value_type(name, func));
+ }
+ return old_handler;
+}
+
+// static
+bool LLDispatcher::unpackMessage(
+ LLMessageSystem* msg,
+ LLDispatcher::key_t& method,
+ LLUUID& invoice,
+ LLDispatcher::sparam_t& parameters)
+{
+ char buf[MAX_STRING]; /*Flawfinder: ignore*/
+ msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, MAX_STRING, buf);
+ method.assign(buf);
+ msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice);
+ S32 size;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList);
+ for (S32 i = 0; i < count; ++i)
+ {
+ // we treat the SParam as binary data (since it might be an
+ // LLUUID in compressed form which may have embedded \0's,)
+ size = msg->getSizeFast(_PREHASH_ParamList, i, _PREHASH_Parameter);
+ msg->getBinaryDataFast(
+ _PREHASH_ParamList, _PREHASH_Parameter,
+ buf, size, i, MAX_STRING-1);
+
+ // If the last byte of the data is 0x0, this is either a normally
+ // packed string, or a binary packed UUID (which for these messages
+ // are packed with a 17th byte 0x0). Unpack into a std::string
+ // without the trailing \0, so "abc\0" becomes std::string("abc", 3)
+ // which matches const char* "abc".
+ if (size > 0
+ && buf[size-1] == 0x0)
+ {
+ // special char*/size constructor because UUIDs may have embedded
+ // 0x0 bytes.
+ std::string binary_data(buf, size-1);
+ parameters.push_back(binary_data);
+ }
+ else
+ {
+ // This is either a NULL string, or a string that was packed
+ // incorrectly as binary data, without the usual trailing '\0'.
+ std::string string_data(buf, size);
+ parameters.push_back(string_data);
+ }
+ }
+ return true;
+}
diff --git a/indra/llmessage/lldispatcher.h b/indra/llmessage/lldispatcher.h
new file mode 100644
index 0000000000..e0eb706be1
--- /dev/null
+++ b/indra/llmessage/lldispatcher.h
@@ -0,0 +1,95 @@
+/**
+ * @file lldispatcher.h
+ * @brief LLDispatcher class header file.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDISPATCHER_H
+#define LL_LLDISPATCHER_H
+
+#include <map>
+#include <vector>
+#include <string>
+
+class LLDispatcher;
+class LLMessageSystem;
+class LLUUID;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDispatchHandler
+//
+// Abstract base class for handling dispatches. Derive your own
+// classes, construct them, and add them to the dispatcher you want to
+// use.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLDispatchHandler
+{
+public:
+ typedef std::vector<std::string> sparam_t;
+ //typedef std::vector<S32> iparam_t;
+ LLDispatchHandler() {}
+ virtual ~LLDispatchHandler() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& string) = 0;
+ //const iparam_t& integers) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDispatcher
+//
+// Basic utility class that handles dispatching keyed operations to
+// function objects implemented as LLDispatchHandler derivations.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLDispatcher
+{
+public:
+ typedef std::string key_t;
+ typedef std::vector<std::string> keys_t;
+ typedef std::vector<std::string> sparam_t;
+ //typedef std::vector<S32> iparam_t;
+
+ // construct a dispatcher.
+ LLDispatcher();
+ virtual ~LLDispatcher();
+
+ // Returns if they keyed handler exists in this dispatcher.
+ bool isHandlerPresent(const key_t& name) const;
+
+ // copy all known keys onto keys_t structure
+ void copyAllHandlerNames(keys_t& names) const;
+
+ // Call this method with the name of the request that has come
+ // in. If the handler is present, it is called with the params and
+ // returns the return value from
+ bool dispatch(
+ const key_t& name,
+ const LLUUID& invoice,
+ const sparam_t& strings) const;
+ //const iparam_t& itegers) const;
+
+ // Add a handler. If one with the same key already exists, its
+ // pointer is returned, otherwise returns NULL. This object does
+ // not do memory management of the LLDispatchHandler, and relies
+ // on the caller to delete the object if necessary.
+ LLDispatchHandler* addHandler(const key_t& name, LLDispatchHandler* func);
+
+ // Helper method to unpack the dispatcher message bus
+ // format. Returns true on success.
+ static bool unpackMessage(
+ LLMessageSystem* msg,
+ key_t& method,
+ LLUUID& invoice,
+ sparam_t& parameters);
+
+protected:
+ typedef std::map<key_t, LLDispatchHandler*> dispatch_map_t;
+ dispatch_map_t mHandlers;
+};
+
+#endif // LL_LLDISPATCHER_H
diff --git a/indra/llmessage/lleventflags.h b/indra/llmessage/lleventflags.h
new file mode 100644
index 0000000000..90120b2648
--- /dev/null
+++ b/indra/llmessage/lleventflags.h
@@ -0,0 +1,17 @@
+/**
+ * @file lleventflags.h
+ * @brief Flags for events.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTFLAGS_H
+#define LL_LLEVENTFLAGS_H
+
+const U32 EVENT_FLAG_NONE = 0x0000;
+
+// set for mature events
+const U32 EVENT_FLAG_MATURE = 0x0001;
+
+#endif
diff --git a/indra/llmessage/llfiltersd2xmlrpc.cpp b/indra/llmessage/llfiltersd2xmlrpc.cpp
new file mode 100644
index 0000000000..6d5f92e983
--- /dev/null
+++ b/indra/llmessage/llfiltersd2xmlrpc.cpp
@@ -0,0 +1,728 @@
+/**
+ * @file llfiltersd2xmlrpc.cpp
+ * @author Phoenix
+ * @date 2005-04-26
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * xml rpc request:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodCall><methodName>examples.getStateName</methodName>
+ * <params><param><value><i4>41</i4></value></param></params>
+ * </methodCall>
+ * </code>
+ *
+ * xml rpc response:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodResponse>
+ * <params><param><value><string>South Dakota</string></value></param></params>
+ * </methodResponse>
+ * </code>
+ *
+ * xml rpc fault:
+ * </code>
+ * <?xml version="1.0"?>
+ * <methodResponse>
+ * <fault><value><struct>
+ * <member><name>faultCode</name><value><int>4</int></value></member>
+ * <member><name>faultString</name><value><string>...</string></value></member>
+ * </struct></value></fault>
+ * </methodResponse>
+ * </code>
+ *
+ * llsd rpc request:
+ * <code>
+ * { 'method':'...', 'parameter':...]}
+ * </code>
+ *
+ * llsd rpc response:
+ * <code>
+ * { 'response':... }
+ * </code>
+ *
+ * llsd rpc fault:
+ * <code>
+ * { 'fault': {'code':i..., 'description':'...'} }
+ * </code>
+ *
+ */
+
+#include "linden_common.h"
+#include "llfiltersd2xmlrpc.h"
+
+#include <sstream>
+#include <iterator>
+#include <xmlrpc-epi/xmlrpc.h>
+#include "apr-1/apr_base64.h"
+
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llmemorystream.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "lluuid.h"
+
+// spammy mode
+//#define LL_SPEW_STREAM_OUT_DEBUGGING 1
+
+/**
+ * String constants
+ */
+static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+static const char XMLRPC_REQUEST_HEADER_1[] = "<methodCall><methodName>";
+static const char XMLRPC_REQUEST_HEADER_2[] = "</methodName><params>";
+static const char XMLRPC_REQUEST_FOOTER[] = "</params></methodCall>";
+static const char XMLRPC_METHOD_RESPONSE_HEADER[] = "<methodResponse>";
+static const char XMLRPC_METHOD_RESPONSE_FOOTER[] = "</methodResponse>";
+static const char XMLRPC_RESPONSE_HEADER[] = "<params><param>";
+static const char XMLRPC_RESPONSE_FOOTER[] = "</param></params>";
+static const char XMLRPC_FAULT_1[] = "<fault><value><struct><member><name>faultCode</name><value><int>";
+static const char XMLRPC_FAULT_2[] = "</int></value></member><member><name>faultString</name><value><string>";
+static const char XMLRPC_FAULT_3[] = "</string></value></member></struct></value></fault>";
+static const char LLSDRPC_RESPONSE_HEADER[] = "{'response':";
+static const char LLSDRPC_RESPONSE_FOOTER[] = "}";
+const char LLSDRPC_REQUEST_HEADER_1[] = "{'method':'";
+const char LLSDRPC_REQUEST_HEADER_2[] = "', 'parameter': ";
+const char LLSDRPC_REQUEST_FOOTER[] = "}";
+static const char LLSDRPC_FAULT_HADER_1[] = "{ 'fault': {'code':i";
+static const char LLSDRPC_FAULT_HADER_2[] = ", 'description':";
+static const char LLSDRPC_FAULT_FOOTER[] = "} }";
+static const S32 DEFAULT_PRECISION = 20;
+
+/**
+ * LLFilterSD2XMLRPC
+ */
+LLFilterSD2XMLRPC::LLFilterSD2XMLRPC()
+{
+}
+
+LLFilterSD2XMLRPC::~LLFilterSD2XMLRPC()
+{
+}
+
+std::string xml_escape_string(const std::string& in)
+{
+ std::ostringstream out;
+ std::string::const_iterator it = in.begin();
+ std::string::const_iterator end = in.end();
+ for(; it != end; ++it)
+ {
+ switch((*it))
+ {
+ case '<':
+ out << "&lt;";
+ break;
+ case '>':
+ out << "&gt;";
+ break;
+ case '&':
+ out << "&amp;";
+ break;
+ case '\'':
+ out << "&apos;";
+ break;
+ case '"':
+ out << "&quot;";
+ break;
+ default:
+ out << (*it);
+ break;
+ }
+ }
+ return out.str();
+}
+
+void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
+{
+ ostr << "<value>";
+ switch(sd.type())
+ {
+ case LLSD::TypeMap:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(map) BEGIN" << llendl;
+#endif
+ ostr << "<struct>";
+ if(ostr.fail())
+ {
+ llinfos << "STREAM FAILURE writing struct" << llendl;
+ }
+ LLSD::map_const_iterator it = sd.beginMap();
+ LLSD::map_const_iterator end = sd.endMap();
+ for(; it != end; ++it)
+ {
+ ostr << "<member><name>" << xml_escape_string((*it).first)
+ << "</name>";
+ streamOut(ostr, (*it).second);
+ if(ostr.fail())
+ {
+ llinfos << "STREAM FAILURE writing '" << (*it).first
+ << "' with sd type " << (*it).second.type() << llendl;
+ }
+ ostr << "</member>";
+ }
+ ostr << "</struct>";
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(map) END" << llendl;
+#endif
+ break;
+ }
+ case LLSD::TypeArray:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(array) BEGIN" << llendl;
+#endif
+ ostr << "<array><data>";
+ LLSD::array_const_iterator it = sd.beginArray();
+ LLSD::array_const_iterator end = sd.endArray();
+ for(; it != end; ++it)
+ {
+ streamOut(ostr, *it);
+ if(ostr.fail())
+ {
+ llinfos << "STREAM FAILURE writing array element sd type "
+ << (*it).type() << llendl;
+ }
+ }
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(array) END" << llendl;
+#endif
+ ostr << "</data></array>";
+ break;
+ }
+ case LLSD::TypeUndefined:
+ // treat undefined as a bool with a false value.
+ case LLSD::TypeBoolean:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(bool)" << llendl;
+#endif
+ ostr << "<boolean>" << (sd.asBoolean() ? "1" : "0") << "</boolean>";
+ break;
+ case LLSD::TypeInteger:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(int)" << llendl;
+#endif
+ ostr << "<i4>" << sd.asInteger() << "</i4>";
+ break;
+ case LLSD::TypeReal:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(real)" << llendl;
+#endif
+ ostr << "<double>" << sd.asReal() << "</double>";
+ break;
+ case LLSD::TypeString:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(string)" << llendl;
+#endif
+ ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
+ break;
+ case LLSD::TypeUUID:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(uuid)" << llendl;
+#endif
+ // serialize it as a string
+ ostr << "<string>" << sd.asString() << "</string>";
+ break;
+ case LLSD::TypeURI:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(uri)" << llendl;
+#endif
+ // serialize it as a string
+ ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
+ break;
+ }
+ case LLSD::TypeBinary:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(binary)" << llendl;
+#endif
+ // this is pretty inefficient, but we'll deal with that
+ // problem when it becomes one.
+ ostr << "<base64>";
+ LLSD::Binary buffer = sd.asBinary();
+ if(!buffer.empty())
+ {
+ int b64_buffer_length = apr_base64_encode_len(buffer.size());
+ char* b64_buffer = new char[b64_buffer_length];
+ b64_buffer_length = apr_base64_encode_binary(
+ b64_buffer,
+ &buffer[0],
+ buffer.size());
+ ostr.write(b64_buffer, b64_buffer_length - 1);
+ delete[] b64_buffer;
+ }
+ ostr << "</base64>";
+ break;
+ }
+ case LLSD::TypeDate:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(date)" << llendl;
+#endif
+ // no need to escape this since it will be alpha-numeric.
+ ostr << "<dateTime.iso8601>" << sd.asString() << "</dateTime.iso8601>";
+ break;
+ default:
+ // unhandled type
+ llwarns << "Unhandled structured data type: " << sd.type()
+ << llendl;
+ break;
+ }
+ ostr << "</value>";
+}
+
+/**
+ * LLFilterSD2XMLRPCResponse
+ */
+
+LLFilterSD2XMLRPCResponse::LLFilterSD2XMLRPCResponse()
+{
+}
+
+LLFilterSD2XMLRPCResponse::~LLFilterSD2XMLRPCResponse()
+{
+}
+
+
+// virtual
+LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ // This pipe does not work if it does not have everyting. This
+ // could be addressed by making a stream parser for llsd which
+ // handled partial information.
+ if(!eos)
+ {
+ return STATUS_BREAK;
+ }
+
+ PUMP_DEBUG;
+ // we have everyting in the buffer, so turn the structure data rpc
+ // response into an xml rpc response.
+ LLBufferStream stream(channels, buffer.get());
+ stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, stream);
+
+ PUMP_DEBUG;
+ LLIOPipe::EStatus rv = STATUS_ERROR;
+ if(sd.has("response"))
+ {
+ PUMP_DEBUG;
+ // it is a normal response. pack it up and ship it out.
+ stream.precision(DEFAULT_PRECISION);
+ stream << XMLRPC_RESPONSE_HEADER;
+ streamOut(stream, sd["response"]);
+ stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER;
+ rv = STATUS_DONE;
+ }
+ else if(sd.has("fault"))
+ {
+ PUMP_DEBUG;
+ // it is a fault.
+ stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger()
+ << XMLRPC_FAULT_2
+ << xml_escape_string(sd["fault"]["description"].asString())
+ << XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER;
+ rv = STATUS_DONE;
+ }
+ else
+ {
+ llwarns << "Unable to determine the type of LLSD response." << llendl;
+ }
+ PUMP_DEBUG;
+ return rv;
+}
+
+/**
+ * LLFilterSD2XMLRPCRequest
+ */
+LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest()
+{
+}
+
+LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest(const char* method)
+{
+ if(method)
+ {
+ mMethod.assign(method);
+ }
+}
+
+LLFilterSD2XMLRPCRequest::~LLFilterSD2XMLRPCRequest()
+{
+}
+
+// virtual
+LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ // This pipe does not work if it does not have everyting. This
+ // could be addressed by making a stream parser for llsd which
+ // handled partial information.
+ PUMP_DEBUG;
+ if(!eos)
+ {
+ llinfos << "!eos" << llendl;
+ return STATUS_BREAK;
+ }
+
+ // See if we can parse it
+ LLBufferStream stream(channels, buffer.get());
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, stream);
+ if(stream.fail())
+ {
+ llinfos << "STREAM FAILURE reading structure data." << llendl;
+ }
+
+ PUMP_DEBUG;
+ // We can get the method and parameters from either the member
+ // function or passed in via the buffer. We prefer the buffer if
+ // we found a parameter and a method, or fall back to using
+ // mMethod and putting everyting in the buffer into the parameter.
+ std::string method;
+ LLSD param_sd;
+ if(sd.has("method") && sd.has("parameter"))
+ {
+ method = sd["method"].asString();
+ param_sd = sd["parameter"];
+ }
+ else
+ {
+ method = mMethod;
+ param_sd = sd;
+ }
+ if(method.empty())
+ {
+ llwarns << "SD -> XML Request no method found." << llendl;
+ return STATUS_ERROR;
+ }
+
+ PUMP_DEBUG;
+ // We have a method, and some kind of parameter, so package it up
+ // and send it out.
+ LLBufferStream ostream(channels, buffer.get());
+ ostream.precision(DEFAULT_PRECISION);
+ if(ostream.fail())
+ {
+ llinfos << "STREAM FAILURE setting precision" << llendl;
+ }
+ ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1
+ << xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2;
+ if(ostream.fail())
+ {
+ llinfos << "STREAM FAILURE writing method headers" << llendl;
+ }
+ switch(param_sd.type())
+ {
+ case LLSD::TypeMap:
+ // If the params are a map, then we do not want to iterate
+ // through them since the iterators returned will be map
+ // ordered un-named values, which will lose the names, and
+ // only stream the values, turning it into an array.
+ ostream << "<param>";
+ streamOut(ostream, param_sd);
+ ostream << "</param>";
+ break;
+ case LLSD::TypeArray:
+ {
+
+ LLSD::array_iterator it = param_sd.beginArray();
+ LLSD::array_iterator end = param_sd.endArray();
+ for(; it != end; ++it)
+ {
+ ostream << "<param>";
+ streamOut(ostream, *it);
+ ostream << "</param>";
+ }
+ break;
+ }
+ default:
+ ostream << "<param>";
+ streamOut(ostream, param_sd);
+ ostream << "</param>";
+ break;
+ }
+
+ stream << XMLRPC_REQUEST_FOOTER;
+ return STATUS_DONE;
+}
+
+/**
+ * LLFilterXMLRPCResponse2LLSD
+ */
+// this is a c function here since it's really an implementation
+// detail that requires a header file just get the definition of the
+// parameters.
+LLIOPipe::EStatus stream_out(std::ostream& ostr, XMLRPC_VALUE value)
+{
+ XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(value);
+ LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
+ switch(type)
+ {
+ case xmlrpc_type_base64:
+ {
+ S32 len = XMLRPC_GetValueStringLen(value);
+ const char* buf = XMLRPC_GetValueBase64(value);
+ ostr << " b(";
+ if((len > 0) && buf)
+ {
+ ostr << len << ")\"";
+ ostr.write(buf, len);
+ ostr << "\"";
+ }
+ else
+ {
+ ostr << "0)\"\"";
+ }
+ break;
+ }
+ case xmlrpc_type_boolean:
+ //lldebugs << "stream_out() bool" << llendl;
+ ostr << " " << (XMLRPC_GetValueBoolean(value) ? "true" : "false");
+ break;
+ case xmlrpc_type_datetime:
+ ostr << " d\"" << XMLRPC_GetValueDateTime_ISO8601(value) << "\"";
+ break;
+ case xmlrpc_type_double:
+ ostr << " r" << XMLRPC_GetValueDouble(value);
+ //lldebugs << "stream_out() double" << XMLRPC_GetValueDouble(value)
+ // << llendl;
+ break;
+ case xmlrpc_type_int:
+ ostr << " i" << XMLRPC_GetValueInt(value);
+ //lldebugs << "stream_out() integer:" << XMLRPC_GetValueInt(value)
+ // << llendl;
+ break;
+ case xmlrpc_type_string:
+ //lldebugs << "stream_out() string: " << str << llendl;
+ ostr << " s(" << XMLRPC_GetValueStringLen(value) << ")'"
+ << XMLRPC_GetValueString(value) << "'";
+ break;
+ case xmlrpc_type_array: // vector
+ case xmlrpc_type_mixed: // vector
+ {
+ //lldebugs << "stream_out() array" << llendl;
+ ostr << " [";
+ U32 needs_comma = 0;
+ XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
+ while(current && (LLIOPipe::STATUS_OK == status))
+ {
+ if(needs_comma++) ostr << ",";
+ status = stream_out(ostr, current);
+ current = XMLRPC_VectorNext(value);
+ }
+ ostr << "]";
+ break;
+ }
+ case xmlrpc_type_struct: // still vector
+ {
+ //lldebugs << "stream_out() struct" << llendl;
+ ostr << " {";
+ std::string name;
+ U32 needs_comma = 0;
+ XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
+ while(current && (LLIOPipe::STATUS_OK == status))
+ {
+ if(needs_comma++) ostr << ",";
+ name.assign(XMLRPC_GetValueID(current));
+ ostr << "'" << LLSDNotationFormatter::escapeString(name) << "':";
+ status = stream_out(ostr, current);
+ current = XMLRPC_VectorNext(value);
+ }
+ ostr << "}";
+ break;
+ }
+ case xmlrpc_type_empty:
+ case xmlrpc_type_none:
+ default:
+ status = LLIOPipe::STATUS_ERROR;
+ llwarns << "Found an empty xmlrpc type.." << llendl;
+ // not much we can do here...
+ break;
+ };
+ return status;
+}
+
+LLFilterXMLRPCResponse2LLSD::LLFilterXMLRPCResponse2LLSD()
+{
+}
+
+LLFilterXMLRPCResponse2LLSD::~LLFilterXMLRPCResponse2LLSD()
+{
+}
+
+LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ if(!eos) return STATUS_BREAK;
+ if(!buffer) return STATUS_ERROR;
+
+ PUMP_DEBUG;
+ // *FIX: This technique for reading data is far from optimal. We
+ // need to have some kind of istream interface into the xml
+ // parser...
+ S32 bytes = buffer->countAfter(channels.in(), NULL);
+ if(!bytes) return STATUS_ERROR;
+ char* buf = new char[bytes + 1];
+ buf[bytes] = '\0';
+ buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
+
+ //lldebugs << "xmlrpc response: " << buf << llendl;
+
+ PUMP_DEBUG;
+ XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML(
+ buf,
+ bytes,
+ NULL);
+ if(!response)
+ {
+ llwarns << "XML -> SD Response unable to parse xml." << llendl;
+ delete[] buf;
+ return STATUS_ERROR;
+ }
+
+ PUMP_DEBUG;
+ LLBufferStream stream(channels, buffer.get());
+ stream.precision(DEFAULT_PRECISION);
+ if(XMLRPC_ResponseIsFault(response))
+ {
+ PUMP_DEBUG;
+ stream << LLSDRPC_FAULT_HADER_1
+ << XMLRPC_GetResponseFaultCode(response)
+ << LLSDRPC_FAULT_HADER_2;
+ const char* fault_str = XMLRPC_GetResponseFaultString(response);
+ std::string fault_string;
+ if(fault_str)
+ {
+ fault_string.assign(fault_str);
+ }
+ stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
+ << "'" <<LLSDRPC_FAULT_FOOTER;
+ }
+ else
+ {
+ PUMP_DEBUG;
+ stream << LLSDRPC_RESPONSE_HEADER;
+ XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
+ if(param)
+ {
+ stream_out(stream, param);
+ }
+ stream << LLSDRPC_RESPONSE_FOOTER;
+ }
+ PUMP_DEBUG;
+ XMLRPC_RequestFree(response, 1);
+ delete[] buf;
+ PUMP_DEBUG;
+ return STATUS_DONE;
+}
+
+/**
+ * LLFilterXMLRPCRequest2LLSD
+ */
+LLFilterXMLRPCRequest2LLSD::LLFilterXMLRPCRequest2LLSD()
+{
+}
+
+LLFilterXMLRPCRequest2LLSD::~LLFilterXMLRPCRequest2LLSD()
+{
+}
+
+LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ if(!eos) return STATUS_BREAK;
+ if(!buffer) return STATUS_ERROR;
+
+ PUMP_DEBUG;
+ // *FIX: This technique for reading data is far from optimal. We
+ // need to have some kind of istream interface into the xml
+ // parser...
+ S32 bytes = buffer->countAfter(channels.in(), NULL);
+ if(!bytes) return STATUS_ERROR;
+ char* buf = new char[bytes + 1];
+ buf[bytes] = '\0';
+ buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
+
+ //lldebugs << "xmlrpc request: " << buf << llendl;
+
+ PUMP_DEBUG;
+ XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML(
+ buf,
+ bytes,
+ NULL);
+ if(!request)
+ {
+ llwarns << "XML -> SD Request process parse error." << llendl;
+ delete[] buf;
+ return STATUS_ERROR;
+ }
+
+ PUMP_DEBUG;
+ LLBufferStream stream(channels, buffer.get());
+ stream.precision(DEFAULT_PRECISION);
+ const char* name = XMLRPC_RequestGetMethodName(request);
+ stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "")
+ << LLSDRPC_REQUEST_HEADER_2;
+ XMLRPC_VALUE param = XMLRPC_RequestGetData(request);
+ if(param)
+ {
+ PUMP_DEBUG;
+ S32 size = XMLRPC_VectorSize(param);
+ if(size > 1)
+ {
+ // if there are multiple parameters, stuff the values into
+ // an array so that the next step in the chain can read them.
+ stream << "[";
+ }
+ XMLRPC_VALUE current = XMLRPC_VectorRewind(param);
+ bool needs_comma = false;
+ while(current)
+ {
+ if(needs_comma)
+ {
+ stream << ",";
+ }
+ needs_comma = true;
+ stream_out(stream, current);
+ current = XMLRPC_VectorNext(param);
+ }
+ if(size > 1)
+ {
+ // close the array
+ stream << "]";
+ }
+ }
+ stream << LLSDRPC_REQUEST_FOOTER;
+ XMLRPC_RequestFree(request, 1);
+ delete[] buf;
+ PUMP_DEBUG;
+ return STATUS_DONE;
+}
+
diff --git a/indra/llmessage/llfiltersd2xmlrpc.h b/indra/llmessage/llfiltersd2xmlrpc.h
new file mode 100644
index 0000000000..efde349271
--- /dev/null
+++ b/indra/llmessage/llfiltersd2xmlrpc.h
@@ -0,0 +1,253 @@
+/**
+ * @file llfiltersd2xmlrpc.h
+ * @author Phoenix
+ * @date 2005-04-26
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFILTERSD2XMLRPC_H
+#define LL_LLFILTERSD2XMLRPC_H
+
+/**
+ * These classes implement the necessary pipes for translating between
+ * xmlrpc and llsd rpc. The llsd rpcs mechanism was developed as an
+ * extensible and easy to parse serialization grammer which maintains
+ * a time efficient in-memory representation.
+ */
+
+#include <iosfwd>
+#include "lliopipe.h"
+
+/**
+ * @class LLFilterSD2XMLRPC
+ * @brief Filter from serialized LLSD to an XMLRPC method call
+ *
+ * This clas provides common functionality for the LLFilterSD2XMLRPRC
+ * request and response classes.
+ */
+class LLFilterSD2XMLRPC : public LLIOPipe
+{
+public:
+ LLFilterSD2XMLRPC();
+ virtual ~LLFilterSD2XMLRPC();
+
+protected:
+ /**
+ * @brief helper method
+ */
+ void streamOut(std::ostream& ostr, const LLSD& sd);
+};
+
+/**
+ * @class LLFilterSD2XMLRPCResponse
+ * @brief Filter from serialized LLSD to an XMLRPC response
+ *
+ * This class filters a serialized LLSD object to an xmlrpc
+ * repsonse. Since resonses are limited to a single param, the xmlrprc
+ * response only serializes it as one object.
+ * This class correctly handles normal llsd responses as well as llsd
+ * rpc faults.
+ *
+ * For example, if given:
+ * <code>{'response':[ i200, r3.4, {"foo":"bar"} ]}</code>
+ * Would generate:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodResponse><params><param><array><data>
+ * <value><int>200</int></value>
+ * <value><double>3.4</double></value>
+ * <value><struct><member>
+ * <name>foo</name><value><string>bar</string></value></member>
+ * </struct></value>
+ * </data></array></param></params></methodResponse>
+ * </code>
+ */
+class LLFilterSD2XMLRPCResponse : public LLFilterSD2XMLRPC
+{
+public:
+ // constructor
+ LLFilterSD2XMLRPCResponse();
+
+ // destructor
+ virtual ~LLFilterSD2XMLRPCResponse();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+};
+
+/**
+ * @class LLFilterSD2XMLRPCRequest
+ * @brief Filter from serialized LLSD to an XMLRPC method call
+ *
+ * This class will accept any kind of serialized LLSD object, but you
+ * probably want to have an array on the outer boundary since this
+ * object will interpret each element in the top level LLSD as a
+ * parameter into the xmlrpc spec.
+ *
+ * For example, you would represent 3 params as:
+ * <code>
+ * {'method'='foo', 'parameter':[i200, r3.4, {"foo":"bar"}]}
+ * </code>
+ * To generate:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodCall><params>
+ * <param><value><int>200</int></value></param>
+ * <param><value><double>3.4</double></value></param>
+ * <param><value><struct><member>
+ * <name>foo</name><value><string>bar</string></value></member>
+ * </struct></value></param>
+ * </params></methodCall>
+ *
+ * This class will accept 2 different kinds of encodings. The first
+ * just an array of params as long as you specify the method in the
+ * constructor. It will also accept a structured data in the form:
+ * {'method':'$method_name', 'parameter':[...] } In the latter form, the
+ * encoded 'method' will be used regardless of the construction of the
+ * object, and the 'parameter' will be used as parameter to the call.
+ */
+class LLFilterSD2XMLRPCRequest : public LLFilterSD2XMLRPC
+{
+public:
+ // constructor
+ LLFilterSD2XMLRPCRequest();
+
+ // constructor
+ LLFilterSD2XMLRPCRequest(const char* method);
+
+ // destructor
+ virtual ~LLFilterSD2XMLRPCRequest();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ // The method name of this request.
+ std::string mMethod;
+};
+
+/**
+ * @class LLFilterXMLRPCResponse2LLSD
+ * @brief Filter from serialized XMLRPC method response to LLSD
+ *
+ * The xmlrpc spec states that responses can only have one element
+ * which can be of any supported type.
+ * This takes in xml of the form:
+ * <code>
+ * <?xml version=\"1.0\"?><methodResponse><params><param>
+ * <value><string>ok</string></value></param></params></methodResponse>
+ * </code>
+ * And processes it into:
+ * <code>'ok'</code>
+ *
+ */
+class LLFilterXMLRPCResponse2LLSD : public LLIOPipe
+{
+public:
+ // constructor
+ LLFilterXMLRPCResponse2LLSD();
+
+ // destructor
+ virtual ~LLFilterXMLRPCResponse2LLSD();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+};
+
+/**
+ * @class LLFilterXMLRPCRequest2LLSD
+ * @brief Filter from serialized XMLRPC method call to LLSD
+ *
+ * This takes in xml of the form:
+ * <code>
+ * <?xml version=\"1.0\"?><methodCall>
+ * <methodName>repeat</methodName>
+ * <params>
+ * <param><value><i4>4</i4></value></param>
+ * <param><value><string>ok</string></value></param>
+ * </params></methodCall>
+ * </code>
+ * And processes it into:
+ * <code>{ 'method':'repeat', 'params':[i4, 'ok'] }</code>
+ */
+class LLFilterXMLRPCRequest2LLSD : public LLIOPipe
+{
+public:
+ // constructor
+ LLFilterXMLRPCRequest2LLSD();
+
+ // destructor
+ virtual ~LLFilterXMLRPCRequest2LLSD();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+};
+
+/**
+ * @brief This function takes string, and escapes it appropritately
+ * for inclusion as xml data.
+ */
+std::string xml_escape_string(const std::string& in);
+
+/**
+ * @brief Externally available constants
+ */
+extern const char LLSDRPC_REQUEST_HEADER_1[];
+extern const char LLSDRPC_REQUEST_HEADER_2[];
+extern const char LLSDRPC_REQUEST_FOOTER[];
+
+#endif // LL_LLFILTERSD2XMLRPC_H
diff --git a/indra/llmessage/llfollowcamparams.h b/indra/llmessage/llfollowcamparams.h
new file mode 100644
index 0000000000..1fa2a089ae
--- /dev/null
+++ b/indra/llmessage/llfollowcamparams.h
@@ -0,0 +1,43 @@
+/**
+ * @file llfollowcamparams.h
+ * @brief Follow camera parameters.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_FOLLOWCAM_PARAMS_H
+#define LL_FOLLOWCAM_PARAMS_H
+
+
+//Ventrella Follow Cam Script Stuff
+enum EFollowCamAttributes {
+ FOLLOWCAM_PITCH = 0,
+ FOLLOWCAM_FOCUS_OFFSET,
+ FOLLOWCAM_FOCUS_OFFSET_X, //this HAS to come after FOLLOWCAM_FOCUS_OFFSET in this list
+ FOLLOWCAM_FOCUS_OFFSET_Y,
+ FOLLOWCAM_FOCUS_OFFSET_Z,
+ FOLLOWCAM_POSITION_LAG,
+ FOLLOWCAM_FOCUS_LAG,
+ FOLLOWCAM_DISTANCE,
+ FOLLOWCAM_BEHINDNESS_ANGLE,
+ FOLLOWCAM_BEHINDNESS_LAG,
+ FOLLOWCAM_POSITION_THRESHOLD,
+ FOLLOWCAM_FOCUS_THRESHOLD,
+ FOLLOWCAM_ACTIVE,
+ FOLLOWCAM_POSITION,
+ FOLLOWCAM_POSITION_X, //this HAS to come after FOLLOWCAM_POSITION in this list
+ FOLLOWCAM_POSITION_Y,
+ FOLLOWCAM_POSITION_Z,
+ FOLLOWCAM_FOCUS,
+ FOLLOWCAM_FOCUS_X, //this HAS to come after FOLLOWCAM_FOCUS in this list
+ FOLLOWCAM_FOCUS_Y,
+ FOLLOWCAM_FOCUS_Z,
+ FOLLOWCAM_POSITION_LOCKED,
+ FOLLOWCAM_FOCUS_LOCKED,
+ NUM_FOLLOWCAM_ATTRIBUTES
+};
+
+//end Ventrella
+
+#endif //FOLLOWCAM_PARAMS_H
diff --git a/indra/llmessage/llhost.cpp b/indra/llmessage/llhost.cpp
new file mode 100644
index 0000000000..f4a1740663
--- /dev/null
+++ b/indra/llmessage/llhost.cpp
@@ -0,0 +1,216 @@
+/**
+ * @file llhost.cpp
+ * @brief Encapsulates an IP address and a port.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+
+#if !LL_WINDOWS
+#include <netdb.h>
+#include <netinet/in.h> // ntonl()
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#endif
+
+#include "llhost.h"
+
+#include "llerror.h"
+
+LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS);
+
+LLHost::LLHost(const std::string& ip_and_port)
+{
+ std::string::size_type colon_index = ip_and_port.find(":");
+ if (colon_index != std::string::npos)
+ {
+ mIP = ip_string_to_u32(ip_and_port.c_str());
+ mPort = 0;
+ }
+ else
+ {
+ std::string ip_str(ip_and_port, 0, colon_index);
+ std::string port_str(ip_and_port, colon_index+1);
+
+ mIP = ip_string_to_u32(ip_str.c_str());
+ mPort = atol(port_str.c_str());
+ }
+}
+
+void LLHost::getString(char* buffer, S32 length) const
+{
+ if (((U32) length) < MAXADDRSTR + 1 + 5)
+ {
+ llerrs << "LLHost::getString - string too short" << llendl;
+ return;
+ }
+
+ snprintf(buffer, length, "%s:%u", u32_to_ip_string(mIP), mPort); /*Flawfinder: ignore*/
+}
+
+void LLHost::getIPString(char* buffer, S32 length) const
+{
+ if ( ((U32) length) < MAXADDRSTR)
+ {
+ llerrs << "LLHost::getIPString - string too short" << llendl;
+ return;
+ }
+
+ snprintf(buffer, length, "%s", u32_to_ip_string(mIP)); /*Flawfinder: ignore*/
+}
+
+
+std::string LLHost::getIPandPort() const
+{
+ char buffer[MAXADDRSTR + 1 + 5];
+ getString(buffer, sizeof(buffer));
+ return buffer;
+}
+
+
+std::string LLHost::getIPString() const
+{
+ return std::string( u32_to_ip_string( mIP ) );
+}
+
+
+void LLHost::getHostName(char *buf, S32 len) const
+{
+ hostent *he;
+
+ if (INVALID_HOST_IP_ADDRESS == mIP)
+ {
+ llwarns << "LLHost::getHostName() : Invalid IP address" << llendl;
+ buf[0] = '\0';
+ return;
+ }
+ he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET);
+ if (!he)
+ {
+#if LL_WINDOWS
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << WSAGetLastError() << llendl;
+#else
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << h_errno << llendl;
+#endif
+ buf[0] = '\0';
+ }
+ else
+ {
+ strncpy(buf, he->h_name, len); /*Flawfinder: ignore*/
+ buf[len-1] = '\0';
+ }
+}
+
+LLString LLHost::getHostName() const
+{
+ hostent *he;
+
+ if (INVALID_HOST_IP_ADDRESS == mIP)
+ {
+ llwarns << "LLHost::getHostName() : Invalid IP address" << llendl;
+ return "";
+ }
+ he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET);
+ if (!he)
+ {
+#if LL_WINDOWS
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << WSAGetLastError() << llendl;
+#else
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << h_errno << llendl;
+#endif
+ return "";
+ }
+ else
+ {
+ LLString hostname = he->h_name;
+ return hostname;
+ }
+}
+
+BOOL LLHost::setHostByName(const char *string)
+{
+ hostent *he;
+ char local_name[MAX_STRING]; /*Flawfinder: ignore*/
+
+ if (strlen(string)+1 > MAX_STRING) /*Flawfinder: ignore*/
+ {
+ llerrs << "LLHost::setHostByName() : Address string is too long: "
+ << string << llendl;
+ }
+
+ strncpy(local_name, string,MAX_STRING); /*Flawfinder: ignore*/
+ local_name[MAX_STRING-1] = '\0';
+#if LL_WINDOWS
+ // We may need an equivalent for Linux, but not sure - djs
+ _strupr(local_name);
+#endif
+
+ he = gethostbyname(local_name);
+ if(!he)
+ {
+ U32 ip_address = inet_addr(string);
+ he = gethostbyaddr((char *)&ip_address, sizeof(ip_address), AF_INET);
+ }
+
+ if (he)
+ {
+ mIP = *(U32 *)he->h_addr_list[0];
+ return TRUE;
+ }
+ else
+ {
+ setAddress(local_name);
+
+ // In windows, h_errno is a macro for WSAGetLastError(), so store value here
+ S32 error_number = h_errno;
+ switch(error_number)
+ {
+ case TRY_AGAIN: // XXX how to handle this case?
+ llwarns << "LLHost::setAddress(): try again" << llendl;
+ break;
+ case HOST_NOT_FOUND:
+ case NO_ADDRESS: // NO_DATA
+ llwarns << "LLHost::setAddress(): host not found" << llendl;
+ break;
+ case NO_RECOVERY:
+ llwarns << "LLHost::setAddress(): unrecoverable error" << llendl;
+ break;
+ default:
+ llwarns << "LLHost::setAddress(): unknown error - " << error_number << llendl;
+ break;
+ }
+ return FALSE;
+ }
+}
+
+LLHost& LLHost::operator=(const LLHost &rhs)
+{
+ if (this != &rhs)
+ {
+ set(rhs.getAddress(), rhs.getPort());
+ }
+ return *this;
+}
+
+
+std::ostream& operator<< (std::ostream& os, const LLHost &hh)
+{
+ os << u32_to_ip_string(hh.mIP) << ":" << hh.mPort ;
+ return os;
+}
+
+
+std::istream& operator>> (std::istream& is, LLHost &rh)
+{
+ is >> rh.mIP;
+ is >> rh.mPort;
+ return is;
+}
diff --git a/indra/llmessage/llhost.h b/indra/llmessage/llhost.h
new file mode 100644
index 0000000000..09dbae61b9
--- /dev/null
+++ b/indra/llmessage/llhost.h
@@ -0,0 +1,133 @@
+/**
+ * @file llhost.h
+ * @brief a LLHost uniquely defines a host (Simulator, Proxy or other)
+ * across the network
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHOST_H
+#define LL_LLHOST_H
+
+#include <iostream>
+#include <string>
+
+#include "net.h"
+
+#include "llstring.h"
+
+const U32 INVALID_PORT = 0;
+const U32 INVALID_HOST_IP_ADDRESS = 0x0;
+
+class LLHost {
+protected:
+ U32 mPort;
+ U32 mIP;
+public:
+
+ static LLHost invalid;
+
+ // CREATORS
+ LLHost()
+ : mPort(INVALID_PORT),
+ mIP(INVALID_HOST_IP_ADDRESS)
+ { } // STL's hash_map expect this T()
+
+ LLHost( U32 ipv4_addr, U32 port )
+ : mPort( port )
+ {
+ mIP = ipv4_addr;
+ }
+
+ LLHost( const char *ipv4_addr, U32 port )
+ : mPort( port )
+ {
+ mIP = ip_string_to_u32(ipv4_addr);
+ }
+
+ explicit LLHost(const U64 ip_port)
+ {
+ U32 ip = (U32)(ip_port >> 32);
+ U32 port = (U32)(ip_port & (U64)0xFFFFFFFF);
+ mIP = ip;
+ mPort = port;
+ }
+
+ explicit LLHost(const std::string& ip_and_port);
+
+ ~LLHost()
+ { }
+
+ // MANIPULATORS
+ void set( U32 ip, U32 port ) { mIP = ip; mPort = port; }
+ void set( const char* ipstr, U32 port ) { mIP = ip_string_to_u32(ipstr); mPort = port; }
+ void setAddress( const char* ipstr ) { mIP = ip_string_to_u32(ipstr); }
+ void setAddress( U32 ip ) { mIP = ip; }
+ void setPort( U32 port ) { mPort = port; }
+ BOOL setHostByName(const char *hname);
+
+ LLHost& operator=(const LLHost &rhs);
+ void invalidate() { mIP = INVALID_HOST_IP_ADDRESS; mPort = INVALID_PORT;};
+
+ // READERS
+ U32 getAddress() const { return mIP; }
+ U32 getPort() const { return mPort; }
+ BOOL isOk() const { return (mIP != INVALID_HOST_IP_ADDRESS) && (mPort != INVALID_PORT); }
+ size_t hash() const { return (mIP << 16) | (mPort & 0xffff); }
+ void getString(char* buffer, S32 length) const; // writes IP:port into buffer
+ void getIPString(char* buffer, S32 length) const; // writes IP into buffer
+ std::string getIPString() const;
+ void getHostName(char *buf, S32 len) const;
+ LLString getHostName() const;
+ std::string getIPandPort() const;
+
+ friend std::ostream& operator<< (std::ostream& os, const LLHost &hh);
+ friend std::istream& operator>> (std::istream& is, LLHost &hh);
+ friend bool operator==( const LLHost &lhs, const LLHost &rhs );
+ friend bool operator!=( const LLHost &lhs, const LLHost &rhs );
+ friend bool operator<(const LLHost &lhs, const LLHost &rhs);
+};
+
+
+// Function Object required for STL templates using LLHost as key
+class LLHostHash
+{
+public:
+ size_t operator() (const LLHost &hh) const { return hh.hash(); }
+};
+
+
+inline bool operator==( const LLHost &lhs, const LLHost &rhs )
+{
+ return (lhs.mIP == rhs.mIP) && (lhs.mPort == rhs.mPort);
+}
+
+inline bool operator!=( const LLHost &lhs, const LLHost &rhs )
+{
+ return (lhs.mIP != rhs.mIP) || (lhs.mPort != rhs.mPort);
+}
+
+inline bool operator<(const LLHost &lhs, const LLHost &rhs)
+{
+ if (lhs.mIP < rhs.mIP)
+ {
+ return true;
+ }
+ if (lhs.mIP > rhs.mIP)
+ {
+ return false;
+ }
+
+ if (lhs.mPort < rhs.mPort)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+#endif // LL_LLHOST_H
diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp
new file mode 100644
index 0000000000..12d9d610cc
--- /dev/null
+++ b/indra/llmessage/llhttpassetstorage.cpp
@@ -0,0 +1,996 @@
+/**
+ * @file llhttpassetstorage.cpp
+ * @brief Subclass capable of loading asset data to/from an external
+ * source. Currently, a web server accessed via curl
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llhttpassetstorage.h"
+
+#include "indra_constants.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+#include "zlib/zlib.h"
+
+const F32 MAX_PROCESSING_TIME = 0.005f;
+const S32 CURL_XFER_BUFFER_SIZE = 65536;
+// Try for 30 minutes for now.
+const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f;
+
+const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096;
+
+const S32 HTTP_OK = 200;
+const S32 HTTP_PUT_OK = 201;
+const S32 HTTP_NO_CONTENT = 204;
+const S32 HTTP_MISSING = 404;
+const S32 HTTP_SERVER_BAD_GATEWAY = 502;
+const S32 HTTP_SERVER_TEMP_UNAVAILABLE = 503;
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLTempAssetData
+// An asset not stored on central asset store, but on a simulator node somewhere.
+/////////////////////////////////////////////////////////////////////////////////
+struct LLTempAssetData
+{
+ LLUUID mAssetID;
+ LLUUID mAgentID;
+ std::string mHostName;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLHTTPAssetRequest
+/////////////////////////////////////////////////////////////////////////////////
+
+class LLHTTPAssetRequest : public LLAssetRequest
+{
+public:
+ LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, LLAssetType::EType type, const char *url, CURLM *curl_multi);
+ virtual ~LLHTTPAssetRequest();
+
+ void setupCurlHandle();
+
+ void prepareCompressedUpload();
+ void finishCompressedUpload();
+ size_t readCompressedData(void* data, size_t size);
+
+ static size_t curlCompressedUploadCallback(
+ void *data, size_t size, size_t nmemb, void *user_data);
+
+public:
+ LLHTTPAssetStorage *mAssetStoragep;
+
+ CURL *mCurlHandle;
+ CURLM *mCurlMultiHandle;
+ char *mURLBuffer;
+ struct curl_slist *mHTTPHeaders;
+ LLVFile *mVFile;
+ LLUUID mTmpUUID;
+ BOOL mIsUpload;
+ BOOL mIsLocalUpload;
+ BOOL mIsDownload;
+
+ bool mZInitialized;
+ z_stream mZStream;
+ char* mZInputBuffer;
+ bool mZInputExhausted;
+
+ FILE *mFP;
+};
+
+
+LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, LLAssetType::EType type, const char *url, CURLM *curl_multi)
+ : LLAssetRequest(uuid, type),
+ mZInitialized(false)
+{
+ mAssetStoragep = asp;
+ mCurlHandle = NULL;
+ mCurlMultiHandle = curl_multi;
+ mVFile = NULL;
+ mIsUpload = FALSE;
+ mIsLocalUpload = FALSE;
+ mIsDownload = FALSE;
+ mHTTPHeaders = NULL;
+
+ mURLBuffer = new char[strlen(url) + 1]; /*Flawfinder: ignore*/
+ if (mURLBuffer)
+ {
+ strcpy(mURLBuffer, url);
+ }
+}
+
+LLHTTPAssetRequest::~LLHTTPAssetRequest()
+{
+ // Cleanup/cancel the request
+ if (mCurlHandle)
+ {
+ curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle);
+ curl_easy_cleanup(mCurlHandle);
+ if (mAssetStoragep)
+ {
+ // Terminating a request. Thus upload or download is no longer pending.
+ if (mIsUpload)
+ {
+ mAssetStoragep->clearPendingUpload();
+ }
+ else if (mIsLocalUpload)
+ {
+ mAssetStoragep->clearPendingLocalUpload();
+ }
+ else if (mIsDownload)
+ {
+ mAssetStoragep->clearPendingDownload();
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - Destroyed request is not upload OR download, this is bad!" << llendl;
+ }
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << llendl;
+ }
+ }
+ if (mHTTPHeaders)
+ {
+ curl_slist_free_all(mHTTPHeaders);
+ }
+ delete[] mURLBuffer;
+ delete mVFile;
+ finishCompressedUpload();
+}
+
+void LLHTTPAssetRequest::setupCurlHandle()
+{
+ mCurlHandle = curl_easy_init();
+ curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer);
+ curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
+ if (mIsDownload)
+ {
+ curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
+ // only do this on downloads, as uploads
+ // to some apache configs (like our test grids)
+ // mistakenly claim the response is gzip'd if the resource
+ // name ends in .gz, even though in a PUT, the response is
+ // just plain HTML saying "created"
+ }
+ /* Remove the Pragma: no-cache header that libcurl inserts by default;
+ we want the cached version, if possible. */
+ if (mZInitialized)
+ {
+ curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, "");
+ // disable use of proxy, which can't handle chunked transfers
+ }
+ mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:");
+ // resist the temptation to explicitly add the Transfer-Encoding: chunked
+ // header here - invokes a libCURL bug
+ curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders);
+ if (mAssetStoragep)
+ {
+ // Set the appropriate pending upload or download flag
+ if (mIsUpload)
+ {
+ mAssetStoragep->setPendingUpload();
+ }
+ else if (mIsLocalUpload)
+ {
+ mAssetStoragep->setPendingLocalUpload();
+ }
+ else if (mIsDownload)
+ {
+ mAssetStoragep->setPendingDownload();
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::setupCurlHandle - Request is not upload OR download, this is bad!" << llendl;
+ }
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << llendl;
+ }
+}
+
+void LLHTTPAssetRequest::prepareCompressedUpload()
+{
+ mZStream.next_in = Z_NULL;
+ mZStream.avail_in = 0;
+ mZStream.zalloc = Z_NULL;
+ mZStream.zfree = Z_NULL;
+ mZStream.opaque = Z_NULL;
+
+ int r = deflateInit2(&mZStream,
+ 1, // compression level
+ Z_DEFLATED, // the only method defined
+ 15 + 16, // the default windowBits + gzip header flag
+ 8, // the default memLevel
+ Z_DEFAULT_STRATEGY);
+
+ if (r != Z_OK)
+ {
+ llerrs << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << llendl;
+ }
+
+ mZInitialized = true;
+ mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE];
+ mZInputExhausted = false;
+
+ mVFile = new LLVFile(gAssetStorage->mVFS,
+ getUUID(), getType(), LLVFile::READ);
+}
+
+void LLHTTPAssetRequest::finishCompressedUpload()
+{
+ if (mZInitialized)
+ {
+ llinfos << "LLHTTPAssetRequest::finishCompressedUpload: "
+ << "read " << mZStream.total_in << " byte asset file, "
+ << "uploaded " << mZStream.total_out << " byte compressed asset"
+ << llendl;
+
+ deflateEnd(&mZStream);
+ delete[] mZInputBuffer;
+ }
+}
+
+size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size)
+{
+ mZStream.next_out = (Bytef*)data;
+ mZStream.avail_out = size;
+
+ while (mZStream.avail_out > 0)
+ {
+ if (mZStream.avail_in == 0 && !mZInputExhausted)
+ {
+ S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE,
+ (S32)(mVFile->getSize() - mVFile->tell()));
+
+ mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/
+
+ mZStream.next_in = (Bytef*)mZInputBuffer;
+ mZStream.avail_in = mVFile->getLastBytesRead();
+
+ mZInputExhausted = mZStream.avail_in == 0;
+ }
+
+ int r = deflate(&mZStream,
+ mZInputExhausted ? Z_FINISH : Z_NO_FLUSH);
+
+ if (r == Z_STREAM_END)
+ {
+ break;
+ }
+ }
+
+ return size - mZStream.avail_out;
+}
+
+//static
+size_t LLHTTPAssetRequest::curlCompressedUploadCallback(
+ void *data, size_t size, size_t nmemb, void *user_data)
+{
+ if (!gAssetStorage)
+ {
+ return 0;
+ }
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ return req->readCompressedData(data, size * nmemb);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLHTTPAssetStorage
+/////////////////////////////////////////////////////////////////////////////////
+
+
+LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name)
+ : LLAssetStorage(msg, xfer, vfs, upstream_host)
+{
+ _init(web_host, local_web_host, host_name);
+}
+
+LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name)
+ : LLAssetStorage(msg, xfer, vfs)
+{
+ _init(web_host, local_web_host, host_name);
+}
+
+void LLHTTPAssetStorage::_init(const char *web_host, const char *local_web_host, const char* host_name)
+{
+ mBaseURL = web_host;
+ mLocalBaseURL = local_web_host;
+ mHostName = host_name;
+
+ // Do not change this "unless you are familiar with and mean to control
+ // internal operations of libcurl"
+ // - http://curl.haxx.se/libcurl/c/curl_global_init.html
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ mCurlMultiHandle = curl_multi_init();
+
+ mPendingDownload = FALSE;
+ mPendingUpload = FALSE;
+ mPendingLocalUpload = FALSE;
+}
+
+LLHTTPAssetStorage::~LLHTTPAssetStorage()
+{
+ curl_multi_cleanup(mCurlMultiHandle);
+ mCurlMultiHandle = NULL;
+
+ curl_global_cleanup();
+}
+
+// storing data is simpler than getting it, so we just overload the whole method
+void LLHTTPAssetStorage::storeAssetData(
+ const LLUUID& uuid,
+ LLAssetType::EType type,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority,
+ bool store_local,
+ const LLUUID& requesting_agent_id)
+{
+ if (mVFS->getExists(uuid, type))
+ {
+ LLAssetRequest *req = new LLAssetRequest(uuid, type);
+ req->mUpCallback = callback;
+ req->mUserData = user_data;
+ req->mRequestingAgentID = requesting_agent_id;
+
+ // this will get picked up and transmitted in checkForTimeouts
+ if(store_local)
+ {
+ mPendingLocalUploads.push_back(req);
+ }
+ else if(is_priority)
+ {
+ mPendingUploads.push_front(req);
+ }
+ else
+ {
+ mPendingUploads.push_back(req);
+ }
+ }
+ else
+ {
+ llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl;
+ if (callback)
+ {
+ callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE );
+ }
+ }
+}
+
+// virtual
+void LLHTTPAssetStorage::storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ llinfos << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
+
+ LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
+
+ legacy->mUpCallback = callback;
+ legacy->mUserData = user_data;
+
+ FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
+ if (fp)
+ {
+ LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+
+ // if this upload fails, the caller needs to setup a new tempfile for us
+ if (temp_file)
+ {
+ LLFile::remove(filename);
+ }
+
+ storeAssetData(
+ asset_id,
+ asset_type,
+ legacyStoreDataCallback,
+ (void**)legacy,
+ temp_file,
+ is_priority);
+ }
+ else
+ {
+ if (callback)
+ {
+ callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE);
+ }
+ }
+}
+
+// internal requester, used by getAssetData in superclass
+void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
+ void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
+ void *user_data, BOOL duplicate,
+ BOOL is_priority)
+{
+ // stash the callback info so we can find it after we get the response message
+ LLAssetRequest *req = new LLAssetRequest(uuid, type);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ // this will get picked up and downloaded in checkForTimeouts
+
+ //
+ // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK! Asset requests were taking too long and timing out.
+ // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add
+ // non-texture requests to the front, and add texture requests to the back. The theory is
+ // that we always want them first, even if they're out of order.
+ //
+
+ if (req->getType() == LLAssetType::AT_TEXTURE)
+ {
+ mPendingDownloads.push_back(req);
+ }
+ else
+ {
+ mPendingDownloads.push_front(req);
+ }
+}
+
+// overloaded to additionally move data to/from the webserver
+void LLHTTPAssetStorage::checkForTimeouts()
+{
+ LLAssetRequest *req = NULL;
+ if (mPendingDownloads.size() > 0 && !mPendingDownload)
+ {
+ req = mPendingDownloads.front();
+ // Setup this curl download request
+ // We need to generate a new request here
+ // since the one in the list could go away
+ char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
+ char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
+ req->getUUID().toString(uuid_str);
+ std::string base_url = getBaseURL(req->getUUID(), req->getType());
+ snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", base_url.c_str() , uuid_str, LLAssetType::lookup(req->getType())); /*Flawfinder: ignore*/
+
+ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
+ new_req->mTmpUUID.generate();
+ new_req->mIsDownload = TRUE;
+
+ // Sets pending download flag internally
+ new_req->setupCurlHandle();
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
+ llinfos << "Requesting " << new_req->mURLBuffer << llendl;
+
+ }
+
+
+ if (mPendingUploads.size() > 0 && !mPendingUpload)
+ {
+ req = mPendingUploads.front();
+ // setup this curl upload request
+
+ bool do_compress = req->getType() == LLAssetType::AT_OBJECT;
+
+ char tmp_url[MAX_STRING];/*Flawfinder: ignore*/
+ char uuid_str[UUID_STR_LENGTH];/*Flawfinder: ignore*/
+ req->getUUID().toString(uuid_str);
+ snprintf(tmp_url, sizeof(tmp_url), /*Flawfinder: ignore*/
+ do_compress ? "%s/%s.%s.gz" : "%s/%s.%s",
+ mBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType()));
+
+ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
+ new_req->mIsUpload = TRUE;
+ if (do_compress)
+ {
+ new_req->prepareCompressedUpload();
+ }
+
+ // Sets pending upload flag internally
+ new_req->setupCurlHandle();
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
+
+ if (do_compress)
+ {
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
+ &LLHTTPAssetRequest::curlCompressedUploadCallback);
+ }
+ else
+ {
+ LLVFile file(mVFS, req->getUUID(), req->getType());
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
+ &curlUpCallback);
+ }
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
+ llinfos << "Requesting PUT " << new_req->mURLBuffer << llendl;
+ // Pending upload will have been flagged by the request
+ }
+
+
+ if (mPendingLocalUploads.size() > 0 && !mPendingLocalUpload)
+ {
+ req = mPendingLocalUploads.front();
+ // setup this curl upload request
+ LLVFile file(mVFS, req->getUUID(), req->getType());
+
+ char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
+ char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
+ req->getUUID().toString(uuid_str);
+
+ // KLW - All temporary uploads are saved locally "http://localhost:12041/asset"
+ snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType())); /*Flawfinder: ignore*/
+
+ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
+ new_req->mIsLocalUpload = TRUE;
+ new_req->mRequestingAgentID = req->mRequestingAgentID;
+
+ // Sets pending upload flag internally
+ new_req->setupCurlHandle();
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
+ llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!"
+ << " Requesting PUT " << new_req->mURLBuffer << llendl;
+ // Pending upload will have been flagged by the request
+ }
+ S32 count = 0;
+ CURLMcode mcode;
+ int queue_length;
+ do
+ {
+ mcode = curl_multi_perform(mCurlMultiHandle, &queue_length);
+ count++;
+ } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5));
+
+ CURLMsg *curl_msg;
+ do
+ {
+ curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
+ if (curl_msg && curl_msg->msg == CURLMSG_DONE)
+ {
+ long curl_result = 0;
+ S32 xfer_result = 0;
+
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req);
+
+ curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
+ if (req->mIsUpload || req->mIsLocalUpload)
+ {
+ if (curl_msg->data.result == CURLE_OK && (curl_result == HTTP_OK || curl_result == HTTP_PUT_OK || curl_result == HTTP_NO_CONTENT))
+ {
+ llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl;
+ if (req->mIsLocalUpload)
+ {
+ addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName);
+ }
+ }
+ else if (curl_msg->data.result == CURLE_COULDNT_CONNECT ||
+ curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
+ curl_result == HTTP_SERVER_BAD_GATEWAY ||
+ curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)
+ {
+ llwarns << "Re-requesting upload for " << req->getUUID() << ". Received upload error to " << req->mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+ }
+ else
+ {
+ llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+
+ xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
+ }
+
+ if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT ||
+ curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
+ curl_result == HTTP_SERVER_BAD_GATEWAY ||
+ curl_result == HTTP_SERVER_TEMP_UNAVAILABLE))
+ {
+ // shared upload finished callback
+ // in the base class, this is called from processUploadComplete
+ _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0));
+ // Pending upload flag will get cleared when the request is deleted
+ }
+ }
+ else if (req->mIsDownload)
+ {
+ if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
+ {
+ if (req->mVFile && req->mVFile->getSize() > 0)
+ {
+ llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl;
+
+ req->mVFile->rename(req->getUUID(), req->getType());
+ }
+ else
+ {
+ // TODO: if this actually indicates a bad asset on the server
+ // (not certain at this point), then delete it
+ llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl;
+ xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ }
+ }
+ else
+ {
+ // KLW - TAT See if an avatar owns this texture, and if so request re-upload.
+ llwarns << "Failure downloading " << req->mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+
+ xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
+
+ if (req->mVFile)
+ {
+ req->mVFile->remove();
+ }
+ }
+
+ // call the static callback for transfer completion
+ // this will cleanup all requests for this asset, including ours
+ downloadCompleteCallback(xfer_result, (void *)req);
+ // Pending download flag will get cleared when the request is deleted
+ }
+ else
+ {
+ // nothing, just axe this request
+ // currently this can only mean an asset delete
+ }
+
+ // Deleting clears the pending upload/download flag if it's set and the request is transferring
+ delete req;
+ req = NULL;
+ }
+
+ } while (curl_msg && queue_length > 0);
+
+
+ LLAssetStorage::checkForTimeouts();
+}
+
+// static
+size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
+ return 0;
+ }
+ S32 bytes = (S32)(size * nmemb);
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ if (! req->mVFile)
+ {
+ req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND);
+ }
+
+ double content_length = 0.0;
+ curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
+
+ // sanitize content_length, reconcile w/ actual data
+ S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize());
+
+ req->mVFile->setMaxSize(file_length);
+ req->mVFile->write((U8*)data, bytes);
+
+ return nmemb;
+}
+
+// static
+size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
+ return 0;
+ }
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ if (! req->mVFile)
+ {
+ req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ);
+ }
+
+ S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell()));
+
+ req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/
+
+ return req->mVFile->getLastBytesRead();
+}
+
+// static
+size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ // do nothing, this is here to soak up script output so it doesn't end up on stdout
+
+ return nmemb;
+}
+
+
+
+// blocking asset fetch which bypasses the VFS
+// this is a very limited function for use by the simstate loader and other one-offs
+S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata)
+{
+ // FIXME: There is no guarantee that the uuid and the asset_type match
+ // - not that it matters. - Doug
+ lldebugs << "LLHTTPAssetStorage::getURLToFile() - " << url << llendl;
+
+ FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
+ if (! fp)
+ {
+ llwarns << "Failed to open " << filename << " for writing" << llendl;
+ return LL_ERR_ASSET_REQUEST_FAILED;
+ }
+
+ // make sure we use the normal curl setup, even though we don't really need a request object
+ LLHTTPAssetRequest req(this, uuid, asset_type, url.c_str(), mCurlMultiHandle);
+ req.mFP = fp;
+ req.mIsDownload = TRUE;
+
+ req.setupCurlHandle();
+ curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
+ curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback);
+ curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle);
+ llinfos << "Requesting as file " << req.mURLBuffer << llendl;
+
+ // braindead curl loop
+ int queue_length;
+ CURLMsg *curl_msg;
+ LLTimer timeout;
+ timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT);
+ bool success = false;
+ S32 xfer_result = 0;
+ do
+ {
+ curl_multi_perform(mCurlMultiHandle, &queue_length);
+ curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
+
+ if (callback)
+ {
+ callback(userdata);
+ }
+
+ if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) )
+ {
+ success = true;
+ }
+ else if (timeout.hasExpired())
+ {
+ llwarns << "Request for " << url << " has timed out." << llendl;
+ success = false;
+ xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
+ break;
+ }
+ } while (!success);
+
+ if (success)
+ {
+ long curl_result = 0;
+ curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
+
+ if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
+ {
+ S32 size = ftell(req.mFP);
+ if (size > 0)
+ {
+ // everything seems to be in order
+ llinfos << "Success downloading " << req.mURLBuffer << " to file, size " << size << llendl;
+ }
+ else
+ {
+ llwarns << "Found " << req.mURLBuffer << " to be zero size" << llendl;
+ xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
+ }
+ }
+ else
+ {
+ xfer_result = curl_result == HTTP_MISSING ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
+ llinfos << "Failure downloading " << req.mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+ }
+ }
+
+ fclose(fp);
+ if (xfer_result)
+ {
+ LLFile::remove(filename);
+ }
+ return xfer_result;
+}
+
+
+// static
+size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ if (! req->mFP)
+ {
+ llwarns << "Missing mFP, aborting curl file download callback!" << llendl;
+ return 0;
+ }
+
+ return fwrite(data, size, nmemb, req->mFP);
+}
+
+// virtual
+void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
+{
+ if (agent_id.isNull() || asset_id.isNull())
+ {
+ llwarns << "TAT: addTempAssetData bad id's asset_id: " << asset_id << " agent_id: " << agent_id << llendl;
+ return;
+ }
+
+ LLTempAssetData temp_asset_data;
+ temp_asset_data.mAssetID = asset_id;
+ temp_asset_data.mAgentID = agent_id;
+ temp_asset_data.mHostName = host_name;
+
+ mTempAssets[asset_id] = temp_asset_data;
+}
+
+// virtual
+BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
+{
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
+ BOOL found = (citer != mTempAssets.end());
+ return found;
+}
+
+// virtual
+std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
+{
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
+ if (citer != mTempAssets.end())
+ {
+ return citer->second.mHostName;
+ }
+ else
+ {
+ return std::string();
+ }
+}
+
+// virtual
+LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
+{
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
+ if (citer != mTempAssets.end())
+ {
+ return citer->second.mAgentID;
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+// virtual
+void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id)
+{
+ mTempAssets.erase(asset_id);
+}
+
+// virtual
+void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
+{
+ uuid_tempdata_map::iterator it = mTempAssets.begin();
+ uuid_tempdata_map::iterator end = mTempAssets.end();
+
+ while (it != end)
+ {
+ const LLTempAssetData& asset_data = it->second;
+ if (asset_data.mAgentID == agent_id)
+ {
+ mTempAssets.erase(it++);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type)
+{
+ if (LLAssetType::AT_TEXTURE == asset_type)
+ {
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id);
+ if (citer != mTempAssets.end())
+ {
+ const std::string& host_name = citer->second.mHostName;
+ std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str());
+ return url;
+ }
+ }
+
+ return mBaseURL;
+}
+
+void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
+{
+ uuid_tempdata_map::const_iterator it = mTempAssets.begin();
+ uuid_tempdata_map::const_iterator end = mTempAssets.end();
+ S32 count = 0;
+ for ( ; it != end; ++it)
+ {
+ const LLTempAssetData& temp_asset_data = it->second;
+ if (avatar_id.isNull()
+ || avatar_id == temp_asset_data.mAgentID)
+ {
+ llinfos << "TAT: dump agent " << temp_asset_data.mAgentID
+ << " texture " << temp_asset_data.mAssetID
+ << " host " << temp_asset_data.mHostName
+ << llendl;
+ count++;
+ }
+ }
+
+ if (avatar_id.isNull())
+ {
+ llinfos << "TAT: dumped " << count << " entries for all avatars" << llendl;
+ }
+ else
+ {
+ llinfos << "TAT: dumped " << count << " entries for avatar " << avatar_id << llendl;
+ }
+}
+
+void LLHTTPAssetStorage::clearTempAssetData()
+{
+ llinfos << "TAT: Clearing temp asset data map" << llendl;
+ mTempAssets.clear();
+}
+
diff --git a/indra/llmessage/llhttpassetstorage.h b/indra/llmessage/llhttpassetstorage.h
new file mode 100644
index 0000000000..a6ba5c795c
--- /dev/null
+++ b/indra/llmessage/llhttpassetstorage.h
@@ -0,0 +1,116 @@
+/**
+ * @file llhttpassetstorage.h
+ * @brief Class for loading asset data to/from an external source over http.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLHTTPASSETSTORAGE_H
+#define LLHTTPASSETSTORAGE_H
+
+#include "llassetstorage.h"
+#include "curl/curl.h"
+
+class LLVFile;
+typedef void (*progress_callback)(void* userdata);
+
+struct LLTempAssetData;
+
+typedef std::map<LLUUID,LLTempAssetData> uuid_tempdata_map;
+
+class LLHTTPAssetStorage : public LLAssetStorage
+{
+public:
+ LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name);
+
+ LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name);
+
+
+ virtual ~LLHTTPAssetStorage();
+
+ virtual void storeAssetData(
+ const LLUUID& uuid,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false,
+ const LLUUID& requesting_agent_id = LLUUID::null);
+
+ virtual void storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority);
+
+ // Hack. One off curl download an URL to a file. Probably should be elsewhere.
+ // Only used by lldynamicstate. The API is broken, and should be replaced with
+ // a generic HTTP file fetch - Doug 9/25/06
+ S32 getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata);
+
+ void checkForTimeouts();
+
+ static size_t curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data);
+ static size_t curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data);
+ static size_t curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data);
+ static size_t nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data);
+
+ // Should only be used by the LLHTTPAssetRequest
+ void setPendingUpload() { mPendingUpload = TRUE; }
+ void setPendingLocalUpload() { mPendingLocalUpload = TRUE; }
+ void setPendingDownload() { mPendingDownload = TRUE; }
+ void clearPendingUpload() { mPendingUpload = FALSE; }
+ void clearPendingLocalUpload() { mPendingLocalUpload = FALSE; }
+ void clearPendingDownload() { mPendingDownload = FALSE; }
+
+ // Temp assets are stored on sim nodes, they have agent ID and location data associated with them.
+ virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name);
+ virtual BOOL hasTempAssetData(const LLUUID& texture_id) const;
+ virtual std::string getTempAssetHostName(const LLUUID& texture_id) const;
+ virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const;
+ virtual void removeTempAssetData(const LLUUID& asset_id);
+ virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id);
+
+ // Pass LLUUID::null for all
+ virtual void dumpTempAssetData(const LLUUID& avatar_id) const;
+ virtual void clearTempAssetData();
+
+protected:
+ void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
+ void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
+ void *user_data, BOOL duplicate, BOOL is_priority);
+
+private:
+ void _init(const char *web_host, const char *local_web_host, const char* host_name);
+
+ // This will return the correct base URI for any http asset request
+ std::string getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type);
+
+protected:
+ std::string mBaseURL;
+ std::string mLocalBaseURL;
+ std::string mHostName;
+
+ CURLM *mCurlMultiHandle;
+
+ BOOL mPendingDownload;
+ BOOL mPendingUpload;
+ BOOL mPendingLocalUpload;
+
+ uuid_tempdata_map mTempAssets;
+};
+
+#endif
diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp
new file mode 100644
index 0000000000..38dee26723
--- /dev/null
+++ b/indra/llmessage/llhttpclient.cpp
@@ -0,0 +1,278 @@
+/**
+ * @file llhttpclient.cpp
+ * @brief Implementation of classes for making HTTP requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llassetstorage.h"
+#include "llhttpclient.h"
+#include "lliopipe.h"
+#include "llurlrequest.h"
+#include "llbufferstream.h"
+#include "llsdserialize.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+static const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
+
+static std::string gCABundle;
+
+
+
+
+LLHTTPClient::Responder::Responder()
+ : mReferenceCount(0)
+{
+}
+
+LLHTTPClient::Responder::~Responder()
+{
+}
+
+// virtual
+void LLHTTPClient::Responder::error(U32 status, const std::string& reason)
+{
+ llinfos << "LLHTTPClient::Responder::error "
+ << status << ": " << reason << llendl;
+}
+
+// virtual
+void LLHTTPClient::Responder::result(const LLSD& content)
+{
+}
+
+// virtual
+void LLHTTPClient::Responder::completed(U32 status, const std::string& reason, const LLSD& content)
+{
+ if (200 <= status && status < 300)
+ {
+ result(content);
+ }
+ else
+ {
+ error(status, reason);
+ }
+}
+
+
+namespace
+{
+ class LLHTTPClientURLAdaptor : public LLURLRequestComplete
+ {
+ public:
+ LLHTTPClientURLAdaptor(LLHTTPClient::ResponderPtr responder)
+ : mResponder(responder),
+ mStatus(499), mReason("LLURLRequest complete w/no status")
+ {
+ }
+
+ ~LLHTTPClientURLAdaptor()
+ {
+ }
+
+ virtual void httpStatus(U32 status, const std::string& reason)
+ {
+ mStatus = status;
+ mReason = reason;
+ }
+
+ virtual void complete(const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer)
+ {
+ LLBufferStream istr(channels, buffer.get());
+ LLSD content;
+
+ if (200 <= mStatus && mStatus < 300)
+ {
+ LLSDSerialize::fromXML(content, istr);
+ }
+
+ if (mResponder.get())
+ {
+ mResponder->completed(mStatus, mReason, content);
+ }
+ }
+
+ private:
+ LLHTTPClient::ResponderPtr mResponder;
+ U32 mStatus;
+ std::string mReason;
+ };
+
+ class Injector : public LLIOPipe
+ {
+ public:
+ virtual const char* contentType() = 0;
+ };
+
+ class LLSDInjector : public Injector
+ {
+ public:
+ LLSDInjector(const LLSD& sd) : mSD(sd) {}
+ virtual ~LLSDInjector() {}
+
+ const char* contentType() { return "application/xml"; }
+
+ virtual EStatus process_impl(const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
+ {
+ LLBufferStream ostream(channels, buffer.get());
+ LLSDSerialize::toXML(mSD, ostream);
+ eos = true;
+ return STATUS_DONE;
+ }
+
+ const LLSD mSD;
+ };
+
+ class FileInjector : public Injector
+ {
+ public:
+ FileInjector(const std::string& filename) : mFilename(filename) {}
+ virtual ~FileInjector() {}
+
+ const char* contentType() { return "application/octet-stream"; }
+
+ virtual EStatus process_impl(const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
+ {
+ LLBufferStream ostream(channels, buffer.get());
+
+ llifstream fstream(mFilename.c_str(), std::iostream::binary | std::iostream::out);
+ fstream.seekg(0, std::ios::end);
+ U32 fileSize = fstream.tellg();
+ fstream.seekg(0, std::ios::beg);
+ char* fileBuffer;
+ fileBuffer = new char [fileSize];
+ fstream.read(fileBuffer, fileSize);
+ ostream.write(fileBuffer, fileSize);
+ fstream.close();
+ eos = true;
+ return STATUS_DONE;
+ }
+
+ const std::string mFilename;
+ };
+
+ class VFileInjector : public Injector
+ {
+ public:
+ VFileInjector(const LLUUID& uuid, LLAssetType::EType asset_type) : mUUID(uuid), mAssetType(asset_type) {}
+ virtual ~VFileInjector() {}
+
+ const char* contentType() { return "application/octet-stream"; }
+
+ virtual EStatus process_impl(const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
+ {
+ LLBufferStream ostream(channels, buffer.get());
+
+ LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ);
+ S32 fileSize = vfile.getSize();
+ U8* fileBuffer;
+ fileBuffer = new U8 [fileSize];
+ vfile.read(fileBuffer, fileSize);
+ ostream.write((char*)fileBuffer, fileSize);
+ eos = true;
+ return STATUS_DONE;
+ }
+
+ const LLUUID mUUID;
+ LLAssetType::EType mAssetType;
+ };
+
+
+ LLPumpIO* theClientPump = NULL;
+}
+
+static void request(const std::string& url, LLURLRequest::ERequestAction method,
+ Injector* body_injector, LLHTTPClient::ResponderPtr responder)
+{
+ if (!LLHTTPClient::hasPump())
+ {
+ responder->completed(U32_MAX, "No pump", LLSD());
+ return;
+ }
+
+ LLPumpIO::chain_t chain;
+
+ LLURLRequest *req = new LLURLRequest(method, url);
+ req->requestEncoding("");
+ if (!gCABundle.empty())
+ {
+ req->checkRootCertificate(true, gCABundle.c_str());
+ }
+ req->setCallback(new LLHTTPClientURLAdaptor(responder));
+
+ if (method == LLURLRequest::HTTP_PUT || method == LLURLRequest::HTTP_POST)
+ {
+ req->addHeader(llformat("Content-Type: %s", body_injector->contentType()).c_str());
+ chain.push_back(LLIOPipe::ptr_t(body_injector));
+ }
+ chain.push_back(LLIOPipe::ptr_t(req));
+
+ theClientPump->addChain(chain, HTTP_REQUEST_EXPIRY_SECS);
+}
+
+void LLHTTPClient::get(const std::string& url, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_GET, NULL, responder);
+}
+
+void LLHTTPClient::put(const std::string& url, const LLSD& body, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder);
+}
+
+void LLHTTPClient::post(const std::string& url, const LLSD& body, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_POST, new LLSDInjector(body), responder);
+}
+
+#if 1
+void LLHTTPClient::postFile(const std::string& url, const std::string& filename, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_POST, new FileInjector(filename), responder);
+}
+
+void LLHTTPClient::postFile(const std::string& url, const LLUUID& uuid,
+ LLAssetType::EType asset_type, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_POST, new VFileInjector(uuid, asset_type), responder);
+}
+#endif
+
+void LLHTTPClient::setPump(LLPumpIO& pump)
+{
+ theClientPump = &pump;
+}
+
+bool LLHTTPClient::hasPump()
+{
+ return theClientPump != NULL;
+}
+
+void LLHTTPClient::setCABundle(const std::string& caBundle)
+{
+ gCABundle = caBundle;
+}
+
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLHTTPClient::Responder* p)
+ {
+ ++p->mReferenceCount;
+ }
+
+ void intrusive_ptr_release(LLHTTPClient::Responder* p)
+ {
+ if(0 == --p->mReferenceCount)
+ {
+ delete p;
+ }
+ }
+};
+
diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h
new file mode 100644
index 0000000000..563450f07e
--- /dev/null
+++ b/indra/llmessage/llhttpclient.h
@@ -0,0 +1,83 @@
+/**
+ * @file llhttpclient.h
+ * @brief Declaration of classes for making HTTP client requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHTTPCLIENT_H
+#define LL_LLHTTPCLIENT_H
+
+/**
+ * These classes represent the HTTP client framework.
+ */
+
+#include <string>
+
+#include <boost/intrusive_ptr.hpp>
+#include "llassetstorage.h"
+
+class LLPumpIO;
+class LLSD;
+
+
+class LLHTTPClient
+{
+public:
+ class Responder
+ {
+ public:
+ Responder();
+ virtual ~Responder();
+
+ virtual void error(U32 status, const std::string& reason); // called with bad status codes
+
+ virtual void result(const LLSD& content);
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content);
+ /**< The default implemetnation calls
+ either:
+ * result(), or
+ * error()
+ */
+
+ public: /* but not really -- don't touch this */
+ U32 mReferenceCount;
+ };
+
+ typedef boost::intrusive_ptr<Responder> ResponderPtr;
+
+ static void get(const std::string& url, ResponderPtr);
+ static void put(const std::string& url, const LLSD& body, ResponderPtr);
+ ///< non-blocking
+ static void post(const std::string& url, const LLSD& body, ResponderPtr);
+ static void postFile(const std::string& url, const std::string& filename, ResponderPtr);
+ static void postFile(const std::string& url, const LLUUID& uuid,
+ LLAssetType::EType asset_type, ResponderPtr responder);
+
+ static void del(const std::string& url, ResponderPtr);
+ ///< sends a DELETE method, but we can't call it delete in c++
+
+
+ static void setPump(LLPumpIO& pump);
+ ///< must be called before any of the above calls are made
+ static bool hasPump();
+ ///< for testing
+
+ static void setCABundle(const std::string& caBundle);
+ ///< use this root CA bundle when checking SSL connections
+ ///< defaults to the standard system root CA bundle
+ ///< @see LLURLRequest::checkRootCertificate()
+};
+
+
+
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLHTTPClient::Responder* p);
+ void intrusive_ptr_release(LLHTTPClient::Responder* p);
+};
+
+
+#endif // LL_LLHTTPCLIENT_H
diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp
new file mode 100644
index 0000000000..e7d441b22c
--- /dev/null
+++ b/indra/llmessage/llhttpnode.cpp
@@ -0,0 +1,449 @@
+/**
+ * @file llhttpnode.cpp
+ * @brief Implementation of classes for generic HTTP/LSL/REST handling.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llhttpnode.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "llstl.h"
+
+static const std::string CONTEXT_REQUEST("request");
+static const std::string CONTEXT_WILDCARD("wildcard");
+
+/**
+ * LLHTTPNode
+ */
+
+class LLHTTPNode::Impl
+{
+public:
+ typedef std::map<std::string, LLHTTPNode*> ChildMap;
+
+ ChildMap mNamedChildren;
+ LLHTTPNode* mWildcardChild;
+ std::string mWildcardName;
+ std::string mWildcardKey;
+ LLHTTPNode* mParentNode;
+
+ Impl() : mWildcardChild(NULL), mParentNode(NULL) { }
+
+ LLHTTPNode* findNamedChild(const std::string& name) const;
+};
+
+
+LLHTTPNode* LLHTTPNode::Impl::findNamedChild(const std::string& name) const
+{
+ LLHTTPNode* child = get_ptr_in_map(mNamedChildren, name);
+
+ if (!child && ((name[0] == '*') || (name == mWildcardName)))
+ {
+ child = mWildcardChild;
+ }
+
+ return child;
+}
+
+
+LLHTTPNode::LLHTTPNode()
+ : impl(* new Impl)
+{
+}
+
+// virtual
+LLHTTPNode::~LLHTTPNode()
+{
+ std::for_each(impl.mNamedChildren.begin(), impl.mNamedChildren.end(),
+ DeletePairedPointer());
+
+ delete impl.mWildcardChild;
+
+ delete &impl;
+}
+
+
+namespace {
+ class NotImplemented
+ {
+ };
+}
+
+// virtual
+LLSD LLHTTPNode::get() const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::put(const LLSD& input) const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::post(const LLSD& input) const
+{
+ throw NotImplemented();
+}
+
+
+// virtual
+void LLHTTPNode::get(LLHTTPNode::ResponsePtr response, const LLSD& context) const
+{
+ try
+ {
+ response->result(get());
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::put(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
+{
+ try
+ {
+ response->result(put(input));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
+{
+ try
+ {
+ response->result(post(input));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) const
+{
+ try
+ {
+ response->result(del(context));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+
+}
+
+// virtual
+LLSD LLHTTPNode::del() const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::del(const LLSD&) const
+{
+ return del();
+}
+
+
+// virtual
+LLHTTPNode* LLHTTPNode::getChild(const std::string& name, LLSD& context) const
+{
+ LLHTTPNode* namedChild = get_ptr_in_map(impl.mNamedChildren, name);
+ if (namedChild)
+ {
+ return namedChild;
+ }
+
+ if (impl.mWildcardChild
+ && impl.mWildcardChild->validate(name, context))
+ {
+ context[CONTEXT_REQUEST][CONTEXT_WILDCARD][impl.mWildcardKey] = name;
+ return impl.mWildcardChild;
+ }
+
+ return NULL;
+}
+
+
+// virtual
+bool LLHTTPNode::handles(const LLSD& remainder, LLSD& context) const
+{
+ return remainder.size() == 0;
+}
+
+// virtual
+bool LLHTTPNode::validate(const std::string& name, LLSD& context) const
+{
+ return false;
+}
+
+const LLHTTPNode* LLHTTPNode::traverse(
+ const std::string& path, LLSD& context) const
+{
+ typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(path, sep);
+ tokenizer::iterator iter = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ const LLHTTPNode* node = this;
+ for(; iter != end; ++iter)
+ {
+ LLHTTPNode* child = node->getChild(*iter, context);
+ if(!child)
+ {
+ lldebugs << "LLHTTPNode::traverse: Couldn't find '" << *iter << "'" << llendl;
+ break;
+ }
+ lldebugs << "LLHTTPNode::traverse: Found '" << *iter << "'" << llendl;
+
+ node = child;
+ }
+
+ LLSD& remainder = context[CONTEXT_REQUEST]["remainder"];
+ for(; iter != end; ++iter)
+ {
+ remainder.append(*iter);
+ }
+
+ return node->handles(remainder, context) ? node : NULL;
+}
+
+
+
+void LLHTTPNode::addNode(const std::string& path, LLHTTPNode* nodeToAdd)
+{
+ typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(path, sep);
+ tokenizer::iterator iter = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ LLHTTPNode* node = this;
+ for(; iter != end; ++iter)
+ {
+ LLHTTPNode* child = node->impl.findNamedChild(*iter);
+ if (!child) { break; }
+ node = child;
+ }
+
+ if (iter == end)
+ {
+ llwarns << "LLHTTPNode::addNode: already a node that handles "
+ << path << llendl;
+ return;
+ }
+
+ while (true)
+ {
+ std::string pathPart = *iter;
+
+ ++iter;
+ bool lastOne = iter == end;
+
+ LLHTTPNode* nextNode = lastOne ? nodeToAdd : new LLHTTPNode();
+
+ switch (pathPart[0])
+ {
+ case '<':
+ // *NOTE: This should really validate that it is of
+ // the proper form: <wildcardkey> so that the substr()
+ // generates the correct key name.
+ node->impl.mWildcardChild = nextNode;
+ node->impl.mWildcardName = pathPart;
+ if(node->impl.mWildcardKey.empty())
+ {
+ node->impl.mWildcardKey = pathPart.substr(
+ 1,
+ pathPart.size() - 2);
+ }
+ break;
+ case '*':
+ node->impl.mWildcardChild = nextNode;
+ if(node->impl.mWildcardName.empty())
+ {
+ node->impl.mWildcardName = pathPart;
+ }
+ break;
+
+ default:
+ node->impl.mNamedChildren[pathPart] = nextNode;
+ }
+ nextNode->impl.mParentNode = node;
+
+ if (lastOne) break;
+ node = nextNode;
+ }
+}
+
+static void append_node_paths(LLSD& result,
+ const std::string& name, const LLHTTPNode* node)
+{
+ result.append(name);
+
+ LLSD paths = node->allNodePaths();
+ LLSD::array_const_iterator i = paths.beginArray();
+ LLSD::array_const_iterator end = paths.endArray();
+
+ for (; i != end; ++i)
+ {
+ result.append(name + "/" + (*i).asString());
+ }
+}
+
+LLSD LLHTTPNode::allNodePaths() const
+{
+ LLSD result;
+
+ Impl::ChildMap::const_iterator i = impl.mNamedChildren.begin();
+ Impl::ChildMap::const_iterator end = impl.mNamedChildren.end();
+ for (; i != end; ++i)
+ {
+ append_node_paths(result, i->first, i->second);
+ }
+
+ if (impl.mWildcardChild)
+ {
+ append_node_paths(result, impl.mWildcardName, impl.mWildcardChild);
+ }
+
+ return result;
+}
+
+
+const LLHTTPNode* LLHTTPNode::rootNode() const
+{
+ const LLHTTPNode* node = this;
+
+ while (true)
+ {
+ const LLHTTPNode* next = node->impl.mParentNode;
+ if (!next)
+ {
+ return node;
+ }
+ node = next;
+ }
+}
+
+
+const LLHTTPNode* LLHTTPNode::findNode(const std::string& name) const
+{
+ return impl.findNamedChild(name);
+}
+
+LLHTTPNode::Response::~Response()
+{
+}
+
+void LLHTTPNode::Response::status(S32 code)
+{
+ status(code, "Unknown Error");
+}
+
+void LLHTTPNode::Response::notFound(const std::string& message)
+{
+ status(404, message);
+}
+
+void LLHTTPNode::Response::notFound()
+{
+ status(404, "Not Found");
+}
+
+void LLHTTPNode::Response::methodNotAllowed()
+{
+ status(405, "Method Not Allowed");
+}
+
+void LLHTTPNode::describe(Description& desc) const
+{
+ desc.shortInfo("unknown service (missing describe() method)");
+}
+
+
+const LLChainIOFactory* LLHTTPNode::getProtocolHandler() const
+{
+ return NULL;
+}
+
+
+
+namespace
+{
+ typedef std::map<std::string, LLHTTPRegistrar::NodeFactory*> FactoryMap;
+
+ FactoryMap& factoryMap()
+ {
+ static FactoryMap theMap;
+ return theMap;
+ }
+}
+
+LLHTTPRegistrar::NodeFactory::~NodeFactory() { }
+
+void LLHTTPRegistrar::registerFactory(
+ const std::string& path, NodeFactory& factory)
+{
+ factoryMap()[path] = &factory;
+}
+
+void LLHTTPRegistrar::buildAllServices(LLHTTPNode& root)
+{
+ const FactoryMap& map = factoryMap();
+
+ FactoryMap::const_iterator i = map.begin();
+ FactoryMap::const_iterator end = map.end();
+ for (; i != end; ++i)
+ {
+ llinfos << "LLHTTPRegistrar::buildAllServices adding node for path "
+ << i->first << llendl;
+
+ root.addNode(i->first, i->second->build());
+ }
+}
+
+LLPointer<LLSimpleResponse> LLSimpleResponse::create()
+{
+ return new LLSimpleResponse();
+}
+
+LLSimpleResponse::~LLSimpleResponse()
+{
+}
+
+void LLSimpleResponse::result(const LLSD& result)
+{
+ status(200, "OK");
+}
+
+void LLSimpleResponse::status(S32 code, const std::string& message)
+{
+ mCode = code;
+ mMessage = message;
+}
+
+void LLSimpleResponse::print(std::ostream& out) const
+{
+ out << mCode << " " << mMessage;
+}
+
+
+std::ostream& operator<<(std::ostream& out, const LLSimpleResponse& resp)
+{
+ resp.print(out);
+ return out;
+}
diff --git a/indra/llmessage/llhttpnode.h b/indra/llmessage/llhttpnode.h
new file mode 100644
index 0000000000..1e799c18b9
--- /dev/null
+++ b/indra/llmessage/llhttpnode.h
@@ -0,0 +1,306 @@
+/**
+ * @file llhttpnode.h
+ * @brief Declaration of classes for generic HTTP/LSL/REST handling.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHTTPNODE_H
+#define LL_LLHTTPNODE_H
+
+#include "llmemory.h"
+#include "llsd.h"
+
+class LLChainIOFactory;
+
+
+/**
+ * These classes represent the HTTP framework: The URL tree, and the LLSD
+ * REST interface that such nodes implement.
+ *
+ * To implement a service, in most cases, subclass LLHTTPNode, implement
+ * get() or post(), and create a global instance of LLHTTPRegistration<>.
+ * This can all be done in a .cpp file, with no publically declared parts.
+ *
+ * To implement a server see lliohttpserver.h
+ * @see LLHTTPWireServer
+ */
+
+/**
+ * @class LLHTTPNode
+ * @brief Base class which handles url traversal, response routing
+ * and support for standard LLSD services
+ *
+ * Users of the HTTP responder will typically derive a class from this
+ * one, implement the get(), put() and/or post() methods, and then
+ * use LLHTTPRegistration to insert it into the URL tree.
+ *
+ * The default implementation handles servicing the request and creating
+ * the pipe fittings needed to read the headers, manage them, convert
+ * to and from LLSD, etc.
+ */
+class LLHTTPNode
+{
+public:
+ LLHTTPNode();
+ virtual ~LLHTTPNode();
+
+ /** @name Responses
+ Most subclasses override one or more of these methods to provide
+ the service. By default, the rest of the LLHTTPNode architecture
+ will handle requests, create the needed LLIOPump, parse the input
+ to LLSD, and format the LLSD result to the output.
+
+ The default implementation of each of these is to call
+ response->methodNotAllowed(); The "simple" versions can be
+ overridden instead in those cases where the service can return
+ an immediately computed response.
+ */
+ //@{
+public:
+ virtual LLSD get() const;
+ virtual LLSD put(const LLSD& input) const;
+ virtual LLSD post(const LLSD& input) const;
+
+ virtual LLSD del() const;
+ virtual LLSD del(const LLSD& context) const;
+
+ class Response : public LLRefCount
+ {
+ public:
+ virtual ~Response();
+
+ virtual void result(const LLSD&) = 0;
+ virtual void status(S32 code, const std::string& message) = 0;
+
+ void status(S32 code);
+ void notFound(const std::string& message);
+ void notFound();
+ void methodNotAllowed();
+ };
+
+ typedef LLPointer<Response> ResponsePtr;
+
+ virtual void get(ResponsePtr, const LLSD& context) const;
+ virtual void put(ResponsePtr, const LLSD& context, const LLSD& input) const;
+ virtual void post(ResponsePtr, const LLSD& context, const LLSD& input) const;
+ virtual void del(ResponsePtr, const LLSD& context) const;
+ //@}
+
+
+ /** @name URL traversal
+ The tree is traversed by calling getChild() with successive
+ path components, on successive results. When getChild() returns
+ null, or there are no more components, the last child responds to
+ the request.
+
+ The default behavior is generally correct, though wildcard nodes
+ will want to implement validate().
+ */
+ //@{
+public:
+ virtual LLHTTPNode* getChild(const std::string& name, LLSD& context) const;
+ /**< returns a child node, if any, at the given name
+ default looks at children and wildcard child (see below)
+ */
+
+ virtual bool handles(const LLSD& remainder, LLSD& context) const;
+ /**< return true if this node can service the remaining components;
+ default returns true if there are no remaining components
+ */
+
+ virtual bool validate(const std::string& name, LLSD& context) const;
+ /**< called only on wildcard nodes, to check if they will handle
+ the name; default is false; overrides will want to check
+ name, and return true if the name will construct to a valid url.
+ For convenience, the <code>getChild()</code> method above will
+ automatically insert the name in
+ context["request"]["wildcard"][key] if this method returns true.
+ For example, the node "agent/<agent_id>/detail" will set
+ context["request"]["wildcard"]["agent_id"] eqaul to the value
+ found during traversal.
+ */
+
+ const LLHTTPNode* traverse(const std::string& path, LLSD& context) const;
+ /**< find a node, if any, that can service this path
+ set up context["request"] information
+ */
+ //@}
+
+ /** @name Child Nodes
+ The standard node can have any number of child nodes under
+ fixed names, and optionally one "wildcard" node that can
+ handle all other names.
+
+ Usually, child nodes are add through LLHTTPRegistration, not
+ by calling this interface directly.
+
+ The added node will be now owned by the parent node.
+ */
+ //@{
+
+ virtual void addNode(const std::string& path, LLHTTPNode* nodeToAdd);
+
+ LLSD allNodePaths() const;
+ ///< Returns an arrary of node paths at and under this node
+
+ const LLHTTPNode* rootNode() const;
+ const LLHTTPNode* findNode(const std::string& name) const;
+
+ //@}
+
+ /* @name Description system
+ The Description object contains information about a service.
+ All subclasses of LLHTTPNode should override description() and use
+ the methods of the Description class to set the various properties.
+ */
+ //@{
+ class Description
+ {
+ public:
+ void shortInfo(const std::string& s){ mInfo["description"] = s; }
+ void longInfo(const std::string& s) { mInfo["details"] = s; }
+
+ // Call this method when the service supports the specified verb.
+ void getAPI() { mInfo["api"].append("GET"); }
+ void putAPI() { mInfo["api"].append("PUT"); }
+ void postAPI() { mInfo["api"].append("POST"); }
+ void delAPI() { mInfo["api"].append("DELETE"); }
+
+ void input(const std::string& s) { mInfo["input"] = s; }
+ void output(const std::string& s) { mInfo["output"] = s; }
+ void source(const char* f, int l) { mInfo["__file__"] = f;
+ mInfo["__line__"] = l; }
+
+ LLSD getInfo() const { return mInfo; }
+
+ private:
+ LLSD mInfo;
+ };
+
+ virtual void describe(Description&) const;
+
+ //@}
+
+
+ virtual const LLChainIOFactory* getProtocolHandler() const;
+ /**< Return a factory object for handling wire protocols.
+ * The base class returns NULL, as it doesn't know about
+ * wire protocols at all. This is okay for most nodes
+ * as LLIOHTTPServer is smart enough to use a default
+ * wire protocol for HTTP for such nodes. Specialized
+ * subclasses that handle things like XML-RPC will want
+ * to implement this. (See LLXMLSDRPCServerFactory.)
+ */
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+
+class LLSimpleResponse : public LLHTTPNode::Response
+{
+public:
+ static LLPointer<LLSimpleResponse> create();
+ ~LLSimpleResponse();
+
+ void result(const LLSD& result);
+ void status(S32 code, const std::string& message);
+
+ void print(std::ostream& out) const;
+
+ S32 mCode;
+ std::string mMessage;
+
+private:
+ LLSimpleResponse() {;} // Must be accessed through LLPointer.
+};
+
+std::ostream& operator<<(std::ostream& out, const LLSimpleResponse& resp);
+
+
+
+/**
+ * @name Automatic LLHTTPNode registration
+ *
+ * To register a node type at a particular url path, construct a global instance
+ * of LLHTTPRegistration:
+ *
+ * LLHTTPRegistration<LLMyNodeType> gHTTPServiceAlphaBeta("/alpha/beta");
+ *
+ * (Note the naming convention carefully.) This object must be global and not
+ * static. However, it needn't be declared in your .h file. It can exist
+ * solely in the .cpp file. The same is true of your subclass of LLHTTPNode:
+ * it can be declared and defined wholly within the .cpp file.
+ *
+ * When constructing a web server, use LLHTTPRegistrar to add all the registered
+ * nodes to the url tree:
+ *
+ * LLHTTPRegistrar::buidlAllServices(mRootNode);
+ */
+//@{
+
+class LLHTTPRegistrar
+{
+public:
+ class NodeFactory
+ {
+ public:
+ virtual ~NodeFactory();
+ virtual LLHTTPNode* build() const = 0;
+ };
+
+ static void buildAllServices(LLHTTPNode& root);
+
+ static void registerFactory(const std::string& path, NodeFactory& factory);
+ ///< construct an LLHTTPRegistration below to call this
+};
+
+template < class NodeType >
+class LLHTTPRegistration
+{
+public:
+ LLHTTPRegistration(const std::string& path)
+ {
+ LLHTTPRegistrar::registerFactory(path, mFactory);
+ }
+
+private:
+ class ThisNodeFactory : public LLHTTPRegistrar::NodeFactory
+ {
+ public:
+ virtual LLHTTPNode* build() const { return new NodeType; }
+ };
+
+ ThisNodeFactory mFactory;
+};
+
+template < class NodeType>
+class LLHTTPParamRegistration
+{
+public:
+ LLHTTPParamRegistration(const std::string& path, LLSD params) :
+ mFactory(params)
+ {
+ LLHTTPRegistrar::registerFactory(path, mFactory);
+ }
+
+private:
+ class ThisNodeFactory : public LLHTTPRegistrar::NodeFactory
+ {
+ public:
+ ThisNodeFactory(LLSD params) : mParams(params) {}
+ virtual LLHTTPNode* build() const { return new NodeType(mParams); }
+ private:
+ LLSD mParams;
+ };
+
+ ThisNodeFactory mFactory;
+};
+
+//@}
+
+#endif // LL_LLHTTPNODE_H
diff --git a/indra/llmessage/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp
new file mode 100644
index 0000000000..2bfa82a0ce
--- /dev/null
+++ b/indra/llmessage/llinstantmessage.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llinstantmessage.cpp
+ * @author Phoenix
+ * @date 2005-08-29
+ * @brief Constants and functions used in IM.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lldbstrings.h"
+#include "llinstantmessage.h"
+#include "llhost.h"
+#include "lluuid.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llmemory.h"
+#include "message.h"
+
+#include "message.h"
+
+const U8 IM_ONLINE = 0;
+const U8 IM_OFFLINE = 1;
+
+const S32 VOTE_YES = 1;
+const S32 VOTE_NO = 0;
+const S32 VOTE_ABSTAIN = -1;
+
+const S32 VOTE_MAJORITY = 0;
+const S32 VOTE_SUPER_MAJORITY = 1;
+const S32 VOTE_UNANIMOUS = 2;
+
+const char EMPTY_BINARY_BUCKET[] = "";
+const S32 EMPTY_BINARY_BUCKET_SIZE = 1;
+const U32 NO_TIMESTAMP = 0;
+const char SYSTEM_FROM[] = "Second Life";
+const S32 IM_TTL = 1;
+
+
+/**
+ * LLIMInfo
+ */
+LLIMInfo::LLIMInfo() :
+ mParentEstateID(0),
+ mOffline(0),
+ mViewerThinksToIsOnline(false),
+ mTimeStamp(0),
+ mSource(IM_FROM_SIM),
+ mTTL(IM_TTL)
+{
+}
+
+LLIMInfo::LLIMInfo(
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& to_id,
+ EInstantMessage im_type,
+ const std::string& name,
+ const std::string& message,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ LLSD data,
+ U8 offline,
+ U32 timestamp,
+ EIMSource source,
+ S32 ttl) :
+ mFromID(from_id),
+ mFromGroup(from_group),
+ mToID(to_id),
+ mParentEstateID(0),
+ mRegionID(region_id),
+ mPosition(position),
+ mOffline(offline),
+ mViewerThinksToIsOnline(false),
+ mIMType(im_type),
+ mID(id),
+ mTimeStamp(timestamp),
+ mName(name),
+ mMessage(message),
+ mData(data),
+ mSource(source),
+ mTTL(ttl)
+{
+}
+
+LLIMInfo::LLIMInfo(LLMessageSystem* msg, EIMSource source, S32 ttl) :
+ mViewerThinksToIsOnline(false),
+ mSource(source),
+ mTTL(ttl)
+{
+ unpackMessageBlock(msg);
+}
+
+LLIMInfo::~LLIMInfo()
+{
+}
+
+void LLIMInfo::packInstantMessage(LLMessageSystem* msg) const
+{
+ lldebugs << "LLIMInfo::packInstantMessage()" << llendl;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ packMessageBlock(msg);
+}
+
+void LLIMInfo::packMessageBlock(LLMessageSystem* msg) const
+{
+ // Construct binary bucket
+ std::vector<U8> bucket;
+ if (mData.has("binary_bucket"))
+ {
+ bucket = mData["binary_bucket"].asBinary();
+ }
+ pack_instant_message_block(
+ msg,
+ mFromID,
+ mFromGroup,
+ LLUUID::null,
+ mToID,
+ mName.c_str(),
+ mMessage.c_str(),
+ mOffline,
+ mIMType,
+ mID,
+ mParentEstateID,
+ mRegionID,
+ mPosition,
+ mTimeStamp,
+ &bucket[0],
+ bucket.size());
+}
+
+void pack_instant_message(
+ LLMessageSystem* msg,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline,
+ EInstantMessage dialog,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ U32 timestamp,
+ const U8* binary_bucket,
+ S32 binary_bucket_size)
+{
+ lldebugs << "pack_instant_message()" << llendl;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ pack_instant_message_block(
+ msg,
+ from_id,
+ from_group,
+ session_id,
+ to_id,
+ name,
+ message,
+ offline,
+ dialog,
+ id,
+ parent_estate_id,
+ region_id,
+ position,
+ timestamp,
+ binary_bucket,
+ binary_bucket_size);
+}
+
+void pack_instant_message_block(
+ LLMessageSystem* msg,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline,
+ EInstantMessage dialog,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ U32 timestamp,
+ const U8* binary_bucket,
+ S32 binary_bucket_size)
+{
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, from_id);
+ msg->addUUIDFast(_PREHASH_SessionID, session_id);
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, from_group);
+ msg->addUUIDFast(_PREHASH_ToAgentID, to_id);
+ msg->addU32Fast(_PREHASH_ParentEstateID, parent_estate_id);
+ msg->addUUIDFast(_PREHASH_RegionID, region_id);
+ msg->addVector3Fast(_PREHASH_Position, position);
+ msg->addU8Fast(_PREHASH_Offline, offline);
+ msg->addU8Fast(_PREHASH_Dialog, (U8) dialog);
+ msg->addUUIDFast(_PREHASH_ID, id);
+ msg->addU32Fast(_PREHASH_Timestamp, timestamp);
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ S32 bytes_left = MTUBYTES;
+ if(message)
+ {
+ char buffer[MTUBYTES];
+ bytes_left -= snprintf(buffer, MTUBYTES, "%s", message);
+ bytes_left = llmax(0, bytes_left);
+ msg->addStringFast(_PREHASH_Message, buffer);
+ }
+ else
+ {
+ msg->addStringFast(_PREHASH_Message, NULL);
+ }
+ const U8* bb;
+ if(binary_bucket)
+ {
+ bb = binary_bucket;
+ binary_bucket_size = llmin(bytes_left, binary_bucket_size);
+ }
+ else
+ {
+ bb = (const U8*)EMPTY_BINARY_BUCKET;
+ binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE;
+ }
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, bb, binary_bucket_size);
+}
+
+void LLIMInfo::unpackMessageBlock(LLMessageSystem* msg)
+{
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, mFromID);
+ msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, mFromGroup);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, mToID);
+ msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, mParentEstateID);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, mRegionID);
+ msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, mPosition);
+ msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Offline, mOffline);
+ U8 dialog;
+ msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Dialog, dialog);
+ mIMType = (EInstantMessage) dialog;
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, mID);
+ msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_Timestamp, mTimeStamp);
+ char name[DB_FULL_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, DB_FULL_NAME_BUF_SIZE, name);
+ mName.assign(name);
+
+ char message[DB_IM_MSG_BUF_SIZE];
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, DB_IM_MSG_BUF_SIZE, message);
+ mMessage.assign(message);
+
+ S32 binary_bucket_size = llmin(
+ MTUBYTES,
+ msg->getSizeFast(
+ _PREHASH_MessageBlock,
+ _PREHASH_BinaryBucket));
+ if(binary_bucket_size)
+ {
+ std::vector<U8> bucket;
+ bucket.resize(binary_bucket_size);
+
+ msg->getBinaryDataFast(
+ _PREHASH_MessageBlock,
+ _PREHASH_BinaryBucket,
+ &bucket[0],
+ 0,
+ 0,
+ binary_bucket_size);
+ mData["binary_bucket"] = bucket;
+ }
+ else
+ {
+ mData.clear();
+ }
+}
+
+LLPointer<LLIMInfo> LLIMInfo::clone()
+{
+ return new LLIMInfo(
+ mFromID,
+ mFromGroup,
+ mToID,
+ mIMType,
+ mName,
+ mMessage,
+ mID,
+ mParentEstateID,
+ mRegionID,
+ mPosition,
+ mData,
+ mOffline,
+ mTimeStamp,
+ mSource,
+ mTTL);
+}
+
diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h
new file mode 100644
index 0000000000..c8138cf491
--- /dev/null
+++ b/indra/llmessage/llinstantmessage.h
@@ -0,0 +1,308 @@
+/**
+ * @file llinstantmessage.h
+ * @brief Constants and declarations used by instant messages.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINSTANTMESSAGE_H
+#define LL_LLINSTANTMESSAGE_H
+
+#include "llhost.h"
+#include "lluuid.h"
+#include "llsd.h"
+#include "llmemory.h"
+#include "v3math.h"
+
+class LLMessageSystem;
+
+// The ImprovedInstantMessage only supports 8 bits in the "Dialog"
+// field, so don't go past the byte boundary
+enum EInstantMessage
+{
+ // default. ID is meaningless, nothing in the binary bucket.
+ IM_NOTHING_SPECIAL = 0,
+
+ // pops a messagebox with a single OK button
+ IM_MESSAGEBOX = 1,
+
+ // pops a countdown messagebox with a single OK button
+ // IM_MESSAGEBOX_COUNTDOWN = 2,
+
+ // You've been invited to join a group.
+ // ID is the group id.
+
+ // The binary bucket contains a null terminated string
+ // representation of the officer/member status and join cost for
+ // the invitee. (bug # 7672) The format is 1 byte for
+ // officer/member (O for officer, M for member), and as many bytes
+ // as necessary for cost.
+ IM_GROUP_INVITATION = 3,
+
+ // Inventory offer.
+ // ID is the transaction id
+ // Binary bucket is a list of inventory uuid and type.
+ IM_INVENTORY_OFFERED = 4,
+ IM_INVENTORY_ACCEPTED = 5,
+ IM_INVENTORY_DECLINED = 6,
+
+ // Group vote
+ // Name is name of person who called vote.
+ // ID is vote ID used for internal tracking
+ IM_GROUP_VOTE = 7,
+
+ // Group message
+ // This means that the message is meant for everyone in the
+ // agent's group. This will result in a database query to find all
+ // participants and start an im session.
+ IM_GROUP_MESSAGE_DEPRECATED = 8,
+
+ // Task inventory offer.
+ // ID is the transaction id
+ // Binary bucket is a (mostly) complete packed inventory item
+ IM_TASK_INVENTORY_OFFERED = 9,
+ IM_TASK_INVENTORY_ACCEPTED = 10,
+ IM_TASK_INVENTORY_DECLINED = 11,
+
+ // Copied as pending, type LL_NOTHING_SPECIAL, for new users
+ // used by offline tools
+ IM_NEW_USER_DEFAULT = 12,
+
+ //
+ // session based messaging - the way that people usually actually
+ // communicate with each other.
+ //
+
+ // Start a session, or add users to a session.
+ IM_SESSION_ADD = 13,
+
+ // Start a session, but don't prune offline users
+ IM_SESSION_OFFLINE_ADD = 14,
+
+ // start a session with your gruop
+ IM_SESSION_GROUP_START = 15,
+
+ // start a session without a calling card (finder or objects)
+ IM_SESSION_CARDLESS_START = 16,
+
+ // send a message to a session.
+ IM_SESSION_SEND = 17,
+
+ // leave a session
+ IM_SESSION_DROP = 18,
+
+ // an instant message from an object - for differentiation on the
+ // viewer, since you can't IM an object yet.
+ IM_FROM_TASK = 19,
+
+ // sent an IM to a busy user, this is the auto response
+ IM_BUSY_AUTO_RESPONSE = 20,
+
+ // Shows the message in the console and chat history
+ IM_CONSOLE_AND_CHAT_HISTORY = 21,
+
+ // IM Types used for luring your friends
+ IM_LURE_USER = 22,
+ IM_LURE_ACCEPTED = 23,
+ IM_LURE_DECLINED = 24,
+ IM_GODLIKE_LURE_USER = 25,
+ IM_YET_TO_BE_USED = 26,
+
+ // IM that notifie of a new group election.
+ // Name is name of person who called vote.
+ // ID is election ID used for internal tracking
+ IM_GROUP_ELECTION_DEPRECATED = 27,
+
+ // IM to tell the user to go to an URL. Put a text message in the
+ // message field, and put the url with a trailing \0 in the binary
+ // bucket.
+ IM_GOTO_URL = 28,
+
+ // IM for help from the GAURDIAN_ANGELS
+ // Binary bucket contains the name of the session.
+ IM_SESSION_911_START = 29,
+
+ // IM sent automatically on call for help,
+ // sends a lure to each Helper reached
+ IM_LURE_911 = 30,
+
+ // a message generated by a script which we don't want to
+ // be sent through e-mail. Similar to IM_FROM_TASK, but
+ // it is shown as an alert on the viewer.
+ IM_FROM_TASK_AS_ALERT = 31,
+
+ // IM from group officer to all group members.
+ IM_GROUP_NOTICE = 32,
+ IM_GROUP_NOTICE_INVENTORY_ACCEPTED = 33,
+ IM_GROUP_NOTICE_INVENTORY_DECLINED = 34,
+
+ IM_GROUP_INVITATION_ACCEPT = 35,
+ IM_GROUP_INVITATION_DECLINE = 36,
+
+ IM_GROUP_NOTICE_REQUESTED = 37,
+
+ IM_FRIENDSHIP_OFFERED = 38,
+ IM_FRIENDSHIP_ACCEPTED = 39,
+ IM_FRIENDSHIP_DECLINED = 40,
+
+ IM_TYPING_START = 41,
+ IM_TYPING_STOP = 42,
+
+ IM_COUNT
+};
+
+
+// Hooks for quickly hacking in experimental admin debug messages
+// without needing to recompile the viewer
+// *NOTE: This functionality has been moved to be a string based
+// operation so that we don't even have to do a full recompile. This
+// enumeration will be phased out soon.
+enum EGodlikeRequest
+{
+ GOD_WANTS_NOTHING,
+
+ // for requesting physics information about an object
+ GOD_WANTS_HAVOK_INFO,
+
+ // two unused requests that can be appropriated for debug
+ // purposes (no viewer recompile necessary)
+ GOD_WANTS_FOO,
+ GOD_WANTS_BAR,
+
+ // to dump simulator terrain data to terrain.raw file
+ GOD_WANTS_TERRAIN_SAVE,
+ // to load simulator terrain data from terrain.raw file
+ GOD_WANTS_TERRAIN_LOAD,
+
+ GOD_WANTS_TOGGLE_AVATAR_GEOMETRY, // HACK for testing new avatar geom
+
+ // real-time telehub operations
+ GOD_WANTS_TELEHUB_INFO,
+ GOD_WANTS_CONNECT_TELEHUB,
+ GOD_WANTS_DELETE_TELEHUB,
+ GOD_WANTS_ADD_TELEHUB_SPAWNPOINT,
+ GOD_WANTS_REMOVE_TELEHUB_SPAWNPOINT,
+
+};
+
+enum EIMSource
+{
+ IM_FROM_VIEWER,
+ IM_FROM_DATASERVER,
+ IM_FROM_SIM
+};
+
+extern const U8 IM_ONLINE;
+extern const U8 IM_OFFLINE;
+
+extern const S32 VOTE_YES;
+extern const S32 VOTE_NO;
+extern const S32 VOTE_ABSTAIN;
+
+extern const S32 VOTE_MAJORITY;
+extern const S32 VOTE_SUPER_MAJORITY;
+extern const S32 VOTE_UNANIMOUS;
+
+extern const char EMPTY_BINARY_BUCKET[];
+extern const S32 EMPTY_BINARY_BUCKET_SIZE;
+
+extern const U32 NO_TIMESTAMP;
+extern const char SYSTEM_FROM[];
+
+// Number of retry attempts on sending the im.
+extern const S32 IM_TTL;
+
+
+class LLIMInfo : public LLRefCount
+{
+protected:
+ LLIMInfo();
+ ~LLIMInfo();
+
+public:
+ LLIMInfo(LLMessageSystem* msg,
+ EIMSource source = IM_FROM_SIM,
+ S32 ttl = IM_TTL);
+
+ LLIMInfo(
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& to_id,
+ EInstantMessage im_type,
+ const std::string& name,
+ const std::string& message,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ LLSD data,
+ U8 offline,
+ U32 timestamp,
+ EIMSource source,
+ S32 ttl = IM_TTL);
+
+ void packInstantMessage(LLMessageSystem* msg) const;
+ void packMessageBlock(LLMessageSystem* msg) const;
+ void unpackMessageBlock(LLMessageSystem* msg);
+ LLPointer<LLIMInfo> clone();
+public:
+ LLUUID mFromID;
+ BOOL mFromGroup;
+ LLUUID mToID;
+ U32 mParentEstateID;
+ LLUUID mRegionID;
+ LLVector3 mPosition;
+ U8 mOffline;
+ bool mViewerThinksToIsOnline;
+ EInstantMessage mIMType;
+ LLUUID mID;
+ U32 mTimeStamp;
+ std::string mName;
+ std::string mMessage;
+ LLSD mData;
+
+ EIMSource mSource;
+ S32 mTTL;
+};
+
+
+void pack_instant_message(
+ LLMessageSystem* msgsystem,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline = IM_ONLINE,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null,
+ U32 parent_estate_id = 0,
+ const LLUUID& region_id = LLUUID::null,
+ const LLVector3& position = LLVector3::zero,
+ U32 timestamp = NO_TIMESTAMP,
+ const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET,
+ S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE);
+
+void pack_instant_message_block(
+ LLMessageSystem* msgsystem,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline = IM_ONLINE,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null,
+ U32 parent_estate_id = 0,
+ const LLUUID& region_id = LLUUID::null,
+ const LLVector3& position = LLVector3::zero,
+ U32 timestamp = NO_TIMESTAMP,
+ const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET,
+ S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE);
+
+
+#endif // LL_LLINSTANTMESSAGE_H
+
diff --git a/indra/llmessage/llinvite.h b/indra/llmessage/llinvite.h
new file mode 100644
index 0000000000..5c66abc1eb
--- /dev/null
+++ b/indra/llmessage/llinvite.h
@@ -0,0 +1,15 @@
+/**
+ * @file llinvite.h
+ * @brief Constants used for inviting users to join groups.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVITE_H
+#define LL_LLINVITE_H
+
+const S32 INVITE_LIST_STR_LEN = 324; // Would be larger, but we don't have much room in the CreateGroupRequest msg.
+const S32 INVITE_LIST_BUF_SIZE = 325;
+
+#endif // LL_LLINVITE_H
diff --git a/indra/llmessage/lliobuffer.cpp b/indra/llmessage/lliobuffer.cpp
new file mode 100644
index 0000000000..f5d1ebd595
--- /dev/null
+++ b/indra/llmessage/lliobuffer.cpp
@@ -0,0 +1,96 @@
+/**
+ * @file lliobuffer.cpp
+ * @author Phoenix
+ * @date 2005-05-04
+ * @brief Definition of buffer based implementations of IO Pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliobuffer.h"
+
+//
+// LLIOBuffer
+//
+LLIOBuffer::LLIOBuffer() :
+ mBuffer(NULL),
+ mBufferSize(0L),
+ mReadHead(NULL),
+ mWriteHead(NULL)
+{
+}
+
+LLIOBuffer::~LLIOBuffer()
+{
+ if(mBuffer)
+ {
+ delete[] mBuffer;
+ }
+}
+
+U8* LLIOBuffer::data() const
+{
+ return mBuffer;
+}
+
+S64 LLIOBuffer::size() const
+{
+ return mBufferSize;
+}
+
+U8* LLIOBuffer::current() const
+{
+ return mReadHead;
+}
+
+S64 LLIOBuffer::bytesLeft() const
+{
+ return mWriteHead - mReadHead;
+}
+
+void LLIOBuffer::clear()
+{
+ mReadHead = mBuffer;
+ mWriteHead = mBuffer;
+}
+
+LLIOPipe::EStatus LLIOBuffer::seek(LLIOBuffer::EHead head, S64 delta)
+{
+ LLIOPipe::EStatus status = STATUS_ERROR;
+ switch(head)
+ {
+ case READ:
+ if(((delta >= 0) && ((mReadHead + delta) <= mWriteHead))
+ || (delta < 0) && ((mReadHead + delta) >= mBuffer))
+ {
+ mReadHead += delta;
+ status = STATUS_OK;
+ }
+ break;
+ case WRITE:
+ if(((delta >= 0) && ((mWriteHead + delta) < (mBuffer + mBufferSize)))
+ || (delta < 0) && ((mWriteHead + delta) > mReadHead))
+ {
+ mWriteHead += delta;
+ status = STATUS_OK;
+ }
+ default:
+ break;
+ }
+ return status;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOBuffer::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ // no-op (I think)
+ llwarns << "You are using an LLIOBuffer which is deprecated." << llendl;
+ return STATUS_OK;
+}
diff --git a/indra/llmessage/lliobuffer.h b/indra/llmessage/lliobuffer.h
new file mode 100644
index 0000000000..770738f2dd
--- /dev/null
+++ b/indra/llmessage/lliobuffer.h
@@ -0,0 +1,117 @@
+/**
+ * @file lliobuffer.h
+ * @author Phoenix
+ * @date 2005-05-04
+ * @brief Declaration of buffers for use in IO Pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOBUFFER_H
+#define LL_LLIOBUFFER_H
+
+#include "lliopipe.h"
+
+/**
+ * @class LLIOBuffer
+ * @brief This class is an io class that represents an automtically
+ * resizing io buffer.
+ * @see LLIOPipe
+ *
+ * This class is currently impelemented quick and dirty, but should be
+ * correct. This class should be extended to have a more flexible
+ * (and capped) memory allocation and usage scheme. Eventually, I
+ * would like to have the ability to share this buffer between
+ * different objects.
+ */
+class LLIOBuffer : public LLIOPipe
+{
+public:
+ LLIOBuffer();
+ virtual ~LLIOBuffer();
+
+ /**
+ * @brief Return a raw pointer to the current data set.
+ *
+ * The pointer returned can be used for reading or even adjustment
+ * if you are a bit crazy up to size() bytes into memory.
+ * @return A potentially NULL pointer to the raw buffer data
+ */
+ U8* data() const;
+
+ /**
+ * @brief Return the size of the buffer
+ */
+ S64 size() const;
+
+ /**
+ * @brief Return a raw pointer to the current read position in the data.
+ *
+ * The pointer returned can be used for reading or even adjustment
+ * if you are a bit crazy up to bytesLeft() bytes into memory.
+ * @return A potentially NULL pointer to the buffer data starting
+ * at the read point
+ */
+ U8* current() const;
+
+ /**
+ * @brief Return the number of unprocessed bytes in buffer.
+ */
+ S64 bytesLeft() const;
+
+ /**
+ * @brief Move the buffer offsets back to the beginning.
+ *
+ * This method effectively clears what has been stored here,
+ * without mucking around with memory allocation.
+ */
+ void clear();
+
+ /**
+ * @brief Enumeration passed into the seek function
+ *
+ * The READ head is used for where to start processing data for
+ * the next link in the chain, while the WRITE head specifies
+ * where new data processed from the previous link in the chain
+ * will be written.
+ */
+ enum EHead
+ {
+ READ,
+ WRITE
+ };
+
+ /**
+ * @brief Seek to a place in the buffer
+ *
+ * @param head The READ or WRITE head.
+ * @param delta The offset from the current position to seek.
+ * @return The status of the operation. status >= if head moved.
+ */
+ EStatus seek(EHead head, S64 delta);
+
+public:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ U8* mBuffer;
+ S64 mBufferSize;
+ U8* mReadHead;
+ U8* mWriteHead;
+};
+
+#endif // LL_LLIOBUFFER_H
diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp
new file mode 100644
index 0000000000..c1a9bc442e
--- /dev/null
+++ b/indra/llmessage/lliohttpserver.cpp
@@ -0,0 +1,833 @@
+/**
+ * @file lliohttpserver.cpp
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Implementation of the http server classes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliohttpserver.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "llapr.h"
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llhttpnode.h"
+#include "lliopipe.h"
+#include "lliosocket.h"
+#include "llioutil.h"
+#include "llmemtype.h"
+#include "llmemorystream.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdserialize_xml.h"
+#include "llstl.h"
+
+static const char HTTP_VERSION_STR[] = "HTTP/1.0";
+static const std::string CONTEXT_REQUEST("request");
+static const std::string HTTP_VERB_GET("GET");
+static const std::string HTTP_VERB_PUT("PUT");
+static const std::string HTTP_VERB_POST("POST");
+static const std::string HTTP_VERB_DELETE("DELETE");
+
+
+class LLHTTPPipe : public LLIOPipe
+{
+public:
+ LLHTTPPipe(const LLHTTPNode& node)
+ : mNode(node), mResponse(NULL), mState(STATE_INVOKE), mChainLock(0), mStatusCode(0)
+ { }
+ virtual ~LLHTTPPipe()
+ {
+ if (mResponse.notNull())
+ {
+ mResponse->nullPipe();
+ }
+ }
+
+private:
+ // LLIOPipe API implementation.
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ LLIOPipe::buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+ const LLHTTPNode& mNode;
+
+ class Response : public LLHTTPNode::Response
+ {
+ public:
+
+ static LLPointer<Response> create(LLHTTPPipe* pipe);
+ virtual ~Response();
+
+ // from LLHTTPNode::Response
+ virtual void result(const LLSD&);
+ virtual void status(S32 code, const std::string& message);
+
+ void nullPipe();
+
+ private:
+ Response() {;} // Must be accessed through LLPointer.
+ LLHTTPPipe* mPipe;
+ };
+ friend class Response;
+
+ LLPointer<Response> mResponse;
+
+ enum State
+ {
+ STATE_INVOKE,
+ STATE_DELAYED,
+ STATE_LOCKED,
+ STATE_GOOD_RESULT,
+ STATE_STATUS_RESULT
+ };
+ State mState;
+
+ S32 mChainLock;
+ LLPumpIO* mLockedPump;
+
+ void lockChain(LLPumpIO*);
+ void unlockChain();
+
+ LLSD mGoodResult;
+ S32 mStatusCode;
+ std::string mStatusMessage;
+};
+
+LLIOPipe::EStatus LLHTTPPipe::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ lldebugs << "LLSDHTTPServer::process_impl" << llendl;
+
+ // Once we have all the data, We need to read the sd on
+ // the the in channel, and respond on the out channel
+
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ PUMP_DEBUG;
+ if (mState == STATE_INVOKE)
+ {
+ PUMP_DEBUG;
+ mState = STATE_DELAYED;
+ // assume deferred unless mResponse does otherwise
+ mResponse = Response::create(this);
+
+ // TODO: Babbage: Parameterize parser?
+ LLBufferStream istr(channels, buffer.get());
+
+ std::string verb = context[CONTEXT_REQUEST]["verb"];
+ if(verb == HTTP_VERB_GET)
+ {
+ mNode.get(LLHTTPNode::ResponsePtr(mResponse), context);
+ }
+ else if(verb == HTTP_VERB_PUT)
+ {
+ LLSD input;
+ LLSDSerialize::fromXML(input, istr);
+
+ mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input);
+ }
+ else if(verb == HTTP_VERB_POST)
+ {
+ LLSD input;
+ LLSDSerialize::fromXML(input, istr);
+
+ mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input);
+ }
+ else if(verb == HTTP_VERB_DELETE)
+ {
+ mNode.del(LLHTTPNode::ResponsePtr(mResponse), context);
+ }
+ else
+ {
+ mResponse->methodNotAllowed();
+ }
+
+ // Log Internal Server Errors
+ if(mStatusCode == 500)
+ {
+ llwarns << "LLHTTPPipe::process_impl:500:Internal Server Error"
+ << llendl;
+ }
+ }
+
+ PUMP_DEBUG;
+ switch (mState)
+ {
+ case STATE_DELAYED:
+ lockChain(pump);
+ mState = STATE_LOCKED;
+ return STATUS_BREAK;
+
+ case STATE_LOCKED:
+ // should never ever happen!
+ return STATUS_ERROR;
+
+ case STATE_GOOD_RESULT:
+ {
+ context["response"]["contentType"] = "application/xml";
+ LLBufferStream ostr(channels, buffer.get());
+ LLSDSerialize::toXML(mGoodResult, ostr);
+
+ return STATUS_DONE;
+ }
+
+ case STATE_STATUS_RESULT:
+ {
+ context["response"]["contentType"] = "text/plain";
+ context["response"]["statusCode"] = mStatusCode;
+ context["response"]["statusMessage"] = mStatusMessage;
+ LLBufferStream ostr(channels, buffer.get());
+ ostr << mStatusMessage << std::ends;
+
+ return STATUS_DONE;
+ }
+ default:
+ llwarns << "LLHTTPPipe::process_impl: unexpected state "
+ << mState << llendl;
+
+ return STATUS_BREAK;
+ }
+// PUMP_DEBUG; // unreachable
+}
+
+LLPointer<LLHTTPPipe::Response> LLHTTPPipe::Response::create(LLHTTPPipe* pipe)
+{
+ LLPointer<Response> result = new Response();
+ result->mPipe = pipe;
+ return result;
+}
+
+// virtual
+LLHTTPPipe::Response::~Response()
+{
+}
+
+void LLHTTPPipe::Response::nullPipe()
+{
+ mPipe = NULL;
+}
+
+// virtual
+void LLHTTPPipe::Response::result(const LLSD& r)
+{
+ if(! mPipe)
+ {
+ llwarns << "LLHTTPPipe::Response::result: NULL pipe" << llendl;
+ return;
+ }
+
+ mPipe->mStatusCode = 200;
+ mPipe->mStatusMessage = "OK";
+ mPipe->mGoodResult = r;
+ mPipe->mState = STATE_GOOD_RESULT;
+ mPipe->unlockChain();
+}
+
+// virtual
+void LLHTTPPipe::Response::status(S32 code, const std::string& message)
+{
+ if(! mPipe)
+ {
+ llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl;
+ return;
+ }
+
+ mPipe->mStatusCode = code;
+ mPipe->mStatusMessage = message;
+ mPipe->mState = STATE_STATUS_RESULT;
+ mPipe->unlockChain();
+}
+
+void LLHTTPPipe::lockChain(LLPumpIO* pump)
+{
+ if (mChainLock != 0) { return; }
+
+ mLockedPump = pump;
+ mChainLock = pump->setLock();
+}
+
+void LLHTTPPipe::unlockChain()
+{
+ if (mChainLock == 0) { return; }
+
+ mLockedPump->clearLock(mChainLock);
+ mLockedPump = NULL;
+ mChainLock = 0;
+}
+
+
+
+/**
+ * @class LLHTTPResponseHeader
+ * @brief Class which correctly builds HTTP headers on a pipe
+ * @see LLIOPipe
+ *
+ * An instance of this class can be placed in a chain where it will
+ * wait for an end of stream. Once it gets that, it will count the
+ * bytes on CHANNEL_OUT (or the size of the buffer in io pipe versions
+ * prior to 2) prepend that data to the request in an HTTP format, and
+ * supply all normal HTTP response headers.
+ */
+class LLHTTPResponseHeader : public LLIOPipe
+{
+public:
+ LLHTTPResponseHeader() {}
+ virtual ~LLHTTPResponseHeader() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ S32 mCode;
+};
+
+
+/**
+ * LLHTTPResponseHeader
+ */
+// virtual
+LLIOPipe::EStatus LLHTTPResponseHeader::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ if(eos)
+ {
+ PUMP_DEBUG;
+ //mGotEOS = true;
+ std::ostringstream ostr;
+ std::string message = context["response"]["statusMessage"];
+
+ int code = context["response"]["statusCode"];
+ if (code < 200)
+ {
+ code = 200;
+ message = "OK";
+ }
+
+ ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n";
+
+ std::string type = context["response"]["contentType"].asString();
+ if (!type.empty())
+ {
+ ostr << "Content-Type: " << type << "\r\n";
+ }
+ S32 content_length = buffer->countAfter(channels.in(), NULL);
+ if(0 < content_length)
+ {
+ ostr << "Content-Length: " << content_length << "\r\n";
+ }
+ ostr << "\r\n";
+
+ LLChangeChannel change(channels.in(), channels.out());
+ std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
+ std::string header = ostr.str();
+ buffer->prepend(channels.out(), (U8*)header.c_str(), header.size());
+ PUMP_DEBUG;
+ return STATUS_DONE;
+ }
+ PUMP_DEBUG;
+ return STATUS_OK;
+}
+
+
+
+/**
+ * @class LLHTTPResponder
+ * @brief This class
+ * @see LLIOPipe
+ *
+ * <b>NOTE:</b> You should not need to create or use one of these, the
+ * details are handled by the HTTPResponseFactory.
+ */
+class LLHTTPResponder : public LLIOPipe
+{
+public:
+ LLHTTPResponder(const LLHTTPNode& tree);
+ ~LLHTTPResponder();
+
+protected:
+ /**
+ * @brief Read data off of CHANNEL_IN keeping track of last read position.
+ *
+ * This is a quick little hack to read headers. It is not IO
+ * optimal, but it makes it easier for me to implement the header
+ * parsing. Plus, there should never be more than a few headers.
+ * This method will tend to read more than necessary, find the
+ * newline, make the front part of dest look like a c string, and
+ * move the read head back to where the newline was found. Thus,
+ * the next read will pick up on the next line.
+ * @param channel The channel to read in the buffer
+ * @param buffer The heap array of processed data
+ * @param dest Destination for the data to be read
+ * @param[in,out] len <b>in</b> The size of the buffer. <b>out</b> how
+ * much was read. This value is not useful for determining where to
+ * seek orfor string assignment.
+ * @returns Returns true if a line was found.
+ */
+ bool readLine(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer,
+ U8* dest,
+ S32& len);
+
+ /**
+ * @brief Mark the request as bad, and handle appropriately
+ *
+ * @param channels The channels to use in the buffer.
+ * @param buffer The heap array of processed data.
+ */
+ void markBad(const LLChannelDescriptors& channels, buffer_ptr_t buffer);
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ enum EState
+ {
+ STATE_NOTHING,
+ STATE_READING_HEADERS,
+ STATE_LOOKING_FOR_EOS,
+ STATE_DONE,
+ STATE_SHORT_CIRCUIT
+ };
+
+ EState mState;
+ U8* mLastRead;
+ std::string mVerb;
+ std::string mAbsPathAndQuery;
+ std::string mPath;
+ std::string mQuery;
+ std::string mVersion;
+ S32 mContentLength;
+
+ // handle the urls
+ const LLHTTPNode& mRootNode;
+};
+
+LLHTTPResponder::LLHTTPResponder(const LLHTTPNode& tree) :
+ mState(STATE_NOTHING),
+ mLastRead(NULL),
+ mContentLength(0),
+ mRootNode(tree)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+}
+
+// virtual
+LLHTTPResponder::~LLHTTPResponder()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ //lldebugs << "destroying LLHTTPResponder" << llendl;
+}
+
+bool LLHTTPResponder::readLine(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer,
+ U8* dest,
+ S32& len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ --len;
+ U8* last = buffer->readAfter(channels.in(), mLastRead, dest, len);
+ dest[len] = '\0';
+ U8* newline = (U8*)strchr((char*)dest, '\n');
+ if(!newline)
+ {
+ if(len)
+ {
+ lldebugs << "readLine failed - too long maybe?" << llendl;
+ markBad(channels, buffer);
+ }
+ return false;
+ }
+ S32 offset = -((len - 1) - (newline - dest));
+ ++newline;
+ *newline = '\0';
+ mLastRead = buffer->seek(channels.in(), last, offset);
+ return true;
+}
+
+void LLHTTPResponder::markBad(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ mState = STATE_SHORT_CIRCUIT;
+ LLBufferStream out(channels, buffer.get());
+ out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n"
+ << "<title>Bad Request</title>\n<body>\nBad Request.\n"
+ << "</body>\n</html>\n";
+}
+
+// virtual
+LLIOPipe::EStatus LLHTTPResponder::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ LLIOPipe::EStatus status = STATUS_OK;
+
+ // parsing headers
+ if((STATE_NOTHING == mState) || (STATE_READING_HEADERS == mState))
+ {
+ PUMP_DEBUG;
+ status = STATUS_BREAK;
+ mState = STATE_READING_HEADERS;
+ const S32 HEADER_BUFFER_SIZE = 1024;
+ char buf[HEADER_BUFFER_SIZE + 1]; /*Flawfinder: ignore*/
+ S32 len = HEADER_BUFFER_SIZE;
+
+#if 0
+ if(true)
+ {
+ LLBufferArray::segment_iterator_t seg_iter = buffer->beginSegment();
+ char buf[1024]; /*Flawfinder: ignore*/
+ while(seg_iter != buffer->endSegment())
+ {
+ memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/
+ buf[(*seg_iter).size()] = '\0';
+ llinfos << (*seg_iter).getChannel() << ": " << buf
+ << llendl;
+ ++seg_iter;
+ }
+ }
+#endif
+
+ PUMP_DEBUG;
+ if(readLine(channels, buffer, (U8*)buf, len))
+ {
+ bool read_next_line = false;
+ bool parse_all = true;
+ if(mVerb.empty())
+ {
+ read_next_line = true;
+ LLMemoryStream header((U8*)buf, len);
+ header >> mVerb;
+
+ if((HTTP_VERB_GET == mVerb)
+ || (HTTP_VERB_POST == mVerb)
+ || (HTTP_VERB_PUT == mVerb)
+ || (HTTP_VERB_DELETE == mVerb))
+ {
+ header >> mAbsPathAndQuery;
+ header >> mVersion;
+
+ lldebugs << "http request: "
+ << mVerb
+ << " " << mAbsPathAndQuery
+ << " " << mVersion << llendl;
+
+ std::string::size_type delimiter
+ = mAbsPathAndQuery.find('?');
+ if (delimiter == std::string::npos)
+ {
+ mPath = mAbsPathAndQuery;
+ mQuery = "";
+ }
+ else
+ {
+ mPath = mAbsPathAndQuery.substr(0, delimiter);
+ mQuery = mAbsPathAndQuery.substr(delimiter+1);
+ }
+
+ if(!mAbsPathAndQuery.empty())
+ {
+ if(mVersion.empty())
+ {
+ // simple request.
+ parse_all = false;
+ mState = STATE_DONE;
+ mVersion.assign("HTTP/1.0");
+ }
+ }
+ }
+ else
+ {
+ read_next_line = false;
+ parse_all = false;
+ lldebugs << "unknown http verb: " << mVerb << llendl;
+ markBad(channels, buffer);
+ }
+ }
+ if(parse_all)
+ {
+ bool keep_parsing = true;
+ while(keep_parsing)
+ {
+ if(read_next_line)
+ {
+ len = HEADER_BUFFER_SIZE;
+ readLine(channels, buffer, (U8*)buf, len);
+ }
+ if(0 == len)
+ {
+ return status;
+ }
+ if(buf[0] == '\r' && buf[1] == '\n')
+ {
+ // end-o-headers
+ keep_parsing = false;
+ mState = STATE_LOOKING_FOR_EOS;
+ break;
+ }
+ char* pos_colon = strchr(buf, ':');
+ if(NULL == pos_colon)
+ {
+ keep_parsing = false;
+ lldebugs << "bad header: " << buf << llendl;
+ markBad(channels, buffer);
+ break;
+ }
+ // we've found a header
+ read_next_line = true;
+ std::string name(buf, pos_colon - buf);
+ std::string value(pos_colon + 2);
+ LLString::toLower(name);
+ if("content-length" == name)
+ {
+ lldebugs << "Content-Length: " << value << llendl;
+ mContentLength = atoi(value.c_str());
+ }
+ }
+ }
+ }
+ }
+
+ PUMP_DEBUG;
+ // look for the end of stream based on
+ if(STATE_LOOKING_FOR_EOS == mState)
+ {
+ if(0 == mContentLength)
+ {
+ mState = STATE_DONE;
+ }
+ else if(buffer->countAfter(channels.in(), mLastRead) >= mContentLength)
+ {
+ mState = STATE_DONE;
+ }
+ // else more bytes should be coming.
+ }
+
+ PUMP_DEBUG;
+ if(STATE_DONE == mState)
+ {
+ // hey, hey, we should have everything now, so we pass it to
+ // a content handler.
+ context[CONTEXT_REQUEST]["verb"] = mVerb;
+ const LLHTTPNode* node = mRootNode.traverse(mPath, context);
+ if(node)
+ {
+ llinfos << "LLHTTPResponder::process_impl found node for "
+ << mAbsPathAndQuery << llendl;
+
+ // Copy everything after mLast read to the out.
+ LLBufferArray::segment_iterator_t seg_iter;
+ seg_iter = buffer->splitAfter(mLastRead);
+ if(seg_iter != buffer->endSegment())
+ {
+ LLChangeChannel change(channels.in(), channels.out());
+ ++seg_iter;
+ std::for_each(seg_iter, buffer->endSegment(), change);
+
+#if 0
+ seg_iter = buffer->beginSegment();
+ char buf[1024]; /*Flawfinder: ignore*/
+ while(seg_iter != buffer->endSegment())
+ {
+ memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/
+ buf[(*seg_iter).size()] = '\0';
+ llinfos << (*seg_iter).getChannel() << ": " << buf
+ << llendl;
+ ++seg_iter;
+ }
+#endif
+ }
+
+ //
+ // *FIX: get rid of extra bytes off the end
+ //
+
+ // Set up a chain which will prepend a content length and
+ // HTTP headers.
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(new LLIOFlush));
+ context[CONTEXT_REQUEST]["path"] = mPath;
+ context[CONTEXT_REQUEST]["query-string"] = mQuery;
+
+ const LLChainIOFactory* protocolHandler
+ = node->getProtocolHandler();
+ if (protocolHandler)
+ {
+ protocolHandler->build(chain, context);
+ }
+ else
+ {
+ // this is a simple LLHTTPNode, so use LLHTTPPipe
+ chain.push_back(LLIOPipe::ptr_t(new LLHTTPPipe(*node)));
+ }
+
+ // Add the header - which needs to have the same
+ // channel information as the link before it since it
+ // is part of the response.
+ LLIOPipe* header = new LLHTTPResponseHeader;
+ chain.push_back(LLIOPipe::ptr_t(header));
+
+ // We need to copy all of the pipes _after_ this so
+ // that the response goes out correctly.
+ LLPumpIO::links_t current_links;
+ pump->copyCurrentLinkInfo(current_links);
+ LLPumpIO::links_t::iterator link_iter = current_links.begin();
+ LLPumpIO::links_t::iterator links_end = current_links.end();
+ bool after_this = false;
+ for(; link_iter < links_end; ++link_iter)
+ {
+ if(after_this)
+ {
+ chain.push_back((*link_iter).mPipe);
+ }
+ else if(this == (*link_iter).mPipe.get())
+ {
+ after_this = true;
+ }
+ }
+
+ // Do the final build of the chain, and send it on
+ // it's way.
+ LLChannelDescriptors chnl = channels;
+ LLPumpIO::LLLinkInfo link;
+ LLPumpIO::links_t links;
+ LLPumpIO::chain_t::iterator it = chain.begin();
+ LLPumpIO::chain_t::iterator end = chain.end();
+ while(it != end)
+ {
+ link.mPipe = *it;
+ link.mChannels = chnl;
+ links.push_back(link);
+ chnl = LLBufferArray::makeChannelConsumer(chnl);
+ ++it;
+ }
+ pump->addChain(
+ links,
+ buffer,
+ context,
+ DEFAULT_CHAIN_EXPIRY_SECS);
+
+ status = STATUS_STOP;
+ }
+ else
+ {
+ llinfos << "LLHTTPResponder::process_impl didn't find a node for "
+ << mAbsPathAndQuery << llendl;
+ LLBufferStream str(channels, buffer.get());
+ mState = STATE_SHORT_CIRCUIT;
+ str << HTTP_VERSION_STR << " 404 Not Found\r\n\r\n<html>\n"
+ << "<title>Not Found</title>\n<body>\nNode '" << mAbsPathAndQuery
+ << "' not found.\n</body>\n</html>\n";
+ }
+ }
+
+ if(STATE_SHORT_CIRCUIT == mState)
+ {
+ //status = mNext->process(buffer, true, pump, context);
+ status = STATUS_DONE;
+ }
+ PUMP_DEBUG;
+ return status;
+}
+
+
+
+void LLCreateHTTPPipe(LLPumpIO::chain_t& chain, const LLHTTPNode& root)
+{
+ chain.push_back(LLIOPipe::ptr_t(new LLHTTPResponder(root)));
+}
+
+
+class LLHTTPResponseFactory : public LLChainIOFactory
+{
+public:
+ bool build(LLPumpIO::chain_t& chain, LLSD ctx) const
+ {
+ LLCreateHTTPPipe(chain, mTree);
+ return true;
+ }
+
+ LLHTTPNode& getRootNode() { return mTree; }
+
+private:
+ LLHTTPNode mTree;
+};
+
+
+LLHTTPNode& LLCreateHTTPServer(
+ apr_pool_t* pool, LLPumpIO& pump, U16 port)
+{
+ LLSocket::ptr_t socket = LLSocket::create(
+ pool,
+ LLSocket::STREAM_TCP,
+ port);
+ if(!socket)
+ {
+ llerrs << "Unable to initialize socket" << llendl;
+ }
+
+ LLHTTPResponseFactory* factory = new LLHTTPResponseFactory;
+ boost::shared_ptr<LLChainIOFactory> factory_ptr(factory);
+
+ LLIOServerSocket* server = new LLIOServerSocket(pool, socket, factory_ptr);
+
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(server));
+ pump.addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ return factory->getRootNode();
+}
+
diff --git a/indra/llmessage/lliohttpserver.h b/indra/llmessage/lliohttpserver.h
new file mode 100644
index 0000000000..05dfdc4bf7
--- /dev/null
+++ b/indra/llmessage/lliohttpserver.h
@@ -0,0 +1,95 @@
+/**
+ * @file lliohttpserver.h
+ * @brief Declaration of function for creating an HTTP wire server
+ * @see LLIOServerSocket, LLPumpIO
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOHTTPSERVER_H
+#define LL_LLIOHTTPSERVER_H
+
+#include "llapr.h"
+#include "llchainio.h"
+#include "llhttpnode.h"
+
+class LLPumpIO;
+
+LLHTTPNode& LLCreateHTTPServer(apr_pool_t* pool, LLPumpIO& pump, U16 port);
+ /**< Creates an HTTP wire server on the pump for the given TCP port.
+ *
+ * Returns the root node of the new server. Add LLHTTPNode instances
+ * to this root.
+ *
+ * Nodes that return NULL for getProtocolHandler(), will use the
+ * default handler that interprets HTTP on the wire and converts
+ * it into calls to get(), put(), post(), del() with appropriate
+ * LLSD arguments and results.
+ *
+ * To have nodes that implement some other wire protocol (XML-RPC
+ * for example), use the helper templates below.
+ */
+
+void LLCreateHTTPPipe(LLPumpIO::chain_t& chain, const LLHTTPNode& root);
+ /**< Create a pipe on the chain that handles HTTP requests.
+ * The requests are served by the node tree given at root.
+ *
+ * This is primarily useful for unit testing.
+ */
+
+/* @name Helper Templates
+ *
+ * These templates make it easy to create nodes that use thier own protocol
+ * handlers rather than the default. Typically, you subclass LLIOPipe to
+ * implement the protocol, and then add a node using the templates:
+ *
+ * rootNode->addNode("thing", new LLHTTPNodeForPipe<LLThingPipe>);
+ *
+ * The templates are:
+ *
+ * LLChainIOFactoryForPipe
+ * - a simple factory that builds instances of a pipe
+ *
+ * LLHTTPNodeForFacotry
+ * - a HTTP node that uses a factory as the protocol handler
+ *
+ * LLHTTPNodeForPipe
+ * - a HTTP node that uses a simple factory based on a pipe
+ */
+//@{
+
+template<class Pipe>
+class LLChainIOFactoryForPipe : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ chain.push_back(LLIOPipe::ptr_t(new Pipe));
+ return true;
+ }
+};
+
+template<class Factory>
+class LLHTTPNodeForFactory : public LLHTTPNode
+{
+public:
+ const LLChainIOFactory* getProtocolHandler() const
+ { return &mProtocolHandler; }
+
+private:
+ Factory mProtocolHandler;
+};
+
+//@}
+
+
+template<class Pipe>
+class LLHTTPNodeForPipe : public LLHTTPNodeForFactory<
+ LLChainIOFactoryForPipe<Pipe> >
+{
+};
+
+
+#endif // LL_LLIOHTTPSERVER_H
+
diff --git a/indra/llmessage/lliopipe.cpp b/indra/llmessage/lliopipe.cpp
new file mode 100644
index 0000000000..eac1a8b68a
--- /dev/null
+++ b/indra/llmessage/lliopipe.cpp
@@ -0,0 +1,93 @@
+/**
+ * @file lliopipe.cpp
+ * @author Phoenix
+ * @date 2004-11-19
+ * @brief Implementation of the LLIOPipe class
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliopipe.h"
+
+#include "llpumpio.h"
+
+static const std::string STATUS_SUCCESS_NAMES[LLIOPipe::STATUS_SUCCESS_COUNT] =
+{
+ std::string("STATUS_OK"),
+ std::string("STATUS_STOP"),
+ std::string("STATUS_DONE"),
+ std::string("STATUS_BREAK"),
+ std::string("STATUS_NEED_PROCESS"),
+};
+
+static const std::string STATUS_ERROR_NAMES[LLIOPipe::STATUS_ERROR_COUNT] =
+{
+ std::string("STATUS_ERROR"),
+ std::string("STATUS_NOT_IMPLEMENTED"),
+ std::string("STATUS_PRECONDITION_NOT_MET"),
+ std::string("STATUS_NO_CONNECTION"),
+ std::string("STATUS_EXPIRED"),
+};
+
+// Debugging schmutz for deadlock
+const char *gPumpFile = "";
+S32 gPumpLine = 0;
+
+void pump_debug(const char *file, S32 line)
+{
+ gPumpFile = file;
+ gPumpLine = line;
+}
+
+/**
+ * LLIOPipe
+ */
+LLIOPipe::LLIOPipe() :
+ mReferenceCount(0)
+{
+}
+
+LLIOPipe::~LLIOPipe()
+{
+ //lldebugs << "destroying LLIOPipe" << llendl;
+}
+
+// static
+std::string LLIOPipe::lookupStatusString(EStatus status)
+{
+ if((status >= 0) && (status < STATUS_SUCCESS_COUNT))
+ {
+ return STATUS_SUCCESS_NAMES[status];
+ }
+ else
+ {
+ S32 error_code = ((S32)status * -1) - 1;
+ if(error_code < STATUS_ERROR_COUNT)
+ {
+ return STATUS_ERROR_NAMES[error_code];
+ }
+ }
+ std::string rv;
+ return rv;
+}
+
+LLIOPipe::EStatus LLIOPipe::process(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ return process_impl(channels, buffer, eos, context, pump);
+}
+
+// virtual
+LLIOPipe::EStatus LLIOPipe::handleError(
+ LLIOPipe::EStatus status,
+ LLPumpIO* pump)
+{
+ // by default, the error is not handled.
+ return status;
+}
diff --git a/indra/llmessage/lliopipe.h b/indra/llmessage/lliopipe.h
new file mode 100644
index 0000000000..5cbe3d8743
--- /dev/null
+++ b/indra/llmessage/lliopipe.h
@@ -0,0 +1,291 @@
+/**
+ * @file lliopipe.h
+ * @author Phoenix
+ * @date 2004-11-18
+ * @brief Declaration of base IO class
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOPIPE_H
+#define LL_LLIOPIPE_H
+
+#include <boost/intrusive_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include "apr-1/apr_poll.h"
+
+#include "llsd.h"
+
+class LLIOPipe;
+class LLPumpIO;
+class LLBufferArray;
+class LLChannelDescriptors;
+
+// Debugging schmutz for deadlocks
+#define LL_DEBUG_PUMPS
+#ifdef LL_DEBUG_PUMPS
+void pump_debug(const char *file, S32 line);
+#define PUMP_DEBUG pump_debug(__FILE__, __LINE__);
+#define END_PUMP_DEBUG pump_debug("none", 0);
+#endif
+
+
+/**
+ * intrusive pointer support
+ */
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLIOPipe* p);
+ void intrusive_ptr_release(LLIOPipe* p);
+};
+
+/**
+ * @class LLIOPipe
+ * @brief This class is an abstract base class for data processing units
+ * @see LLPumpIO
+ *
+ * The LLIOPipe is a base class for implementing the basic non-blocking
+ * processing of data subsystem in our system.
+ *
+ * Implementations of this class should behave like a stateful or
+ * stateless signal processor. Each call to <code>process()</code>
+ * hands the pipe implementation a buffer and a set of channels in the
+ * buffer to process, and the pipe returns the status of the
+ * operation. This is an abstract base class and developer created
+ * concrete implementations provide block or stream based processing
+ * of data to implement a particular protocol.
+ */
+class LLIOPipe
+{
+public:
+ /**
+ * @brief I have decided that IO objects should have a reference
+ * count. In general, you can pass bald LLIOPipe pointers around
+ * as you need, but if you need to maintain a reference to one,
+ * you need to hold a ptr_t.
+ */
+ typedef boost::intrusive_ptr<LLIOPipe> ptr_t;
+
+ /**
+ * @brief Scattered memory container.
+ */
+ typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
+
+ /**
+ * @brief Enumeration for IO return codes
+ *
+ * A status code a positive integer value is considered a success,
+ * but may indicate special handling for future calls, for
+ * example, issuing a STATUS_STOP to an LLIOSocketReader instance
+ * will tell the instance to stop reading the socket. A status
+ * code with a negative value means that a problem has been
+ * encountered which will require further action on the caller or
+ * a developer to correct. Some mechanisms, such as the LLPumpIO
+ * may depend on this definition of success and failure.
+ */
+ enum EStatus
+ {
+ // Processing occurred normally, future calls will be accepted.
+ STATUS_OK = 0,
+
+ // Processing occured normally, but stop unsolicited calls to
+ // process.
+ STATUS_STOP = 1,
+
+ // This pipe is done with the processing. Future calls to
+ // process will be accepted as long as new data is available.
+ STATUS_DONE = 2,
+
+ // This pipe is requesting that it become the head in a process.
+ STATUS_BREAK = 3,
+
+ // This pipe is requesting that it become the head in a process.
+ STATUS_NEED_PROCESS = 4,
+
+ // Keep track of the highest number of success codes here.
+ STATUS_SUCCESS_COUNT = 5,
+
+ // A generic error code.
+ STATUS_ERROR = -1,
+
+ // This method has not yet been implemented. This usually
+ // indicates the programmer working on the pipe is not yet
+ // done.
+ STATUS_NOT_IMPLEMENTED = -2,
+
+ // This indicates that a pipe precondition was not met. For
+ // example, many pipes require an element to appear after them
+ // in a chain (ie, mNext is not null) and will return this in
+ // response to method calls. To recover from this, it will
+ // require the caller to adjust the pipe state or may require
+ // a dev to adjust the code to satisfy the preconditions.
+ STATUS_PRECONDITION_NOT_MET = -3,
+
+ // This means we could not connect to a remote host.
+ STATUS_NO_CONNECTION = -4,
+
+ // This means we could not connect to a remote host.
+ STATUS_EXPIRED = -5,
+
+ // Keep track of the count of codes here.
+ STATUS_ERROR_COUNT = 5,
+ };
+
+ /**
+ * @brief Helper function to check status.
+ *
+ * When writing code to check status codes, if you do not
+ * specifically check a particular value, use this method for
+ * checking an error condition.
+ * @param status The status to check.
+ * @return Returns true if the code indicates an error occurred.
+ */
+ inline static bool isError(EStatus status)
+ {
+ return ((S32)status < 0);
+ }
+
+ /**
+ * @brief Helper function to check status.
+ *
+ * When writing code to check status codes, if you do not
+ * specifically check a particular value, use this method for
+ * checking an error condition.
+ * @param status The status to check.
+ * @return Returns true if the code indicates no error was generated.
+ */
+ inline static bool isSuccess(EStatus status)
+ {
+ return ((S32)status >= 0);
+ }
+
+ /**
+ * @brief Helper function to turn status into a string.
+ *
+ * @param status The status to check.
+ * @return Returns the name of the status code or empty string on failure.
+ */
+ static std::string lookupStatusString(EStatus status);
+
+ /**
+ * @brief Process the data in buffer.
+ *
+ * @param data The data processed
+ * @param eos True if this function call is the last because end of stream.
+ * @param pump The pump which is calling process. May be NULL.
+ * @param context Shared meta-data for the process.
+ * @return Returns a status code from the operation.
+ */
+ EStatus process(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+ /**
+ * @brief Give this pipe a chance to handle a generated error
+ *
+ * If this pipe is in a chain being processed by a pump, and one
+ * of the pipes generates an error, the pump will rewind through
+ * the chain to see if any of the links can handle the error. For
+ * example, if a connection is refused in a socket connection, the
+ * socket client can try to find a new destination host. Return an
+ * error code if this pipe does not handle the error passed in.
+ * @param status The status code for the error
+ * @param pump The pump which was calling process before the error
+ * was generated.
+ * @return Returns a status code from the operation. Returns an
+ * error code if the error passed in was not handled. Returns
+ * STATUS_OK to indicate the error has been handled.
+ */
+ virtual EStatus handleError(EStatus status, LLPumpIO* pump);
+
+ /**
+ * @brief Base Destructor - do not call <code>delete</code> directly.
+ */
+ virtual ~LLIOPipe();
+
+protected:
+ /**
+ * @brief Base Constructor.
+ */
+ LLIOPipe();
+
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump) = 0;
+
+private:
+ friend void boost::intrusive_ptr_add_ref(LLIOPipe* p);
+ friend void boost::intrusive_ptr_release(LLIOPipe* p);
+ U32 mReferenceCount;
+};
+
+namespace boost
+{
+ inline void intrusive_ptr_add_ref(LLIOPipe* p)
+ {
+ ++p->mReferenceCount;
+ }
+ inline void intrusive_ptr_release(LLIOPipe* p)
+ {
+ if(0 == --p->mReferenceCount)
+ {
+ delete p;
+ }
+ }
+};
+
+
+#if 0
+/**
+ * @class LLIOBoiler
+ * @brief This class helps construct new LLIOPipe specializations
+ * @see LLIOPipe
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLIOBoiler : public LLIOPipe
+{
+public:
+ LLIOBoiler();
+ virtual ~LLIOBoiler();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+};
+
+// virtual
+LLIOPipe::EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+#endif // #if 0 - use this block as a boilerplate
+
+#endif // LL_LLIOPIPE_H
diff --git a/indra/llmessage/lliosocket.cpp b/indra/llmessage/lliosocket.cpp
new file mode 100644
index 0000000000..7649fef0cf
--- /dev/null
+++ b/indra/llmessage/lliosocket.cpp
@@ -0,0 +1,596 @@
+/**
+ * @file lliosocket.cpp
+ * @author Phoenix
+ * @date 2005-07-31
+ * @brief Sockets declarations for use with the io pipes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliosocket.h"
+
+#include "llapr.h"
+
+#include "llbuffer.h"
+#include "llhost.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+
+//
+// constants
+//
+
+static const S32 LL_DEFAULT_LISTEN_BACKLOG = 10;
+static const S32 LL_SEND_BUFFER_SIZE = 40000;
+static const S32 LL_RECV_BUFFER_SIZE = 40000;
+//static const U16 LL_PORT_DISCOVERY_RANGE_MIN = 13000;
+//static const U16 LL_PORT_DISCOVERY_RANGE_MAX = 13050;
+
+//
+// local methods
+//
+
+bool is_addr_in_use(apr_status_t status)
+{
+#if LL_WINDOWS
+ return (WSAEADDRINUSE == APR_TO_OS_ERROR(status));
+#else
+ return (EADDRINUSE == APR_TO_OS_ERROR(status));
+#endif
+}
+
+///
+/// LLSocket
+///
+
+// static
+LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ LLSocket::ptr_t rv;
+ apr_socket_t* socket = NULL;
+ apr_pool_t* new_pool = NULL;
+ apr_status_t status = APR_EGENERAL;
+
+ // create a pool for the socket
+ status = apr_pool_create(&new_pool, pool);
+ if(ll_apr_warn_status(status))
+ {
+ if(new_pool) apr_pool_destroy(new_pool);
+ return rv;
+ }
+
+ if(STREAM_TCP == type)
+ {
+ status = apr_socket_create(
+ &socket,
+ APR_INET,
+ SOCK_STREAM,
+ APR_PROTO_TCP,
+ new_pool);
+ }
+ else if(DATAGRAM_UDP == type)
+ {
+ status = apr_socket_create(
+ &socket,
+ APR_INET,
+ SOCK_DGRAM,
+ APR_PROTO_UDP,
+ new_pool);
+ }
+ else
+ {
+ if(new_pool) apr_pool_destroy(new_pool);
+ return rv;
+ }
+ if(ll_apr_warn_status(status))
+ {
+ if(new_pool) apr_pool_destroy(new_pool);
+ return rv;
+ }
+ rv = ptr_t(new LLSocket(socket, new_pool));
+ if(port > 0)
+ {
+ apr_sockaddr_t* sa = NULL;
+ status = apr_sockaddr_info_get(
+ &sa,
+ APR_ANYADDR,
+ APR_UNSPEC,
+ port,
+ 0,
+ new_pool);
+ if(ll_apr_warn_status(status))
+ {
+ rv.reset();
+ return rv;
+ }
+ // This allows us to reuse the address on quick down/up. This
+ // is unlikely to create problems.
+ ll_apr_warn_status(apr_socket_opt_set(socket, APR_SO_REUSEADDR, 1));
+ status = apr_socket_bind(socket, sa);
+ if(ll_apr_warn_status(status))
+ {
+ rv.reset();
+ return rv;
+ }
+ lldebugs << "Bound " << ((DATAGRAM_UDP == type) ? "udp" : "tcp")
+ << " socket to port: " << sa->port << llendl;
+ if(STREAM_TCP == type)
+ {
+ // If it's a stream based socket, we need to tell the OS
+ // to keep a queue of incoming connections for ACCEPT.
+ lldebugs << "Setting listen state for socket." << llendl;
+ status = apr_socket_listen(
+ socket,
+ LL_DEFAULT_LISTEN_BACKLOG);
+ if(ll_apr_warn_status(status))
+ {
+ rv.reset();
+ return rv;
+ }
+ }
+ }
+ else
+ {
+ // we need to indicate that we have an ephemeral port if the
+ // previous calls were successful. It will
+ port = PORT_EPHEMERAL;
+ }
+ rv->mPort = port;
+ rv->setOptions();
+ return rv;
+}
+
+// static
+LLSocket::ptr_t LLSocket::create(apr_socket_t* socket, apr_pool_t* pool)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ LLSocket::ptr_t rv;
+ if(!socket)
+ {
+ return rv;
+ }
+ rv = ptr_t(new LLSocket(socket, pool));
+ rv->mPort = PORT_EPHEMERAL;
+ rv->setOptions();
+ return rv;
+}
+
+
+bool LLSocket::blockingConnect(const LLHost& host)
+{
+ if(!mSocket) return false;
+ apr_sockaddr_t* sa = NULL;
+ char ip_address[MAXADDRSTR]; /*Flawfinder: ignore*/
+ host.getIPString(ip_address, MAXADDRSTR);
+ if(ll_apr_warn_status(apr_sockaddr_info_get(
+ &sa,
+ ip_address,
+ APR_UNSPEC,
+ host.getPort(),
+ 0,
+ mPool)))
+ {
+ return false;
+ }
+ apr_socket_timeout_set(mSocket, 1000);
+ if(ll_apr_warn_status(apr_socket_connect(mSocket, sa))) return false;
+ setOptions();
+ return true;
+}
+
+LLSocket::LLSocket(apr_socket_t* socket, apr_pool_t* pool) :
+ mSocket(socket),
+ mPool(pool),
+ mPort(PORT_INVALID)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLSocket::~LLSocket()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ // *FIX: clean up memory we are holding.
+ //lldebugs << "Destroying LLSocket" << llendl;
+ if(mSocket)
+ {
+ apr_socket_close(mSocket);
+ }
+ if(mPool)
+ {
+ apr_pool_destroy(mPool);
+ }
+}
+
+void LLSocket::setOptions()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ // set up the socket options
+ ll_apr_warn_status(apr_socket_timeout_set(mSocket, 0));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
+
+}
+
+///
+/// LLIOSocketReader
+///
+
+LLIOSocketReader::LLIOSocketReader(LLSocket::ptr_t socket) :
+ mSource(socket),
+ mInitialized(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLIOSocketReader::~LLIOSocketReader()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ //lldebugs << "Destroying LLIOSocketReader" << llendl;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOSocketReader::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ if(!mSource) return STATUS_PRECONDITION_NOT_MET;
+ if(!mInitialized)
+ {
+ PUMP_DEBUG;
+ // Since the read will not block, it's ok to initialize and
+ // attempt to read off the descriptor immediately.
+ mInitialized = true;
+ if(pump)
+ {
+ PUMP_DEBUG;
+ lldebugs << "Initializing poll descriptor for LLIOSocketReader."
+ << llendl;
+ apr_pollfd_t poll_fd;
+ poll_fd.p = NULL;
+ poll_fd.desc_type = APR_POLL_SOCKET;
+ poll_fd.reqevents = APR_POLLIN;
+ poll_fd.rtnevents = 0x0;
+ poll_fd.desc.s = mSource->getSocket();
+ poll_fd.client_data = NULL;
+ pump->setConditional(this, &poll_fd);
+ }
+ }
+ //if(!buffer)
+ //{
+ // buffer = new LLBufferArray;
+ //}
+ PUMP_DEBUG;
+ const apr_size_t READ_BUFFER_SIZE = 1024;
+ char read_buf[READ_BUFFER_SIZE]; /*Flawfinder: ignore*/
+ apr_size_t len;
+ apr_status_t status = APR_SUCCESS;
+ do
+ {
+ PUMP_DEBUG;
+ len = READ_BUFFER_SIZE;
+ status = apr_socket_recv(mSource->getSocket(), read_buf, &len);
+ buffer->append(channels.out(), (U8*)read_buf, len);
+ } while((APR_SUCCESS == status) && (READ_BUFFER_SIZE == len));
+ lldebugs << "socket read status: " << status << llendl;
+ LLIOPipe::EStatus rv = STATUS_OK;
+
+ PUMP_DEBUG;
+ // *FIX: Also need to check for broken pipe
+ if(APR_STATUS_IS_EOF(status))
+ {
+ // *FIX: Should we shut down the socket read?
+ if(pump)
+ {
+ pump->setConditional(this, NULL);
+ }
+ rv = STATUS_DONE;
+ eos = true;
+ }
+ else if(APR_STATUS_IS_EAGAIN(status))
+ {
+ // everything is fine, but we can terminate this process pump.
+ rv = STATUS_BREAK;
+ }
+ else
+ {
+ if(ll_apr_warn_status(status))
+ {
+ rv = STATUS_ERROR;
+ }
+ }
+ PUMP_DEBUG;
+ return rv;
+}
+
+///
+/// LLIOSocketWriter
+///
+
+LLIOSocketWriter::LLIOSocketWriter(LLSocket::ptr_t socket) :
+ mDestination(socket),
+ mLastWritten(NULL),
+ mInitialized(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLIOSocketWriter::~LLIOSocketWriter()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ //lldebugs << "Destroying LLIOSocketWriter" << llendl;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOSocketWriter::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ if(!mDestination) return STATUS_PRECONDITION_NOT_MET;
+ if(!mInitialized)
+ {
+ PUMP_DEBUG;
+ // Since the write will not block, it's ok to initialize and
+ // attempt to write immediately.
+ mInitialized = true;
+ if(pump)
+ {
+ PUMP_DEBUG;
+ lldebugs << "Initializing poll descriptor for LLIOSocketWriter."
+ << llendl;
+ apr_pollfd_t poll_fd;
+ poll_fd.p = NULL;
+ poll_fd.desc_type = APR_POLL_SOCKET;
+ poll_fd.reqevents = APR_POLLOUT;
+ poll_fd.rtnevents = 0x0;
+ poll_fd.desc.s = mDestination->getSocket();
+ poll_fd.client_data = NULL;
+ pump->setConditional(this, &poll_fd);
+ }
+ }
+
+ PUMP_DEBUG;
+ // *FIX: Some sort of writev implementation would be much more
+ // efficient - not only because writev() is better, but also
+ // because we won't have to do as much work to find the start
+ // address.
+ LLBufferArray::segment_iterator_t it;
+ LLBufferArray::segment_iterator_t end = buffer->endSegment();
+ LLSegment segment;
+ it = buffer->constructSegmentAfter(mLastWritten, segment);
+ /*
+ if(NULL == mLastWritten)
+ {
+ it = buffer->beginSegment();
+ segment = (*it);
+ }
+ else
+ {
+ it = buffer->getSegment(mLastWritten);
+ segment = (*it);
+ S32 size = segment.size();
+ U8* data = segment.data();
+ if((data + size) == mLastWritten)
+ {
+ ++it;
+ segment = (*it);
+ }
+ else
+ {
+ // *FIX: check the math on this one
+ segment = LLSegment(
+ (*it).getChannelMask(),
+ mLastWritten + 1,
+ size - (mLastWritten - data));
+ }
+ }
+ */
+
+ PUMP_DEBUG;
+ apr_size_t len;
+ bool done = false;
+ while(it != end)
+ {
+ PUMP_DEBUG;
+ if((*it).isOnChannel(channels.in()))
+ {
+ PUMP_DEBUG;
+ // *FIX: check return code - sockets will fail (broken, etc.)
+ len = (apr_size_t)segment.size();
+ apr_socket_send(
+ mDestination->getSocket(),
+ (const char*)segment.data(),
+ &len);
+ mLastWritten = segment.data() + len - 1;
+ PUMP_DEBUG;
+ if((S32)len < segment.size())
+ {
+ break;
+ }
+ }
+ ++it;
+ if(it != end)
+ {
+ segment = (*it);
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ PUMP_DEBUG;
+ if(done && eos)
+ {
+ return STATUS_DONE;
+ }
+ return STATUS_OK;
+}
+
+
+///
+/// LLIOServerSocket
+///
+
+LLIOServerSocket::LLIOServerSocket(
+ apr_pool_t* pool,
+ LLIOServerSocket::socket_t listener,
+ factory_t factory) :
+ mPool(pool),
+ mListenSocket(listener),
+ mReactor(factory),
+ mInitialized(false),
+ mResponseTimeout(DEFAULT_CHAIN_EXPIRY_SECS)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLIOServerSocket::~LLIOServerSocket()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ //lldebugs << "Destroying LLIOServerSocket" << llendl;
+}
+
+void LLIOServerSocket::setResponseTimeout(F32 timeout_secs)
+{
+ mResponseTimeout = timeout_secs;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOServerSocket::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ if(!pump)
+ {
+ llwarns << "Need a pump for server socket." << llendl;
+ return STATUS_ERROR;
+ }
+ if(!mInitialized)
+ {
+ PUMP_DEBUG;
+ // This segment sets up the pump so that we do not call
+ // process again until we have an incoming read, aka connect()
+ // from a remote host.
+ lldebugs << "Initializing poll descriptor for LLIOServerSocket."
+ << llendl;
+ apr_pollfd_t poll_fd;
+ poll_fd.p = NULL;
+ poll_fd.desc_type = APR_POLL_SOCKET;
+ poll_fd.reqevents = APR_POLLIN;
+ poll_fd.rtnevents = 0x0;
+ poll_fd.desc.s = mListenSocket->getSocket();
+ poll_fd.client_data = NULL;
+ pump->setConditional(this, &poll_fd);
+ mInitialized = true;
+ return STATUS_OK;
+ }
+
+ // we are initialized, and told to process, so we must have a
+ // socket waiting for a connection.
+ lldebugs << "accepting socket" << llendl;
+
+ PUMP_DEBUG;
+ apr_pool_t* new_pool = NULL;
+ apr_status_t status = apr_pool_create(&new_pool, mPool);
+ apr_socket_t* socket = NULL;
+ status = apr_socket_accept(
+ &socket,
+ mListenSocket->getSocket(),
+ new_pool);
+ LLSocket::ptr_t llsocket(LLSocket::create(socket, new_pool));
+ //EStatus rv = STATUS_ERROR;
+ if(llsocket)
+ {
+ PUMP_DEBUG;
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(llsocket)));
+ if(mReactor->build(chain, LLSD()))
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(llsocket)));
+ pump->addChain(chain, mResponseTimeout);
+ status = STATUS_OK;
+ }
+ else
+ {
+ llwarns << "Unable to build reactor to socket." << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "Unable to create linden socket." << llendl;
+ }
+
+ PUMP_DEBUG;
+ // This needs to always return success, lest it get removed from
+ // the pump.
+ return STATUS_OK;
+}
+
+
+#if 0
+LLIODataSocket::LLIODataSocket(
+ U16 suggested_port,
+ U16 start_discovery_port,
+ apr_pool_t* pool) :
+ mSocket(NULL)
+{
+ if(!pool || (PORT_INVALID == suggested_port)) return;
+ if(ll_apr_warn_status(apr_socket_create(&mSocket, APR_INET, SOCK_DGRAM, APR_PROTO_UDP, pool))) return;
+ apr_sockaddr_t* sa = NULL;
+ if(ll_apr_warn_status(apr_sockaddr_info_get(&sa, APR_ANYADDR, APR_UNSPEC, suggested_port, 0, pool))) return;
+ apr_status_t status = apr_socket_bind(mSocket, sa);
+ if((start_discovery_port > 0) && is_addr_in_use(status))
+ {
+ const U16 MAX_ATTEMPT_PORTS = 50;
+ for(U16 attempt_port = start_discovery_port;
+ attempt_port < (start_discovery_port + MAX_ATTEMPT_PORTS);
+ ++attempt_port)
+ {
+ sa->port = attempt_port;
+ sa->sa.sin.sin_port = htons(attempt_port);
+ status = apr_socket_bind(mSocket, sa);
+ if(APR_SUCCESS == status) break;
+ if(is_addr_in_use(status)) continue;
+ (void)ll_apr_warn_status(status);
+ }
+ }
+ if(ll_apr_warn_status(status)) return;
+ if(sa->port)
+ {
+ lldebugs << "Bound datagram socket to port: " << sa->port << llendl;
+ mPort = sa->port;
+ }
+ else
+ {
+ mPort = LLIOSocket::PORT_EPHEMERAL;
+ }
+
+ // set up the socket options options
+ ll_apr_warn_status(apr_socket_timeout_set(mSocket, 0));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
+}
+
+LLIODataSocket::~LLIODataSocket()
+{
+}
+
+
+#endif
diff --git a/indra/llmessage/lliosocket.h b/indra/llmessage/lliosocket.h
new file mode 100644
index 0000000000..fd15949b69
--- /dev/null
+++ b/indra/llmessage/lliosocket.h
@@ -0,0 +1,355 @@
+/**
+ * @file lliosocket.h
+ * @author Phoenix
+ * @date 2005-07-31
+ * @brief Declaration of files used for handling sockets and associated pipes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOSOCKET_H
+#define LL_LLIOSOCKET_H
+
+/**
+ * The socket interface provided here is a simple wraper around apr
+ * sockets, with a pipe source and sink to read and write off of the
+ * socket. Every socket only performs non-blocking operations except
+ * the server socket which only performs blocking operations when an
+ * OS poll indicates it will not block.
+ */
+
+#include "lliopipe.h"
+#include "apr-1/apr_pools.h"
+#include "apr-1/apr_network_io.h"
+#include "llchainio.h"
+
+class LLHost;
+
+/**
+ * @class LLSocket
+ * @brief Implementation of a wrapper around a socket.
+ *
+ * An instance of this class represents a single socket over it's
+ * entire life - from uninitialized, to connected, to a listening
+ * socket depending on it's purpose. This class simplifies our access
+ * into the socket interface by only providing stream/tcp and
+ * datagram/udp sockets - the only types we are interested in, since
+ * those are the only properly supported by all of our platforms.
+ */
+class LLSocket
+{
+public:
+ /**
+ * @brief Reference counted shared pointers to sockets.
+ */
+ typedef boost::shared_ptr<LLSocket> ptr_t;
+
+ /**
+ * @brief Type of socket to create.
+ */
+ enum EType
+ {
+ STREAM_TCP,
+ DATAGRAM_UDP,
+ };
+
+ /**
+ * @brief Anonymous enumeration to help identify ports
+ */
+ enum
+ {
+ PORT_INVALID = (U16)-1,
+ PORT_EPHEMERAL = 0,
+ };
+
+ /**
+ * @brief Create a socket.
+ *
+ * This is the call you would use if you intend to create a listen
+ * socket. If you intend the socket to be known to external
+ * clients without prior port notification, do not use
+ * PORT_EPHEMERAL.
+ * @param pool The apr pool to use. A child pool will be created
+ * and associated with the socket.
+ * @param type The type of socket to create
+ * @param port The port for the socket
+ * @return A valid socket shared pointer if the call worked.
+ */
+ static ptr_t create(
+ apr_pool_t* pool,
+ EType type,
+ U16 port = PORT_EPHEMERAL);
+
+ /**
+ * @brief Create a LLSocket when you already have an apr socket.
+ *
+ * This method assumes an ephemeral port. This is typically used
+ * by calls which spawn a socket such as a call to
+ * <code>accept()</code> as in the server socket. This call should
+ * not fail if you have a valid apr socket.
+ * Because of the nature of how accept() works, you are expected
+ * to create a new pool for the socket, use that pool for the
+ * accept, and pass it in here where it will be bound with the
+ * socket and destroyed at the same time.
+ * @param socket The apr socket to use
+ * @param pool The pool used to create the socket. *NOTE: The pool
+ * passed in will be DESTROYED.
+ * @return A valid socket shared pointer if the call worked.
+ */
+ static ptr_t create(apr_socket_t* socket, apr_pool_t* pool);
+
+ /**
+ * @brief Perform a blocking connect to a host. Do not use in production.
+ *
+ * @param host The host to connect this socket to.
+ * @return Returns true if the connect was successful.
+ */
+ bool blockingConnect(const LLHost& host);
+
+ /**
+ * @brief Get the type of socket
+ */
+ //EType getType() const { return mType; }
+
+ /**
+ * @brief Get the port.
+ *
+ * This will return PORT_EPHEMERAL if bind was never called.
+ * @return Returns the port associated with this socket.
+ */
+ U16 getPort() const { return mPort; }
+
+ /**
+ * @brief Get the apr socket implementation.
+ *
+ * @return Returns the raw apr socket.
+ */
+ apr_socket_t* getSocket() const { return mSocket; }
+
+protected:
+ /**
+ * @brief Protected constructor since should only make sockets
+ * with one of the two <code>create()</code> calls.
+ */
+ LLSocket(apr_socket_t* socket, apr_pool_t* pool);
+
+ /**
+ * @brief Set default socket options.
+ */
+ void setOptions();
+
+public:
+ /**
+ * @brief Do not call this directly.
+ */
+ ~LLSocket();
+
+protected:
+ // The apr socket.
+ apr_socket_t* mSocket;
+
+ // our memory pool
+ apr_pool_t* mPool;
+
+ // The port if we know it.
+ U16 mPort;
+
+ //EType mType;
+};
+
+/**
+ * @class LLIOSocketReader
+ * @brief An LLIOPipe implementation which reads from a socket.
+ * @see LLIOPipe
+ *
+ * An instance of a socket reader wraps around an LLSocket and
+ * performs non-blocking reads and passes it to the next pipe in the
+ * chain.
+ */
+class LLIOSocketReader : public LLIOPipe
+{
+public:
+ LLIOSocketReader(LLSocket::ptr_t socket);
+ ~LLIOSocketReader();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data coming in the socket.
+ *
+ * Since the socket and next pipe must exist for process to make
+ * any sense, this method will return STATUS_PRECONDITION_NOT_MET
+ * unless if they are not known.
+ * If a STATUS_STOP returned by the next link in the chain, this
+ * reader will turn of the socket polling.
+ * @param buffer Pointer to a buffer which needs processing. Probably NULL.
+ * @param bytes Number of bytes to in buffer to process. Probably 0.
+ * @param eos True if this function is the last. Almost always false.
+ * @param read Number of bytes actually processed.
+ * @param pump The pump which is calling process. May be NULL.
+ * @param context A data structure to pass structured data
+ * @return STATUS_OK unless the preconditions are not met.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLSocket::ptr_t mSource;
+ std::vector<U8> mBuffer;
+ bool mInitialized;
+};
+
+/**
+ * @class LLIOSocketWriter
+ * @brief An LLIOPipe implementation which writes to a socket
+ * @see LLIOPipe
+ *
+ * An instance of a socket writer wraps around an LLSocket and
+ * performs non-blocking writes of the data passed in.
+ */
+class LLIOSocketWriter : public LLIOPipe
+{
+public:
+ LLIOSocketWriter(LLSocket::ptr_t socket);
+ ~LLIOSocketWriter();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Write the data in buffer to the socket.
+ *
+ * Since the socket pipe must exist for process to make any sense,
+ * this method will return STATUS_PRECONDITION_NOT_MET if it is
+ * not known.
+ * @param buffer Pointer to a buffer which needs processing.
+ * @param bytes Number of bytes to in buffer to process.
+ * @param eos True if this function is the last.
+ * @param read Number of bytes actually processed.
+ * @param pump The pump which is calling process. May be NULL.
+ * @param context A data structure to pass structured data
+ * @return A return code for the write.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLSocket::ptr_t mDestination;
+ U8* mLastWritten;
+ bool mInitialized;
+};
+
+/**
+ * @class LLIOServerSocket
+ * @brief An IOPipe implementation which listens and spawns connected
+ * sockets.
+ * @see LLIOPipe, LLChainIOFactory
+ *
+ * Each server socket instance coordinates with a pump to ensure it
+ * only processes waiting connections. It uses the provided socket,
+ * and assumes it is correctly initialized. When the connection is
+ * established, the server will call the chain factory to build a
+ * chain, and attach a socket reader and the front and a socket writer
+ * at the end. It is up to the chain factory to create something which
+ * correctly handles the established connection using the reader as a
+ * source, and the writer as the final sink.
+ * The newly added chain timeout is in DEFAULT_CHAIN_EXPIRY_SECS unless
+ * adjusted with a call to setResponseTimeout().
+ */
+class LLIOServerSocket : public LLIOPipe
+{
+public:
+ typedef LLSocket::ptr_t socket_t;
+ typedef boost::shared_ptr<LLChainIOFactory> factory_t;
+ LLIOServerSocket(apr_pool_t* pool, socket_t listener, factory_t reactor);
+ virtual ~LLIOServerSocket();
+
+ /**
+ * @brief Set the timeout for the generated chains.
+ *
+ * This value is passed directly to the LLPumpIO::addChain()
+ * method. The default on construction is set to
+ * DEFAULT_CHAIN_EXPIRY_SECS which is a reasonable value for most
+ * applications based on this library. Avoid passing in
+ * NEVER_CHAIN_EXPIRY_SECS unless you have another method of
+ * harvesting chains.
+ * @param timeout_secs The seconds before timeout for the response chain.
+ */
+ void setResponseTimeout(F32 timeout_secs);
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ apr_pool_t* mPool;
+ socket_t mListenSocket;
+ factory_t mReactor;
+ bool mInitialized;
+ F32 mResponseTimeout;
+};
+
+#if 0
+/**
+ * @class LLIODataSocket
+ * @brief BRIEF_DESC
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLIODataSocket : public LLIOSocket
+{
+public:
+ /**
+ * @brief Construct a datagram socket.
+ *
+ * If you pass in LLIOSocket::PORT_EPHEMERAL as the suggested
+ * port, The socket will not be in a 'listen' mode, but can still
+ * read data sent back to it's port. When suggested_port is not
+ * ephemeral or invalid and bind fails, the port discovery
+ * algorithm will search through a limited set of ports to
+ * try to find an open port. If that process fails, getPort() will
+ * return LLIOSocket::PORT_INVALID
+ * @param suggested_port The port you would like to bind. Use
+ * LLIOSocket::PORT_EPHEMERAL for an unspecified port.
+ * @param start_discovery_port The start range for
+ * @param pool The pool to use for allocation.
+ */
+ LLIODataSocket(
+ U16 suggested_port,
+ U16 start_discovery_port,
+ apr_pool_t* pool);
+ virtual ~LLIODataSocket();
+
+protected:
+
+private:
+ apr_socket_t* mSocket;
+};
+#endif
+
+#endif // LL_LLIOSOCKET_H
diff --git a/indra/llmessage/llioutil.cpp b/indra/llmessage/llioutil.cpp
new file mode 100644
index 0000000000..b0369439e6
--- /dev/null
+++ b/indra/llmessage/llioutil.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llioutil.cpp
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Utility functionality for the io pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llioutil.h"
+
+/**
+ * LLIOFlush
+ */
+LLIOPipe::EStatus LLIOFlush::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ eos = true;
+ return STATUS_OK;
+}
+
+/**
+ * @class LLIOSleep
+ */
+LLIOPipe::EStatus LLIOSleep::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ if(mSeconds > 0.0)
+ {
+ if(pump) pump->sleepChain(mSeconds);
+ mSeconds = 0.0;
+ return STATUS_BREAK;
+ }
+ return STATUS_DONE;
+}
+
+/**
+ * @class LLIOAddChain
+ */
+LLIOPipe::EStatus LLIOAddChain::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ pump->addChain(mChain, mTimeout);
+ return STATUS_DONE;
+}
+
+/**
+ * LLChangeChannel
+ */
+LLChangeChannel::LLChangeChannel(S32 is, S32 becomes) :
+ mIs(is),
+ mBecomes(becomes)
+{
+}
+
+void LLChangeChannel::operator()(LLSegment& segment)
+{
+ if(segment.isOnChannel(mIs))
+ {
+ segment.setChannel(mBecomes);
+ }
+}
diff --git a/indra/llmessage/llioutil.h b/indra/llmessage/llioutil.h
new file mode 100644
index 0000000000..3b8452918e
--- /dev/null
+++ b/indra/llmessage/llioutil.h
@@ -0,0 +1,154 @@
+/**
+ * @file llioutil.h
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Helper classes for dealing with IOPipes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOUTIL_H
+#define LL_LLIOUTIL_H
+
+#include "llbuffer.h"
+#include "lliopipe.h"
+#include "llpumpio.h"
+
+/**
+ * @class LLIOFlush
+ * @brief This class is used as a mini chain head which drains the buffer.
+ * @see LLIOPipe
+ *
+ * An instance of this class acts as a useful chain head when all data
+ * is known, and you simply want to get the chain moving.
+ */
+class LLIOFlush : public LLIOPipe
+{
+public:
+ LLIOFlush() {}
+ virtual ~LLIOFlush() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+protected:
+};
+
+/**
+ * @class LLIOSleep
+ * @brief This is a simple helper class which will hold a chain and
+ * process it later using pump mechanisms
+ * @see LLIOPipe
+ */
+class LLIOSleep : public LLIOPipe
+{
+public:
+ LLIOSleep(F64 sleep_seconds) : mSeconds(sleep_seconds) {}
+ virtual ~LLIOSleep() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+protected:
+ F64 mSeconds;
+};
+
+/**
+ * @class LLIOAddChain
+ * @brief Simple pipe that just adds a chain to a pump.
+ * @see LLIOPipe
+ */
+class LLIOAddChain : public LLIOPipe
+{
+public:
+ LLIOAddChain(const LLPumpIO::chain_t& chain, F32 timeout) :
+ mChain(chain),
+ mTimeout(timeout)
+ {}
+ virtual ~LLIOAddChain() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLPumpIO::chain_t mChain;
+ F32 mTimeout;
+};
+
+/**
+ * @class LLChangeChannel
+ * @brief This class changes the channel of segments in the buffer
+ * @see LLBufferArray
+ *
+ * This class is useful for iterating over the segments in a buffer
+ * array and changing each channel that matches to a different
+ * channel.
+ * Example:
+ * <code>
+ * set_in_to_out(LLChannelDescriptors channels, LLBufferArray* buf)
+ * {
+ * std::for_each(
+ * buf->beginSegment(),
+ * buf->endSegment(),
+ * LLChangeChannel(channels.in(), channels.out()));
+ * }
+ * </code>
+ */
+class LLChangeChannel //: public unary_function<T, void>
+{
+public:
+ /**
+ * @brief Constructor for iterating over a segment range to change channel.
+ *
+ * @param is The channel to match when looking at a segment.
+ * @param becomes The channel to set the segment when a match is found.
+ */
+ LLChangeChannel(S32 is, S32 becomes);
+
+ /**
+ * @brief Do the work of changing the channel
+ */
+ void operator()(LLSegment& segment);
+
+protected:
+ S32 mIs;
+ S32 mBecomes;
+};
+
+
+#endif // LL_LLIOUTIL_H
diff --git a/indra/llmessage/llloginflags.h b/indra/llmessage/llloginflags.h
new file mode 100644
index 0000000000..b21088a61d
--- /dev/null
+++ b/indra/llmessage/llloginflags.h
@@ -0,0 +1,37 @@
+/**
+ * @file llloginflags.h
+ * @brief Login flag constants.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLOGINFLAGS_H
+#define LL_LLLOGINFLAGS_H
+
+// Is this your first login to Second Life?
+const U32 LOGIN_FLAG_FIRST_LOGIN = (1 << 0);
+
+// Is this a trial account?
+const U32 LOGIN_FLAG_TRIAL = (1 << 1);
+
+// Stipend paid since last login?
+const U32 LOGIN_FLAG_STIPEND_SINCE_LOGIN = (1 << 2);
+
+// Is your account enabled?
+const U32 LOGIN_FLAG_ENABLED = (1 << 3);
+
+// Is the Pacific Time zone (aka the server time zone)
+// currently observing daylight savings time?
+const U32 LOGIN_FLAG_PACIFIC_DAYLIGHT_TIME = (1 << 4);
+
+// Does the avatar have wearables or not
+const U32 LOGIN_FLAG_GENDERED = (1 << 5);
+
+// Kick flags
+const U32 LOGIN_KICK_OK = 0x0;
+const U32 LOGIN_KICK_NO_AGENT = (1 << 0);
+const U32 LOGIN_KICK_SESSION_MISMATCH = (1 << 1);
+const U32 LOGIN_KICK_NO_SIMULATOR = (1 << 2);
+
+#endif
diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp
new file mode 100644
index 0000000000..9fe8e89b20
--- /dev/null
+++ b/indra/llmessage/llmail.cpp
@@ -0,0 +1,285 @@
+/**
+ * @file llmail.cpp
+ * @brief smtp helper functions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <string>
+#include <sstream>
+#include <boost/regex.hpp>
+
+#include "llmail.h"
+
+#include "apr-1/apr_pools.h"
+#include "apr-1/apr_network_io.h"
+
+#include "llapr.h"
+#include "llerror.h"
+#include "llhost.h"
+#include "net.h"
+
+//
+// constants
+//
+const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE = 4096;
+
+static bool gMailEnabled = true;
+static apr_pool_t* gMailPool;
+static apr_sockaddr_t* gSockAddr;
+static apr_socket_t* gMailSocket;
+
+// According to RFC2822
+static const boost::regex valid_subject_chars("[\\x1-\\x9\\xb\\xc\\xe-\\x7f]+");
+bool connect_smtp();
+void disconnect_smtp();
+
+//#if LL_WINDOWS
+//SOCKADDR_IN gMailDstAddr, gMailSrcAddr, gMailLclAddr;
+//#else
+//struct sockaddr_in gMailDstAddr, gMailSrcAddr, gMailLclAddr;
+//#endif
+
+// Define this for a super-spammy mail mode.
+//#define LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND 1
+
+bool connect_smtp()
+{
+ // Prepare an soket to talk smtp
+ apr_status_t status;
+ status = apr_socket_create(
+ &gMailSocket,
+ gSockAddr->sa.sin.sin_family,
+ SOCK_STREAM,
+ APR_PROTO_TCP,
+ gMailPool);
+ if(ll_apr_warn_status(status)) return false;
+ status = apr_socket_connect(gMailSocket, gSockAddr);
+ if(ll_apr_warn_status(status))
+ {
+ status = apr_socket_close(gMailSocket);
+ ll_apr_warn_status(status);
+ return false;
+ }
+ return true;
+}
+
+void disconnect_smtp()
+{
+ if(gMailSocket)
+ {
+ apr_status_t status = apr_socket_close(gMailSocket);
+ ll_apr_warn_status(status);
+ gMailSocket = NULL;
+ }
+}
+
+// Returns TRUE on success.
+// message should NOT be SMTP escaped.
+BOOL send_mail(const char* from_name, const char* from_address,
+ const char* to_name, const char* to_address,
+ const char* subject, const char* message)
+{
+ std::string header = build_smtp_transaction(
+ from_name,
+ from_address,
+ to_name,
+ to_address,
+ subject);
+ if(header.empty())
+ {
+ return FALSE;
+ }
+
+ std::string message_str;
+ if(message)
+ {
+ message_str = message;
+ }
+ bool rv = send_mail(header, message_str, to_address, from_address);
+ if(rv) return TRUE;
+ return FALSE;
+}
+
+void init_mail(const std::string& hostname, apr_pool_t* pool)
+{
+ gMailSocket = NULL;
+ if(hostname.empty() || !pool)
+ {
+ gMailPool = NULL;
+ gSockAddr = NULL;
+ }
+ else
+ {
+ gMailPool = pool;
+
+ // collect all the information into a socaddr sturcture. the
+ // documentation is a bit unclear, but I either have to
+ // specify APR_UNSPEC or not specify any flags. I am not sure
+ // which option is better.
+ apr_status_t status = apr_sockaddr_info_get(
+ &gSockAddr,
+ hostname.c_str(),
+ APR_UNSPEC,
+ 25,
+ APR_IPV4_ADDR_OK,
+ gMailPool);
+ ll_apr_warn_status(status);
+ }
+}
+
+void enable_mail(bool mail_enabled)
+{
+ gMailEnabled = mail_enabled;
+}
+
+std::string build_smtp_transaction(
+ const char* from_name,
+ const char* from_address,
+ const char* to_name,
+ const char* to_address,
+ const char* subject)
+{
+ if(!from_address || !to_address)
+ {
+ llinfos << "send_mail build_smtp_transaction reject: missing to and/or"
+ << " from address." << llendl;
+ return std::string();
+ }
+ if(! boost::regex_match(subject, valid_subject_chars))
+ {
+ llinfos << "send_mail build_smtp_transaction reject: bad subject header: "
+ << "to=<" << to_address
+ << ">, from=<" << from_address << ">"
+ << llendl;
+ return std::string();
+ }
+ std::ostringstream from_fmt;
+ if(from_name && from_name[0])
+ {
+ // "My Name" <myaddress@example.com>
+ from_fmt << "\"" << from_name << "\" <" << from_address << ">";
+ }
+ else
+ {
+ // <myaddress@example.com>
+ from_fmt << "<" << from_address << ">";
+ }
+ std::ostringstream to_fmt;
+ if(to_name && to_name[0])
+ {
+ to_fmt << "\"" << to_name << "\" <" << to_address << ">";
+ }
+ else
+ {
+ to_fmt << "<" << to_address << ">";
+ }
+ std::ostringstream header;
+ header
+ << "HELO lindenlab.com\r\n"
+ << "MAIL FROM:<" << from_address << ">\r\n"
+ << "RCPT TO:<" << to_address << ">\r\n"
+ << "DATA\r\n"
+ << "From: " << from_fmt.str() << "\r\n"
+ << "To: " << to_fmt.str() << "\r\n"
+ << "Subject: " << subject << "\r\n"
+ << "\r\n";
+ return header.str();
+}
+
+bool send_mail(
+ const std::string& header,
+ const std::string& message,
+ const char* from_address,
+ const char* to_address)
+{
+ if(!from_address || !to_address)
+ {
+ llinfos << "send_mail reject: missing to and/or from address."
+ << llendl;
+ return false;
+ }
+
+ // *FIX: this translation doesn't deal with a single period on a
+ // line by itself.
+ std::ostringstream rfc2822_msg;
+ for(U32 i = 0; i < message.size(); ++i)
+ {
+ switch(message[i])
+ {
+ case '\0':
+ break;
+ case '\n':
+ // *NOTE: this is kinda busted if we're fed \r\n
+ rfc2822_msg << "\r\n";
+ break;
+ default:
+ rfc2822_msg << message[i];
+ break;
+ }
+ }
+
+ if(!gMailEnabled)
+ {
+ llinfos << "send_mail reject: mail system is disabled: to=<"
+ << to_address << ">, from=<" << from_address
+ << ">" << llendl;
+ // Any future interface to SMTP should return this as an
+ // error. --mark
+ return true;
+ }
+ if(!gSockAddr)
+ {
+ llwarns << "send_mail reject: mail system not initialized: to=<"
+ << to_address << ">, from=<" << from_address
+ << ">" << llendl;
+ return false;
+ }
+
+ if(!connect_smtp())
+ {
+ llwarns << "send_mail reject: SMTP connect failure: to=<"
+ << to_address << ">, from=<" << from_address
+ << ">" << llendl;
+ return false;
+ }
+
+ std::ostringstream smtp_fmt;
+ smtp_fmt << header << rfc2822_msg.str() << "\r\n" << ".\r\n" << "QUIT\r\n";
+ std::string smtp_transaction = smtp_fmt.str();
+ size_t original_size = smtp_transaction.size();
+ apr_size_t send_size = original_size;
+ apr_status_t status = apr_socket_send(
+ gMailSocket,
+ smtp_transaction.c_str(),
+ (apr_size_t*)&send_size);
+ disconnect_smtp();
+ if(ll_apr_warn_status(status))
+ {
+ llwarns << "send_mail socket failure: unable to write "
+ << "to=<" << to_address
+ << ">, from=<" << from_address << ">"
+ << ", bytes=" << original_size
+ << ", sent=" << send_size << llendl;
+ return false;
+ }
+ if(send_size >= LL_MAX_KNOWN_GOOD_MAIL_SIZE)
+ {
+ llwarns << "send_mail message has been shown to fail in testing "
+ << "when sending messages larger than " << LL_MAX_KNOWN_GOOD_MAIL_SIZE
+ << " bytes. The next log about success is potentially a lie." << llendl;
+ }
+ llinfos << "send_mail success: "
+ << "to=<" << to_address
+ << ">, from=<" << from_address << ">"
+ << ", bytes=" << original_size
+ << ", sent=" << send_size << llendl;
+
+#if LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND
+ llinfos << rfc2822_msg.str() << llendl;
+#endif
+ return true;
+}
diff --git a/indra/llmessage/llmail.h b/indra/llmessage/llmail.h
new file mode 100644
index 0000000000..e34b827f5f
--- /dev/null
+++ b/indra/llmessage/llmail.h
@@ -0,0 +1,66 @@
+/**
+ * @file llmail.h
+ * @brief smtp helper functions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMAIL_H
+#define LL_LLMAIL_H
+
+#include "apr-1/apr_pools.h"
+
+// if hostname is NULL, then the host is resolved as 'mail'
+void init_mail(const std::string& hostname, apr_pool_t* pool);
+
+// Allow all email transmission to be disabled/enabled.
+void enable_mail(bool mail_enabled);
+
+// returns TRUE if the call succeeds, FALSE otherwise.
+//
+// Results in:
+// From: "from_name" <from_address>
+// To: "to_name" <to_address>
+// Subject: subject
+// message
+BOOL send_mail(const char* from_name, const char* from_address,
+ const char* to_name, const char* to_address,
+ const char* subject, const char* message);
+
+/**
+ * @brief build the complete smtp transaction & header for use in an
+ * mail.
+ *
+ * @param from_name The name of the email sender
+ * @param from_address The email address for the sender
+ * @param to_name The name of the email recipient
+ * @param to_name The email recipient address
+ * @param subject The subject of the email
+ * @return Returns the complete SMTP transaction mail header.
+ */
+std::string build_smtp_transaction(
+ const char* from_name,
+ const char* from_address,
+ const char* to_name,
+ const char* to_address,
+ const char* subject);
+
+/**
+ * @brief send an email with header and body.
+ *
+ * @param header The email header. Use build_mail_header().
+ * @param message The unescaped email message.
+ * @param from_address Used for debugging
+ * @param to_address Used for debugging
+ * @return Returns true if the message could be sent.
+ */
+bool send_mail(
+ const std::string& header,
+ const std::string& message,
+ const char* from_address,
+ const char* to_address);
+
+extern const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE;
+
+#endif
diff --git a/indra/llmessage/llmessagethrottle.cpp b/indra/llmessage/llmessagethrottle.cpp
new file mode 100644
index 0000000000..0cfaac3276
--- /dev/null
+++ b/indra/llmessage/llmessagethrottle.cpp
@@ -0,0 +1,135 @@
+/**
+ * @file llmessagethrottle.cpp
+ * @brief LLMessageThrottle class used for throttling messages.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llhash.h"
+
+#include "llmessagethrottle.h"
+#include "llframetimer.h"
+
+// This is used for the stl search_n function.
+bool eq_message_throttle_entry(LLMessageThrottleEntry a, LLMessageThrottleEntry b)
+ { return a.getHash() == b.getHash(); }
+
+const U64 SEC_TO_USEC = 1000000;
+
+// How long (in microseconds) each type of message stays in its throttle list.
+const U64 MAX_MESSAGE_AGE[MTC_EOF] =
+{
+ 10 * SEC_TO_USEC, // MTC_VIEWER_ALERT
+ 10 * SEC_TO_USEC // MTC_AGENT_ALERT
+};
+
+LLMessageThrottle::LLMessageThrottle()
+{
+}
+
+LLMessageThrottle::~LLMessageThrottle()
+{
+}
+
+void LLMessageThrottle::pruneEntries()
+{
+ // Go through each message category, and prune entries older than max age.
+ S32 cat;
+ for (cat = 0; cat < MTC_EOF; cat++)
+ {
+ message_list_t* message_list = &(mMessageList[cat]);
+
+ // Use a reverse iterator, since entries on the back will be the oldest.
+ message_list_reverse_iterator_t r_iterator = message_list->rbegin();
+ message_list_reverse_iterator_t r_last = message_list->rend();
+
+ // Look for the first entry younger than the maximum age.
+ F32 max_age = (F32)MAX_MESSAGE_AGE[cat];
+ BOOL found = FALSE;
+ while (r_iterator != r_last && !found)
+ {
+ if ( LLFrameTimer::getTotalTime() - (*r_iterator).getEntryTime() < max_age )
+ {
+ // We found a young enough entry.
+ found = TRUE;
+
+ // Did we find at least one entry to remove?
+ if (r_iterator != message_list->rbegin())
+ {
+ // Yes, remove it.
+ message_list->erase(r_iterator.base(), message_list->end());
+ }
+ }
+ else
+ {
+ r_iterator++;
+ }
+ }
+
+ // If we didn't find any entries young enough to keep, remove them all.
+ if (!found)
+ {
+ message_list->clear();
+ }
+ }
+}
+
+BOOL LLMessageThrottle::addViewerAlert(const LLUUID& to, const char* mesg)
+{
+ message_list_t* message_list = &(mMessageList[MTC_VIEWER_ALERT]);
+
+ // Concatenate from,to,mesg into one string.
+ std::ostringstream full_mesg;
+ full_mesg << to << mesg;
+
+ // Create an entry for this message.
+ size_t hash = llhash<const char*> (full_mesg.str().c_str());
+ LLMessageThrottleEntry entry(hash, LLFrameTimer::getTotalTime());
+
+ // Check if this message is already in the list.
+ message_list_iterator_t found = std::search_n(message_list->begin(), message_list->end(),
+ 1, entry, eq_message_throttle_entry);
+
+ if (found == message_list->end())
+ {
+ // This message was not found. Add it to the list.
+ message_list->push_front(entry);
+ return TRUE;
+ }
+ else
+ {
+ // This message was already in the list.
+ return FALSE;
+ }
+}
+
+BOOL LLMessageThrottle::addAgentAlert(const LLUUID& agent, const LLUUID& task, const char* mesg)
+{
+ message_list_t* message_list = &(mMessageList[MTC_AGENT_ALERT]);
+
+ // Concatenate from,to,mesg into one string.
+ std::ostringstream full_mesg;
+ full_mesg << agent << task << mesg;
+
+ // Create an entry for this message.
+ size_t hash = llhash<const char*> (full_mesg.str().c_str());
+ LLMessageThrottleEntry entry(hash, LLFrameTimer::getTotalTime());
+
+ // Check if this message is already in the list.
+ message_list_iterator_t found = std::search_n(message_list->begin(), message_list->end(),
+ 1, entry, eq_message_throttle_entry);
+
+ if (found == message_list->end())
+ {
+ // This message was not found. Add it to the list.
+ message_list->push_front(entry);
+ return TRUE;
+ }
+ else
+ {
+ // This message was already in the list.
+ return FALSE;
+ }
+}
+
diff --git a/indra/llmessage/llmessagethrottle.h b/indra/llmessage/llmessagethrottle.h
new file mode 100644
index 0000000000..4c3c01bab9
--- /dev/null
+++ b/indra/llmessage/llmessagethrottle.h
@@ -0,0 +1,62 @@
+/**
+ * @file llmessagethrottle.h
+ * @brief LLMessageThrottle class used for throttling messages.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMESSAGETHROTTLE_H
+#define LL_LLMESSAGETHROTTLE_H
+
+#include <deque>
+
+#include "linden_common.h"
+#include "lluuid.h"
+
+typedef enum e_message_throttle_categories
+{
+ MTC_VIEWER_ALERT,
+ MTC_AGENT_ALERT,
+ MTC_EOF
+} EMessageThrottleCats;
+
+class LLMessageThrottleEntry
+{
+public:
+ LLMessageThrottleEntry(const size_t hash, const U64 entry_time)
+ : mHash(hash), mEntryTime(entry_time) {}
+
+ size_t getHash() { return mHash; }
+ U64 getEntryTime() { return mEntryTime; }
+protected:
+ size_t mHash;
+ U64 mEntryTime;
+};
+
+
+class LLMessageThrottle
+{
+public:
+ LLMessageThrottle();
+ ~LLMessageThrottle();
+
+ BOOL addViewerAlert (const LLUUID& to, const char* mesg);
+ BOOL addAgentAlert (const LLUUID& agent, const LLUUID& task, const char* mesg);
+
+ void pruneEntries();
+
+protected:
+ typedef std::deque<LLMessageThrottleEntry> message_list_t;
+ typedef std::deque<LLMessageThrottleEntry>::iterator message_list_iterator_t;
+ typedef std::deque<LLMessageThrottleEntry>::reverse_iterator message_list_reverse_iterator_t;
+ typedef std::deque<LLMessageThrottleEntry>::const_iterator message_list_const_iterator_t;
+ typedef std::deque<LLMessageThrottleEntry>::const_reverse_iterator message_list_const_reverse_iterator_t;
+ message_list_t mMessageList[MTC_EOF];
+};
+
+extern LLMessageThrottle gMessageThrottle;
+
+#endif
+
+
diff --git a/indra/llmessage/llmime.cpp b/indra/llmessage/llmime.cpp
new file mode 100644
index 0000000000..9df9cdf3a7
--- /dev/null
+++ b/indra/llmessage/llmime.cpp
@@ -0,0 +1,613 @@
+/**
+ * @file llmime.cpp
+ * @author Phoenix
+ * @date 2006-12-20
+ * @brief Implementation of mime tools.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmime.h"
+
+#include <vector>
+
+#include "llmemorystream.h"
+
+/**
+ * Useful constants.
+ */
+// Headers specified in rfc-2045 will be canonicalized below.
+static const std::string CONTENT_LENGTH("Content-Length");
+static const std::string CONTENT_TYPE("Content-Type");
+static const S32 KNOWN_HEADER_COUNT = 6;
+static const std::string KNOWN_HEADER[KNOWN_HEADER_COUNT] =
+{
+ CONTENT_LENGTH,
+ CONTENT_TYPE,
+ std::string("MIME-Version"),
+ std::string("Content-Transfer-Encoding"),
+ std::string("Content-ID"),
+ std::string("Content-Description"),
+};
+
+// parser helpers
+static const std::string MULTIPART("multipart");
+static const std::string BOUNDARY("boundary");
+static const std::string END_OF_CONTENT_PARAMETER("\r\n ;\t");
+static const std::string SEPARATOR_PREFIX("--");
+//static const std::string SEPARATOR_SUFFIX("\r\n");
+
+/*
+Content-Type: multipart/mixed; boundary="segment"
+Content-Length: 24832
+
+--segment
+Content-Type: image/j2c
+Content-Length: 23715
+
+<data>
+
+--segment
+Content-Type: text/xml; charset=UTF-8
+
+<meta data>
+EOF
+
+*/
+
+/**
+ * LLMimeIndex
+ */
+
+/**
+ * @class LLMimeIndex::Impl
+ * @brief Implementation details of the mime index class.
+ * @see LLMimeIndex
+ */
+class LLMimeIndex::Impl
+{
+public:
+ Impl() : mOffset(-1), mUseCount(1)
+ {}
+ Impl(LLSD headers, S32 offset) :
+ mHeaders(headers), mOffset(offset), mUseCount(1)
+ {}
+public:
+ LLSD mHeaders;
+ S32 mOffset;
+ S32 mUseCount;
+
+ typedef std::vector<LLMimeIndex> sub_part_t;
+ sub_part_t mAttachments;
+};
+
+LLSD LLMimeIndex::headers() const
+{
+ return mImpl->mHeaders;
+}
+
+S32 LLMimeIndex::offset() const
+{
+ return mImpl->mOffset;
+}
+
+S32 LLMimeIndex::contentLength() const
+{
+ // Find the content length in the headers.
+ S32 length = -1;
+ LLSD content_length = mImpl->mHeaders[CONTENT_LENGTH];
+ if(content_length.isDefined())
+ {
+ length = content_length.asInteger();
+ }
+ return length;
+}
+
+std::string LLMimeIndex::contentType() const
+{
+ std::string type;
+ LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
+ if(content_type.isDefined())
+ {
+ type = content_type.asString();
+ }
+ return type;
+}
+
+bool LLMimeIndex::isMultipart() const
+{
+ bool multipart = false;
+ LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
+ if(content_type.isDefined())
+ {
+ std::string type = content_type.asString();
+ int comp = type.compare(0, MULTIPART.size(), MULTIPART);
+ if(0 == comp)
+ {
+ multipart = true;
+ }
+ }
+ return multipart;
+}
+
+S32 LLMimeIndex::subPartCount() const
+{
+ return mImpl->mAttachments.size();
+}
+
+LLMimeIndex LLMimeIndex::subPart(S32 index) const
+{
+ LLMimeIndex part;
+ if((index >= 0) && (index < (S32)mImpl->mAttachments.size()))
+ {
+ part = mImpl->mAttachments[index];
+ }
+ return part;
+}
+
+LLMimeIndex::LLMimeIndex() : mImpl(new LLMimeIndex::Impl)
+{
+}
+
+LLMimeIndex::LLMimeIndex(LLSD headers, S32 content_offset) :
+ mImpl(new LLMimeIndex::Impl(headers, content_offset))
+{
+}
+
+LLMimeIndex::LLMimeIndex(const LLMimeIndex& mime) :
+ mImpl(mime.mImpl)
+{
+ ++mImpl->mUseCount;
+}
+
+LLMimeIndex::~LLMimeIndex()
+{
+ if(0 == --mImpl->mUseCount)
+ {
+ delete mImpl;
+ }
+}
+
+LLMimeIndex& LLMimeIndex::operator=(const LLMimeIndex& mime)
+{
+ // Increment use count first so that we handle self assignment
+ // automatically.
+ ++mime.mImpl->mUseCount;
+ if(0 == --mImpl->mUseCount)
+ {
+ delete mImpl;
+ }
+ mImpl = mime.mImpl;
+ return *this;
+}
+
+bool LLMimeIndex::attachSubPart(LLMimeIndex sub_part)
+{
+ // *FIX: Should we check for multi-part?
+ if(mImpl->mAttachments.size() < S32_MAX)
+ {
+ mImpl->mAttachments.push_back(sub_part);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * LLMimeParser
+ */
+/**
+ * @class LLMimeParser::Impl
+ * @brief Implementation details of the mime parser class.
+ * @see LLMimeParser
+ */
+class LLMimeParser::Impl
+{
+public:
+ // @brief Constructor.
+ Impl();
+
+ // @brief Reset this for a new parse.
+ void reset();
+
+ /**
+ * @brief Parse a mime entity to find the index information.
+ *
+ * This method will scan the istr until a single complete mime
+ * entity is read, an EOF, or limit bytes have been scanned. The
+ * istr will be modified by this parsing, so pass in a temporary
+ * stream or rewind/reset the stream after this call.
+ * @param istr An istream which contains a mime entity.
+ * @param limit The maximum number of bytes to scan.
+ * @param separator The multipart separator if it is known.
+ * @param is_subpart Set true if parsing a multipart sub part.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(
+ std::istream& istr,
+ S32 limit,
+ const std::string& separator,
+ bool is_subpart,
+ LLMimeIndex& index);
+
+protected:
+ /**
+ * @brief parse the headers.
+ *
+ * At the end of a successful parse, mScanCount will be at the
+ * start of the content.
+ * @param istr The input stream.
+ * @param limit maximum number of bytes to process
+ * @param headers[out] A map of the headers found.
+ * @return Returns true if the parse was successful.
+ */
+ bool parseHeaders(std::istream& istr, S32 limit, LLSD& headers);
+
+ /**
+ * @brief Figure out the separator string from a content type header.
+ *
+ * @param multipart_content_type The content type value from the headers.
+ * @return Returns the separator string.
+ */
+ std::string findSeparator(std::string multipart_content_type);
+
+ /**
+ * @brief Scan through istr past the separator.
+ *
+ * @param istr The input stream.
+ * @param limit Maximum number of bytes to scan.
+ * @param separator The multipart separator.
+ */
+ void scanPastSeparator(
+ std::istream& istr,
+ S32 limit,
+ const std::string& separator);
+
+ /**
+ * @brief Scan through istr past the content of the current mime part.
+ *
+ * @param istr The input stream.
+ * @param limit Maximum number of bytes to scan.
+ * @param headers The headers for this mime part.
+ * @param separator The multipart separator if known.
+ */
+ void scanPastContent(
+ std::istream& istr,
+ S32 limit,
+ LLSD headers,
+ const std::string separator);
+
+ /**
+ * @brief Eat CRLF.
+ *
+ * This method has no concept of the limit, so ensure you have at
+ * least 2 characters left to eat before hitting the limit. This
+ * method will increment mScanCount as it goes.
+ * @param istr The input stream.
+ * @return Returns true if CRLF was found and consumed off of istr.
+ */
+ bool eatCRLF(std::istream& istr);
+
+ // @brief Returns true if parsing should continue.
+ bool continueParse() const { return (!mError && mContinue); }
+
+ // @brief anonymous enumeration for parse buffer size.
+ enum
+ {
+ LINE_BUFFER_LENGTH = 1024
+ };
+
+protected:
+ S32 mScanCount;
+ bool mContinue;
+ bool mError;
+ char mBuffer[LINE_BUFFER_LENGTH];
+};
+
+LLMimeParser::Impl::Impl()
+{
+ reset();
+}
+
+void LLMimeParser::Impl::reset()
+{
+ mScanCount = 0;
+ mContinue = true;
+ mError = false;
+ mBuffer[0] = '\0';
+}
+
+bool LLMimeParser::Impl::parseIndex(
+ std::istream& istr,
+ S32 limit,
+ const std::string& separator,
+ bool is_subpart,
+ LLMimeIndex& index)
+{
+ LLSD headers;
+ bool parsed_something = false;
+ if(parseHeaders(istr, limit, headers))
+ {
+ parsed_something = true;
+ LLMimeIndex mime(headers, mScanCount);
+ index = mime;
+ if(index.isMultipart())
+ {
+ // Figure out the separator, scan past it, and recurse.
+ std::string ct = headers[CONTENT_TYPE].asString();
+ std::string sep = findSeparator(ct);
+ scanPastSeparator(istr, limit, sep);
+ while(continueParse() && parseIndex(istr, limit, sep, true, mime))
+ {
+ index.attachSubPart(mime);
+ }
+ }
+ else
+ {
+ // Scan to the end of content.
+ scanPastContent(istr, limit, headers, separator);
+ if(is_subpart)
+ {
+ scanPastSeparator(istr, limit, separator);
+ }
+ }
+ }
+ if(mError) return false;
+ return parsed_something;
+}
+
+bool LLMimeParser::Impl::parseHeaders(
+ std::istream& istr,
+ S32 limit,
+ LLSD& headers)
+{
+ while(continueParse())
+ {
+ // Get the next line.
+ // We subtract 1 from the limit so that we make sure
+ // not to read past limit when we get() the newline.
+ S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
+ istr.getline(mBuffer, max_get, '\r');
+ mScanCount += istr.gcount();
+ int c = istr.get();
+ if(EOF == c)
+ {
+ mContinue = false;
+ return false;
+ }
+ ++mScanCount;
+ if(c != '\n')
+ {
+ mError = true;
+ return false;
+ }
+ if(mScanCount >= limit)
+ {
+ mContinue = false;
+ }
+
+ // Check if that's the end of headers.
+ if('\0' == mBuffer[0])
+ {
+ break;
+ }
+
+ // Split out the name and value.
+ // *NOTE: The use of strchr() here is safe since mBuffer is
+ // guaranteed to be NULL terminated from the call to getline()
+ // above.
+ char* colon = strchr(mBuffer, ':');
+ if(!colon)
+ {
+ mError = true;
+ return false;
+ }
+
+ // Cononicalize the name part, and store the name: value in
+ // the headers structure. We do this by iterating through
+ // 'known' headers and replacing the value found with the
+ // correct one.
+ // *NOTE: Not so efficient, but iterating through a small
+ // subset should not be too much of an issue.
+ std::string name(mBuffer, colon++ - mBuffer);
+ while(isspace(*colon)) ++colon;
+ std::string value(colon);
+ for(S32 ii = 0; ii < KNOWN_HEADER_COUNT; ++ii)
+ {
+ if(0 == LLString::compareInsensitive(
+ name.c_str(),
+ KNOWN_HEADER[ii].c_str()))
+ {
+ name = KNOWN_HEADER[ii];
+ break;
+ }
+ }
+ headers[name] = value;
+ }
+ if(headers.isUndefined()) return false;
+ return true;
+}
+
+std::string LLMimeParser::Impl::findSeparator(std::string header)
+{
+ // 01234567890
+ //Content-Type: multipart/mixed; boundary="segment"
+ std::string separator;
+ std::string::size_type pos = header.find(BOUNDARY);
+ if(std::string::npos == pos) return separator;
+ pos += BOUNDARY.size() + 1;
+ std::string::size_type end;
+ if(header[pos] == '"')
+ {
+ // the boundary is quoted, find the end from pos, and take the
+ // substring.
+ end = header.find('"', ++pos);
+ if(std::string::npos == end)
+ {
+ // poorly formed boundary.
+ mError = true;
+ }
+ }
+ else
+ {
+ // otherwise, it's every character until a whitespace, end of
+ // line, or another parameter begins.
+ end = header.find_first_of(END_OF_CONTENT_PARAMETER, pos);
+ if(std::string::npos == end)
+ {
+ // it goes to the end of the string.
+ end = header.size();
+ }
+ }
+ if(!mError) separator = header.substr(pos, end - pos);
+ return separator;
+}
+
+void LLMimeParser::Impl::scanPastSeparator(
+ std::istream& istr,
+ S32 limit,
+ const std::string& sep)
+{
+ std::ostringstream ostr;
+ ostr << SEPARATOR_PREFIX << sep;
+ std::string separator = ostr.str();
+ bool found_separator = false;
+ while(!found_separator && continueParse())
+ {
+ // Subtract 1 from the limit so that we make sure not to read
+ // past limit when we get() the newline.
+ S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
+ istr.getline(mBuffer, max_get, '\r');
+ mScanCount += istr.gcount();
+ if(istr.gcount() >= LINE_BUFFER_LENGTH - 1)
+ {
+ // that's way too long to be a separator, so ignore it.
+ continue;
+ }
+ int c = istr.get();
+ if(EOF == c)
+ {
+ mContinue = false;
+ return;
+ }
+ ++mScanCount;
+ if(c != '\n')
+ {
+ mError = true;
+ return;
+ }
+ if(mScanCount >= limit)
+ {
+ mContinue = false;
+ }
+ if(0 == LLString::compareStrings(mBuffer, separator.c_str()))
+ {
+ found_separator = true;
+ }
+ }
+}
+
+void LLMimeParser::Impl::scanPastContent(
+ std::istream& istr,
+ S32 limit,
+ LLSD headers,
+ const std::string separator)
+{
+ if(headers.has(CONTENT_LENGTH))
+ {
+ S32 content_length = headers[CONTENT_LENGTH].asInteger();
+ // Subtract 2 here for the \r\n after the content.
+ S32 max_skip = llmin(content_length, limit - mScanCount - 2);
+ istr.ignore(max_skip);
+ mScanCount += max_skip;
+
+ // *NOTE: Check for hitting the limit and eof here before
+ // checking for the trailing EOF, because our mime parser has
+ // to gracefully handle incomplete mime entites.
+ if((mScanCount >= limit) || istr.eof())
+ {
+ mContinue = false;
+ }
+ else if(!eatCRLF(istr))
+ {
+ mError = true;
+ return;
+ }
+ }
+}
+
+bool LLMimeParser::Impl::eatCRLF(std::istream& istr)
+{
+ int c = istr.get();
+ ++mScanCount;
+ if(c != '\r')
+ {
+ return false;
+ }
+ c = istr.get();
+ ++mScanCount;
+ if(c != '\n')
+ {
+ return false;
+ }
+ return true;
+}
+
+
+LLMimeParser::LLMimeParser() : mImpl(* new LLMimeParser::Impl)
+{
+}
+
+LLMimeParser::~LLMimeParser()
+{
+ delete & mImpl;
+}
+
+void LLMimeParser::reset()
+{
+ mImpl.reset();
+}
+
+bool LLMimeParser::parseIndex(std::istream& istr, LLMimeIndex& index)
+{
+ std::string separator;
+ return mImpl.parseIndex(istr, S32_MAX, separator, false, index);
+}
+
+bool LLMimeParser::parseIndex(
+ const std::vector<U8>& buffer,
+ LLMimeIndex& index)
+{
+ LLMemoryStream mstr(&buffer[0], buffer.size());
+ return parseIndex(mstr, buffer.size() + 1, index);
+}
+
+bool LLMimeParser::parseIndex(
+ std::istream& istr,
+ S32 limit,
+ LLMimeIndex& index)
+{
+ std::string separator;
+ return mImpl.parseIndex(istr, limit, separator, false, index);
+}
+
+bool LLMimeParser::parseIndex(const U8* buffer, S32 length, LLMimeIndex& index)
+{
+ LLMemoryStream mstr(buffer, length);
+ return parseIndex(mstr, length + 1, index);
+}
+
+/*
+bool LLMimeParser::verify(std::istream& isr, LLMimeIndex& index) const
+{
+ return false;
+}
+
+bool LLMimeParser::verify(U8* buffer, S32 length, LLMimeIndex& index) const
+{
+ LLMemoryStream mstr(buffer, length);
+ return verify(mstr, index);
+}
+*/
diff --git a/indra/llmessage/llmime.h b/indra/llmessage/llmime.h
new file mode 100644
index 0000000000..62e1204b88
--- /dev/null
+++ b/indra/llmessage/llmime.h
@@ -0,0 +1,274 @@
+/**
+ * @file llmime.h
+ * @author Phoenix
+ * @date 2006-12-20
+ * @brief Declaration of mime tools.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMIME_H
+#define LL_LLMIME_H
+
+#include <string>
+#include "llsd.h"
+
+/**
+ * This file declares various tools for parsing and creating MIME
+ * objects as described in RFCs 2045, 2046, 2047, 2048, and 2049.
+ */
+
+/**
+ * @class LLMimeIndex
+ * @brief Skeletal information useful for handling mime packages.
+ * @see LLMimeParser
+ *
+ * An instance of this class is the parsed output from a LLMimeParser
+ * which then allows for easy access into a data stream to find and
+ * get what you want out of it.
+ *
+ * This class meant as a tool to quickly find what you seek in a
+ * parsed mime entity. As such, it does not have useful support for
+ * modification of a mime entity and specializes the interface toward
+ * querying data from a fixed mime entity. Modifying an instance of
+ * LLMimeIndx does not alter a mime entity and changes to a mime
+ * entity itself are not propogated into an instance of a LLMimeIndex.
+ *
+ * Usage:<br>
+ * LLMimeIndex mime_index;<br>
+ * std::ifstream fstr("package.mime", ios::binary);<br>
+ * LLMimeParser parser;<br>
+ * if(parser.parseIndex(fstr, mime_index))<br>
+ * {<br>
+ * std::vector<U8> content;<br>
+ * content.resize(mime_index.contentLength());<br>
+ * fstr.seekg(mime_index.offset(), ios::beg);<br>
+ * // ...do work on fstr and content<br>
+ * }<br>
+ */
+class LLMimeIndex
+{
+public:
+ /* @name Client interface.
+ */
+ //@{
+ /**
+ * @brief Get the full parsed headers for this.
+ *
+ * If there are any headers, it will be a map of header name to
+ * the value found on the line. The name is everything before the
+ * colon, and the value is the string found after the colon to the
+ * end of the line after trimming leading whitespace. So, for
+ * example:
+ * Content-Type: text/plain
+ * would become an entry in the headers of:
+ * headers["Content-Type"] == "text/plain"
+ *
+ * If this instance of an index was generated by the
+ * LLMimeParser::parseIndex() call, all header names in rfc2045
+ * will be capitalized as in rfc, eg Content-Length and
+ * MIME-Version, not content-length and mime-version.
+ * @return Returns an LLSD map of header name to value. Returns
+ * undef if there are no headers.
+ */
+ LLSD headers() const;
+
+ /**
+ * @brief Get the content offset.
+ *
+ * @return Returns the number of bytes to the start of the data
+ * segment from the start of serialized mime entity. Returns -1 if
+ * offset is not known.
+ */
+ S32 offset() const;
+
+ /**
+ * @brief Get the length of the data segment for this mime part.
+ *
+ * @return Returns the content length in bytes. Returns -1 if
+ * length is not known.
+ */
+ S32 contentLength() const;
+
+ /**
+ * @brief Get the mime type associated with this node.
+ *
+ * @return Returns the mimetype.
+ */
+ std::string contentType() const;
+
+ /**
+ * @brief Helper method which simplifies parsing the return from type()
+ *
+ * @return Returns true if this is a multipart mime, and therefore
+ * getting subparts will succeed.
+ */
+ bool isMultipart() const;
+
+ /**
+ * @brief Get the number of atachments.
+ *
+ * @return Returns the number of sub-parts for this.
+ */
+ S32 subPartCount() const;
+
+ /**
+ * @brief Get the indicated attachment.
+ *
+ * @param index Value from 0 to (subPartCount() - 1).
+ * @return Returns the indicated sub-part, or an invalid mime
+ * index on failure.
+ */
+ LLMimeIndex subPart(S32 index) const;
+ //@}
+
+ /* @name Interface for building, testing, and helpers for typical use.
+ */
+ //@{
+ /**
+ * @brief Default constructor - creates a useless LLMimeIndex.
+ */
+ LLMimeIndex();
+
+ /**
+ * @brief Full constructor.
+ *
+ * @param headers The complete headers.
+ * @param content_offset The number of bytes to the start of the
+ * data segment of this mime entity from the start of the stream
+ * or buffer.
+ */
+ LLMimeIndex(LLSD headers, S32 content_offset);
+
+ /**
+ * @brief Copy constructor.
+ *
+ * @param mime The other mime object.
+ */
+ LLMimeIndex(const LLMimeIndex& mime);
+
+ // @brief Destructor.
+ ~LLMimeIndex();
+
+ /*
+ * @breif Assignment operator.
+ *
+ * @param mime The other mime object.
+ * @return Returns this after assignment.
+ */
+ LLMimeIndex& operator=(const LLMimeIndex& mime);
+
+ /**
+ * @brief Add attachment information as a sub-part to a multipart mime.
+ *
+ * @param sub_part the part to attach.
+ * @return Returns true on success, false on failure.
+ */
+ bool attachSubPart(LLMimeIndex sub_part);
+ //@}
+
+protected:
+ // Implementation.
+ class Impl;
+ Impl* mImpl;
+};
+
+
+/**
+ * @class LLMimeParser
+ * @brief This class implements a MIME parser and verifier.
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLMimeParser
+{
+public:
+ // @brief Make a new mime parser.
+ LLMimeParser();
+
+ // @brief Mime parser Destructor.
+ ~LLMimeParser();
+
+ // @brief Reset internal state of this parser.
+ void reset();
+
+
+ /* @name Index generation interface.
+ */
+ //@{
+ /**
+ * @brief Parse a stream to find the mime index information.
+ *
+ * This method will scan the istr until a single complete mime
+ * entity is read or EOF. The istr will be modified by this
+ * parsing, so pass in a temporary stream or rewind/reset the
+ * stream after this call.
+ * @param istr An istream which contains a mime entity.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(std::istream& istr, LLMimeIndex& index);
+
+ /**
+ * @brief Parse a vector to find the mime index information.
+ *
+ * @param buffer A vector with data to parse.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(const std::vector<U8>& buffer, LLMimeIndex& index);
+
+ /**
+ * @brief Parse a stream to find the mime index information.
+ *
+ * This method will scan the istr until a single complete mime
+ * entity is read, an EOF, or limit bytes have been scanned. The
+ * istr will be modified by this parsing, so pass in a temporary
+ * stream or rewind/reset the stream after this call.
+ * @param istr An istream which contains a mime entity.
+ * @param limit The maximum number of bytes to scan.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(std::istream& istr, S32 limit, LLMimeIndex& index);
+
+ /**
+ * @brief Parse a memory bufffer to find the mime index information.
+ *
+ * @param buffer The start of the buffer to parse.
+ * @param buffer_length The length of the buffer.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(const U8* buffer, S32 buffer_length, LLMimeIndex& index);
+ //@}
+
+ /**
+ * @brief
+ *
+ * @return
+ */
+ //bool verify(std::istream& istr, LLMimeIndex& index) const;
+
+ /**
+ * @brief
+ *
+ * @return
+ */
+ //bool verify(U8* buffer, S32 buffer_length, LLMimeIndex& index) const;
+
+protected:
+ // Implementation.
+ class Impl;
+ Impl& mImpl;
+
+private:
+ // @brief Not implemneted to prevent copy consturction.
+ LLMimeParser(const LLMimeParser& parser);
+
+ // @brief Not implemneted to prevent assignment.
+ LLMimeParser& operator=(const LLMimeParser& mime);
+};
+
+#endif // LL_LLMIME_H
diff --git a/indra/llmessage/llnamevalue.cpp b/indra/llmessage/llnamevalue.cpp
new file mode 100644
index 0000000000..02ddec1bf5
--- /dev/null
+++ b/indra/llmessage/llnamevalue.cpp
@@ -0,0 +1,2141 @@
+/**
+ * @file llnamevalue.cpp
+ * @brief class for defining name value pairs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Examples:
+// AvatarCharacter STRING RW DSV male1
+
+#include "linden_common.h"
+
+#include <map>
+
+#include "llnamevalue.h"
+#include "u64.h"
+#include "llstring.h"
+#include "llcamera.h"
+
+// Anonymous enumeration to provide constants in this file.
+// *NOTE: These values may be used in sscanf statements below as their
+// value-1, so search for '2047' if you cange NV_BUFFER_LEN or '63' if
+// you change U64_BUFFER_LEN.
+enum
+{
+ NV_BUFFER_LEN = 2048,
+ U64_BUFFER_LEN = 64
+};
+
+struct user_callback_t
+{
+ user_callback_t() {};
+ user_callback_t(TNameValueCallback cb, void** data) : m_Callback(cb), m_Data(data) {}
+ TNameValueCallback m_Callback;
+ void ** m_Data;
+};
+typedef std::map<char *, user_callback_t> user_callback_map_t;
+user_callback_map_t gUserCallbackMap;
+
+LLStringTable gNVNameTable(16384);
+
+char NameValueTypeStrings[NVT_EOF][NAME_VALUE_TYPE_STRING_LENGTH] =
+{
+ "NULL",
+ "STRING",
+ "F32",
+ "S32",
+ "VEC3",
+ "U32",
+ "CAMERA", // Deprecated, but leaving in case removing completely would cause problems
+ "ASSET",
+ "U64"
+}; /*Flawfinder: Ignore*/
+
+char NameValueClassStrings[NVC_EOF][NAME_VALUE_CLASS_STRING_LENGTH] =
+{
+ "NULL",
+ "R", // read only
+ "RW", // read write
+ "CB" // callback
+}; /*Flawfinder: Ignore*/
+
+char NameValueSendtoStrings[NVS_EOF][NAME_VALUE_SENDTO_STRING_LENGTH] =
+{
+ "NULL",
+ "S", // "Sim", formerly SIM
+ "DS", // "Data Sim" formerly SIM_SPACE
+ "SV", // "Sim Viewer" formerly SIM_VIEWER
+ "DSV" // "Data Sim Viewer", formerly SIM_SPACE_VIEWER
+}; /*Flawfinder: Ignore*/
+
+
+void add_use_callback(char *name, TNameValueCallback ucb, void **user_data)
+{
+ char *temp = gNVNameTable.addString(name);
+ gUserCallbackMap[temp] = user_callback_t(ucb,user_data);
+}
+
+
+//
+// Class
+//
+
+LLNameValue::LLNameValue()
+{
+ baseInit();
+}
+
+void LLNameValue::baseInit()
+{
+ mNVNameTable = &gNVNameTable;
+
+ mName = NULL;
+ mNameValueReference.string = NULL;
+
+ mType = NVT_NULL;
+ mStringType = NameValueTypeStrings[NVT_NULL];
+
+ mClass = NVC_NULL;
+ mStringClass = NameValueClassStrings[NVC_NULL];
+
+ mSendto = NVS_NULL;
+ mStringSendto = NameValueSendtoStrings[NVS_NULL];
+}
+
+void LLNameValue::init(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto, TNameValueCallback nvcb, void **user_data)
+{
+ mNVNameTable = &gNVNameTable;
+
+ mName = mNVNameTable->addString(name);
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ mStringType = mNVNameTable->addString(type);
+ if (!strcmp(mStringType, "STRING"))
+ {
+ S32 string_length = (S32)strlen(data); /*Flawfinder: Ignore*/
+ mType = NVT_STRING;
+
+ delete[] mNameValueReference.string;
+
+ // two options here. . . data can either look like foo or "foo"
+ // WRONG! - this is a poorly implemented and incomplete escape
+ // mechanism. For example, using this scheme, there is no way
+ // to tell an intentional double quotes from a zero length
+ // string. This needs to excised. Phoenix
+ //if (strchr(data, '\"'))
+ //{
+ // string_length -= 2;
+ // mNameValueReference.string = new char[string_length + 1];;
+ // strncpy(mNameValueReference.string, data + 1, string_length);
+ //}
+ //else
+ //{
+ mNameValueReference.string = new char[string_length + 1];;
+ strncpy(mNameValueReference.string, data, string_length); /*Flawfinder: Ignore*/
+ //}
+ mNameValueReference.string[string_length] = 0;
+ }
+ else if (!strcmp(mStringType, "F32"))
+ {
+ mType = NVT_F32;
+ mNameValueReference.f32 = new F32((F32)atof(data));
+ }
+ else if (!strcmp(mStringType, "S32"))
+ {
+ mType = NVT_S32;
+ mNameValueReference.s32 = new S32(atoi(data));
+ }
+ else if (!strcmp(mStringType, "U64"))
+ {
+ mType = NVT_U64;
+ mNameValueReference.u64 = new U64(str_to_U64(data));
+ }
+ else if (!strcmp(mStringType, "VEC3"))
+ {
+ mType = NVT_VEC3;
+ F32 t1, t2, t3;
+
+ // two options here. . . data can either look like 0, 1, 2 or <0, 1, 2>
+
+ if (strchr(data, '<'))
+ {
+ sscanf(data, "<%f, %f, %f>", &t1, &t2, &t3);
+ }
+ else
+ {
+ sscanf(data, "%f, %f, %f", &t1, &t2, &t3);
+ }
+
+ // finite checks
+ if (!llfinite(t1) || !llfinite(t2) || !llfinite(t3))
+ {
+ t1 = 0.f;
+ t2 = 0.f;
+ t3 = 0.f;
+ }
+
+ mNameValueReference.vec3 = new LLVector3(t1, t2, t3);
+ }
+ else if (!strcmp(mStringType, "U32"))
+ {
+ mType = NVT_U32;
+ mNameValueReference.u32 = new U32(atoi(data));
+ }
+ else if(!strcmp(mStringType, (const char*)NameValueTypeStrings[NVT_ASSET]))
+ {
+ // assets are treated like strings, except that the name has
+ // meaning to an LLAssetInfo object
+ S32 string_length = (S32)strlen(data); /*Flawfinder: Ignore*/
+ mType = NVT_ASSET;
+
+ // two options here. . . data can either look like foo or "foo"
+ // WRONG! - this is a poorly implemented and incomplete escape
+ // mechanism. For example, using this scheme, there is no way
+ // to tell an intentional double quotes from a zero length
+ // string. This needs to excised. Phoenix
+ //if (strchr(data, '\"'))
+ //{
+ // string_length -= 2;
+ // mNameValueReference.string = new char[string_length + 1];;
+ // strncpy(mNameValueReference.string, data + 1, string_length);
+ //}
+ //else
+ //{
+ mNameValueReference.string = new char[string_length + 1];;
+ strncpy(mNameValueReference.string, data, string_length); /*Flawfinder: Ignore*/
+ //}
+ mNameValueReference.string[string_length] = 0;
+ }
+ else
+ {
+ llwarns << "Unknown name value type string " << mStringType << " for " << mName << llendl;
+ mType = NVT_NULL;
+ }
+
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ if (!strcmp(nvclass, "R") ||
+ !strcmp(nvclass, "READ_ONLY")) // legacy
+ {
+ mClass = NVC_READ_ONLY;
+ mStringClass = mNVNameTable->addString("R");
+ }
+ else if (!strcmp(nvclass, "RW") ||
+ !strcmp(nvclass, "READ_WRITE")) // legacy
+ {
+ mClass = NVC_READ_WRITE;
+ mStringClass = mNVNameTable->addString("RW");
+ }
+ else if (!strcmp(nvclass, "CB") ||
+ !strcmp(nvclass, "CALLBACK")) // legacy
+ {
+ mClass = NVC_CALLBACK;
+ mStringClass = mNVNameTable->addString("CB");
+ mNameValueCB = nvcb;
+ mUserData = user_data;
+ }
+ else
+ {
+ // assume it's bad
+ mClass = NVC_NULL;
+ mStringClass = mNVNameTable->addString(nvclass);
+ mNameValueCB = NULL;
+ mUserData = NULL;
+
+ // are we a user-defined call back?
+ for (user_callback_map_t::iterator iter = gUserCallbackMap.begin();
+ iter != gUserCallbackMap.end(); iter++)
+ {
+ char* tname = iter->first;
+ if (tname == mStringClass)
+ {
+ mClass = NVC_CALLBACK;
+ mNameValueCB = (iter->second).m_Callback;
+ mUserData = (iter->second).m_Data;
+ }
+ }
+
+ // Warn if we didn't find a callback
+ if (mClass == NVC_NULL)
+ {
+ llwarns << "Unknown user callback in name value init() for " << mName << llendl;
+ }
+ }
+
+ // Initialize the sendto variable
+ if (!strcmp(nvsendto, "S") ||
+ !strcmp(nvsendto, "SIM")) // legacy
+ {
+ mSendto = NVS_SIM;
+ mStringSendto = mNVNameTable->addString("S");
+ }
+ else if (!strcmp(nvsendto, "DS") ||
+ !strcmp(nvsendto, "SIM_SPACE")) // legacy
+ {
+ mSendto = NVS_DATA_SIM;
+ mStringSendto = mNVNameTable->addString("DS");
+ }
+ else if (!strcmp(nvsendto, "SV") ||
+ !strcmp(nvsendto, "SIM_VIEWER")) // legacy
+ {
+ mSendto = NVS_SIM_VIEWER;
+ mStringSendto = mNVNameTable->addString("SV");
+ }
+ else if (!strcmp(nvsendto, "DSV") ||
+ !strcmp(nvsendto, "SIM_SPACE_VIEWER")) // legacy
+ {
+ mSendto = NVS_DATA_SIM_VIEWER;
+ mStringSendto = mNVNameTable->addString("DSV");
+ }
+ else
+ {
+ llwarns << "LLNameValue::init() - unknown sendto field "
+ << nvsendto << " for NV " << mName << llendl;
+ mSendto = NVS_NULL;
+ mStringSendto = mNVNameTable->addString("S");
+ }
+
+}
+
+
+LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, TNameValueCallback nvcb, void **user_data)
+{
+ baseInit();
+ // if not specified, send to simulator only
+ init(name, data, type, nvclass, "SIM", nvcb, user_data);
+}
+
+
+LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto, TNameValueCallback nvcb, void **user_data)
+{
+ baseInit();
+ init(name, data, type, nvclass, nvsendto, nvcb, user_data);
+}
+
+
+
+// Initialize without any initial data.
+LLNameValue::LLNameValue(const char *name, const char *type, const char *nvclass, TNameValueCallback nvcb, void **user_data)
+{
+ baseInit();
+ mName = mNVNameTable->addString(name);
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ mStringType = mNVNameTable->addString(type);
+ if (!strcmp(mStringType, "STRING"))
+ {
+ mType = NVT_STRING;
+ mNameValueReference.string = NULL;
+ }
+ else if (!strcmp(mStringType, "F32"))
+ {
+ mType = NVT_F32;
+ mNameValueReference.f32 = NULL;
+ }
+ else if (!strcmp(mStringType, "S32"))
+ {
+ mType = NVT_S32;
+ mNameValueReference.s32 = NULL;
+ }
+ else if (!strcmp(mStringType, "VEC3"))
+ {
+ mType = NVT_VEC3;
+ mNameValueReference.vec3 = NULL;
+ }
+ else if (!strcmp(mStringType, "U32"))
+ {
+ mType = NVT_U32;
+ mNameValueReference.u32 = NULL;
+ }
+ else if (!strcmp(mStringType, "U64"))
+ {
+ mType = NVT_U64;
+ mNameValueReference.u64 = NULL;
+ }
+ else if(!strcmp(mStringType, (const char*)NameValueTypeStrings[NVT_ASSET]))
+ {
+ mType = NVT_ASSET;
+ mNameValueReference.string = NULL;
+ }
+ else
+ {
+ mType = NVT_NULL;
+ llinfos << "Unknown name-value type " << mStringType << llendl;
+ }
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ mStringClass = mNVNameTable->addString(nvclass);
+ if (!strcmp(mStringClass, "READ_ONLY"))
+ {
+ mClass = NVC_READ_ONLY;
+ }
+ else if (!strcmp(mStringClass, "READ_WRITE"))
+ {
+ mClass = NVC_READ_WRITE;
+ }
+ else if (!strcmp(mStringClass, "CALLBACK"))
+ {
+ mClass = NVC_READ_WRITE;
+ mNameValueCB = nvcb;
+ mUserData = user_data;
+ }
+
+ // Initialize the sendto variable
+ mStringSendto = mNVNameTable->addString("SIM");
+ mSendto = NVS_SIM;
+}
+
+
+// data is in the format:
+// "NameValueName Type Class Data"
+LLNameValue::LLNameValue(const char *data)
+{
+ baseInit();
+ static char name[NV_BUFFER_LEN];
+ static char type[NV_BUFFER_LEN];
+ static char nvclass[NV_BUFFER_LEN];
+ static char nvsendto[NV_BUFFER_LEN];
+ static char nvdata[NV_BUFFER_LEN];
+
+ S32 i;
+
+ S32 character_count = 0;
+ S32 length = 0;
+
+ // go to first non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // read in the name
+ sscanf((data + character_count), "%2047s", name);
+
+ // bump past it and add null terminator
+ length = (S32)strlen(name); /* Flawfinder: ignore */
+ name[length] = 0;
+ character_count += length;
+
+ // go to the next non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // read in the type
+ sscanf((data + character_count), "%2047s", type);
+
+ // bump past it and add null terminator
+ length = (S32)strlen(type); /* Flawfinder: ignore */
+ type[length] = 0;
+ character_count += length;
+
+ // go to the next non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // do we have a type argument?
+ for (i = NVC_READ_ONLY; i < NVC_EOF; i++)
+ {
+ if (!strncmp(NameValueClassStrings[i], data + character_count, strlen(NameValueClassStrings[i]))) /* Flawfinder: ignore */
+ {
+ break;
+ }
+ }
+
+ if (i != NVC_EOF)
+ {
+ // yes we do!
+ // read in the class
+ sscanf((data + character_count), "%2047s", nvclass);
+
+ // bump past it and add null terminator
+ length = (S32)strlen(nvclass); /* Flawfinder: ignore */
+ nvclass[length] = 0;
+ character_count += length;
+
+ // go to the next non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ // no type argument given, default to read-write
+ strncpy(nvclass, "READ_WRITE", sizeof(nvclass) -1); /* Flawfinder: ignore */
+ nvclass[sizeof(nvclass) -1] = '\0';
+ }
+
+ // Do we have a sendto argument?
+ for (i = NVS_SIM; i < NVS_EOF; i++)
+ {
+ if (!strncmp(NameValueSendtoStrings[i], data + character_count, strlen(NameValueSendtoStrings[i]))) /* Flawfinder: ignore */
+ {
+ break;
+ }
+ }
+
+ if (i != NVS_EOF)
+ {
+ // found a sendto argument
+ sscanf((data + character_count), "%2047s", nvsendto);
+
+ // add null terminator
+ length = (S32)strlen(nvsendto); /* Flawfinder: ignore */
+ nvsendto[length] = 0;
+ character_count += length;
+
+ // seek to next non-whitespace characer
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ // no sendto argument given, default to sim only
+ strncpy(nvsendto, "SIM", sizeof(nvsendto) -1); /* Flawfinder: ignore */
+ nvsendto[sizeof(nvsendto) -1] ='\0';
+ }
+
+
+ // copy the rest character by character into data
+ length = 0;
+
+ while ( (*(nvdata + length++) = *(data + character_count++)) )
+ ;
+
+ init(name, nvdata, type, nvclass, nvsendto);
+}
+
+
+LLNameValue::~LLNameValue()
+{
+ mNVNameTable->removeString(mName);
+ mName = NULL;
+
+ switch(mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ delete [] mNameValueReference.string;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_F32:
+ delete mNameValueReference.f32;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_S32:
+ delete mNameValueReference.s32;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_VEC3:
+ delete mNameValueReference.vec3;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_U32:
+ delete mNameValueReference.u32;
+ mNameValueReference.u32 = NULL;
+ break;
+ case NVT_U64:
+ delete mNameValueReference.u64;
+ mNameValueReference.u64 = NULL;
+ break;
+ default:
+ break;
+ }
+
+ delete[] mNameValueReference.string;
+ mNameValueReference.string = NULL;
+}
+
+char *LLNameValue::getString()
+{
+ if (mType == NVT_STRING)
+ {
+ return mNameValueReference.string;
+ }
+ else
+ {
+ llerrs << mName << " not a string!" << llendl;
+ return NULL;
+ }
+}
+
+const char *LLNameValue::getAsset() const
+{
+ if (mType == NVT_ASSET)
+ {
+ return mNameValueReference.string;
+ }
+ else
+ {
+ llerrs << mName << " not an asset!" << llendl;
+ return NULL;
+ }
+}
+
+F32 *LLNameValue::getF32()
+{
+ if (mType == NVT_F32)
+ {
+ return mNameValueReference.f32;
+ }
+ else
+ {
+ llerrs << mName << " not a F32!" << llendl;
+ return NULL;
+ }
+}
+
+S32 *LLNameValue::getS32()
+{
+ if (mType == NVT_S32)
+ {
+ return mNameValueReference.s32;
+ }
+ else
+ {
+ llerrs << mName << " not a S32!" << llendl;
+ return NULL;
+ }
+}
+
+U32 *LLNameValue::getU32()
+{
+ if (mType == NVT_U32)
+ {
+ return mNameValueReference.u32;
+ }
+ else
+ {
+ llerrs << mName << " not a U32!" << llendl;
+ return NULL;
+ }
+}
+
+U64 *LLNameValue::getU64()
+{
+ if (mType == NVT_U64)
+ {
+ return mNameValueReference.u64;
+ }
+ else
+ {
+ llerrs << mName << " not a U64!" << llendl;
+ return NULL;
+ }
+}
+
+void LLNameValue::getVec3(LLVector3 &vec)
+{
+ if (mType == NVT_VEC3)
+ {
+ vec = *mNameValueReference.vec3;
+ }
+ else
+ {
+ llerrs << mName << " not a Vec3!" << llendl;
+ }
+}
+
+LLVector3 *LLNameValue::getVec3()
+{
+ if (mType == NVT_VEC3)
+ {
+ return (mNameValueReference.vec3);
+ }
+ else
+ {
+ llerrs << mName << " not a Vec3!" << llendl;
+ return NULL;
+ }
+}
+
+
+F32 LLNameValue::magnitude()
+{
+ switch(mType)
+ {
+ case NVT_STRING:
+ return (F32)(strlen(mNameValueReference.string)); /* Flawfinder: ignore */
+ break;
+ case NVT_F32:
+ return (fabsf(*mNameValueReference.f32));
+ break;
+ case NVT_S32:
+ return (fabsf((F32)(*mNameValueReference.s32)));
+ break;
+ case NVT_VEC3:
+ return (mNameValueReference.vec3->magVec());
+ break;
+ case NVT_U32:
+ return (F32)(*mNameValueReference.u32);
+ break;
+ default:
+ llerrs << "No magnitude operation for NV type " << mStringType << llendl;
+ break;
+ }
+ return 0.f;
+}
+
+
+void LLNameValue::callCallback()
+{
+ if (mNameValueCB)
+ {
+ (*mNameValueCB)(this, mUserData);
+ }
+ else
+ {
+ llinfos << mName << " has no callback!" << llendl;
+ }
+}
+
+
+BOOL LLNameValue::sendToData() const
+{
+ return (mSendto == NVS_DATA_SIM || mSendto == NVS_DATA_SIM_VIEWER);
+}
+
+
+BOOL LLNameValue::sendToViewer() const
+{
+ return (mSendto == NVS_SIM_VIEWER || mSendto == NVS_DATA_SIM_VIEWER);
+}
+
+
+LLNameValue &LLNameValue::operator=(const LLNameValue &a)
+{
+ if (mType != a.mType)
+ {
+ return *this;
+ }
+ if (mClass == NVC_READ_ONLY)
+ return *this;
+
+ BOOL b_changed = FALSE;
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this != a))
+ {
+ b_changed = TRUE;
+ }
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ if (mNameValueReference.string)
+ delete [] mNameValueReference.string;
+
+ mNameValueReference.string = new char [strlen(a.mNameValueReference.string) + 1]; /* Flawfinder: ignore */
+ if(mNameValueReference.string != NULL)
+ {
+ strcpy(mNameValueReference.string, a.mNameValueReference.string); /* Flawfinder: ignore */
+ }
+ break;
+ case NVT_F32:
+ *mNameValueReference.f32 = *a.mNameValueReference.f32;
+ break;
+ case NVT_S32:
+ *mNameValueReference.s32 = *a.mNameValueReference.s32;
+ break;
+ case NVT_VEC3:
+ *mNameValueReference.vec3 = *a.mNameValueReference.vec3;
+ break;
+ case NVT_U32:
+ *mNameValueReference.u32 = *a.mNameValueReference.u32;
+ break;
+ case NVT_U64:
+ *mNameValueReference.u64 = *a.mNameValueReference.u64;
+ break;
+ default:
+ llerrs << "Unknown Name value type " << (U32)a.mType << llendl;
+ break;
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return *this;
+}
+
+void LLNameValue::setString(const char *a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_STRING:
+ if (a)
+ {
+ if ( (mClass == NVC_CALLBACK)
+ &&(strcmp(this->mNameValueReference.string,a)))
+ {
+ b_changed = TRUE;
+ }
+
+ if (mNameValueReference.string)
+ {
+ delete [] mNameValueReference.string;
+ }
+
+ mNameValueReference.string = new char [strlen(a) + 1]; /* Flawfinder: ignore */
+ if(mNameValueReference.string != NULL)
+ {
+ strcpy(mNameValueReference.string, a); /* Flawfinder: ignore */
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+ }
+ else
+ {
+ if (mNameValueReference.string)
+ delete [] mNameValueReference.string;
+
+ mNameValueReference.string = new char [1];
+ mNameValueReference.string[0] = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return;
+}
+
+
+void LLNameValue::setAsset(const char *a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_ASSET:
+ if (a)
+ {
+ if ( (mClass == NVC_CALLBACK)
+ &&(strcmp(this->mNameValueReference.string,a)))
+ {
+ b_changed = TRUE;
+ }
+
+ if (mNameValueReference.string)
+ {
+ delete [] mNameValueReference.string;
+ }
+ mNameValueReference.string = new char [strlen(a) + 1]; /* Flawfinder: ignore */
+ if(mNameValueReference.string != NULL)
+ {
+ strcpy(mNameValueReference.string, a); /* Flawfinder: ignore */
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+ }
+ else
+ {
+ if (mNameValueReference.string)
+ delete [] mNameValueReference.string;
+
+ mNameValueReference.string = new char [1];
+ mNameValueReference.string[0] = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ if (b_changed)
+ {
+ callCallback();
+ }
+}
+
+
+void LLNameValue::setF32(const F32 a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_F32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.f32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.f32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ break;
+ }
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return;
+}
+
+
+void LLNameValue::setS32(const S32 a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_S32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.s32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.s32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_U32:
+ if ( (mClass == NVC_CALLBACK)
+ && ((S32) (*this->mNameValueReference.u32) != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.u32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_F32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.f32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.f32 = (F32)a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ break;
+ }
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return;
+}
+
+
+void LLNameValue::setU32(const U32 a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_S32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.s32 != (S32) a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.s32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_U32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.u32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.u32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_F32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.f32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.f32 = (F32)a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ llerrs << "NameValue: Trying to set U32 into a " << mStringType << ", unknown conversion" << llendl;
+ break;
+ }
+ return;
+}
+
+
+void LLNameValue::setVec3(const LLVector3 &a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_VEC3:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.vec3 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.vec3 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ llerrs << "NameValue: Trying to set LLVector3 into a " << mStringType << ", unknown conversion" << llendl;
+ break;
+ }
+ return;
+}
+
+
+BOOL LLNameValue::nonzero()
+{
+ switch(mType)
+ {
+ case NVT_STRING:
+ if (!mNameValueReference.string)
+ return 0;
+ return (mNameValueReference.string[0] != 0);
+ case NVT_F32:
+ return (*mNameValueReference.f32 != 0.f);
+ case NVT_S32:
+ return (*mNameValueReference.s32 != 0);
+ case NVT_U32:
+ return (*mNameValueReference.u32 != 0);
+ case NVT_VEC3:
+ return (mNameValueReference.vec3->magVecSquared() != 0.f);
+ default:
+ llerrs << "NameValue: Trying to call nonzero on a " << mStringType << ", unknown conversion" << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+std::string LLNameValue::printNameValue()
+{
+ std::string buffer;
+ buffer = llformat("%s %s %s %s ", mName, mStringType, mStringClass, mStringSendto);
+ buffer += printData();
+// llinfos << "Name Value Length: " << buffer.size() + 1 << llendl;
+ return buffer;
+}
+
+std::string LLNameValue::printData()
+{
+ std::string buffer;
+ switch(mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ buffer = mNameValueReference.string;
+ break;
+ case NVT_F32:
+ buffer = llformat("%f", *mNameValueReference.f32);
+ break;
+ case NVT_S32:
+ buffer = llformat("%d", *mNameValueReference.s32);
+ break;
+ case NVT_U32:
+ buffer = llformat("%u", *mNameValueReference.u32);
+ break;
+ case NVT_U64:
+ {
+ char u64_string[U64_BUFFER_LEN]; /* Flawfinder: ignore */
+ U64_to_str(*mNameValueReference.u64, u64_string, sizeof(u64_string));
+ buffer = u64_string;
+ }
+ break;
+ case NVT_VEC3:
+ buffer = llformat( "%f, %f, %f", mNameValueReference.vec3->mV[VX], mNameValueReference.vec3->mV[VY], mNameValueReference.vec3->mV[VZ]);
+ break;
+ default:
+ llerrs << "Trying to print unknown NameValue type " << mStringType << llendl;
+ break;
+ }
+ return buffer;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLNameValue &a)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ s << a.mNameValueReference.string;
+ break;
+ case NVT_F32:
+ s << (*a.mNameValueReference.f32);
+ break;
+ case NVT_S32:
+ s << *(a.mNameValueReference.s32);
+ break;
+ case NVT_U32:
+ s << *(a.mNameValueReference.u32);
+ break;
+ case NVT_U64:
+ {
+ char u64_string[U64_BUFFER_LEN]; /* Flawfinder: ignore */
+ U64_to_str(*a.mNameValueReference.u64, u64_string, sizeof(u64_string));
+ s << u64_string;
+ }
+ case NVT_VEC3:
+ s << *(a.mNameValueReference.vec3);
+ break;
+ default:
+ llerrs << "Trying to print unknown NameValue type " << a.mStringType << llendl;
+ break;
+ }
+ return s;
+}
+
+
+// nota bene: return values aren't static for now to prevent memory leaks
+
+LLNameValue &operator+(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if (b.mType == NVT_STRING)
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+
+ S32 length1 = (S32)strlen(a.mNameValueReference.string); /* Flawfinder: Ignore */
+ S32 length2 = (S32)strlen(b.mNameValueReference.string); /* Flawfinder: Ignore */
+ delete [] retval.mNameValueReference.string;
+ retval.mNameValueReference.string = new char[length1 + length2 + 1];
+ if(retval.mNameValueReference.string != NULL)
+ {
+ strcpy(retval.mNameValueReference.string, a.mNameValueReference.string); /* Flawfinder: Ignore */
+ strcat(retval.mNameValueReference.string, b.mNameValueReference.string); /* Flawfinder: Ignore */
+ }
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 + *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 + *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 + *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 + *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 + *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 + *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 + *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 + *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 + *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 + *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown add of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator-(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 - *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 - *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 - *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 - *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 - *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 - *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 - *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 - *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 - *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 - *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown subtract of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator*(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 * *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 * *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 * *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 * *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 * *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 * *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32((*a.mNameValueReference.vec3) * (*b.mNameValueReference.vec3));
+ }
+ break;
+ default:
+ llerrs << "Unknown multiply of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator/(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 / *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 / *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 / *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 / *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 / *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 / *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 / *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 / *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 / *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown divide of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator%(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 % *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 % *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 % *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 % *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 % *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown % of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+
+// Multiplying anything times a float gives you some floats
+LLNameValue &operator*(const LLNameValue &a, F32 k)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * k);
+ break;
+ case NVT_S32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 * k);
+ break;
+ case NVT_U32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 * k);
+ break;
+ case NVT_VEC3:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 * k);
+ break;
+ default:
+ llerrs << "Unknown multiply of NV type " << a.mStringType << " with F32" << llendl;
+ break;
+ }
+ return retval;
+}
+
+
+LLNameValue &operator*(F32 k, const LLNameValue &a)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * k);
+ break;
+ case NVT_S32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 * k);
+ break;
+ case NVT_U32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 * k);
+ break;
+ case NVT_VEC3:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 * k);
+ break;
+ default:
+ llerrs << "Unknown multiply of NV type " << a.mStringType << " with F32" << llendl;
+ break;
+ }
+ return retval;
+}
+
+
+bool operator==(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if (b.mType == NVT_STRING)
+ {
+ if (!a.mNameValueReference.string)
+ return FALSE;
+ if (!b.mNameValueReference.string)
+ return FALSE;
+ return (!strcmp(a.mNameValueReference.string, b.mNameValueReference.string));
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 == *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 == *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 == *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 == *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 == *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 == (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 == *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 == *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 == *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ return (*a.mNameValueReference.vec3 == *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown == NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+bool operator<=(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if (b.mType == NVT_STRING)
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval <= 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 <= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 <= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 <= *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 <= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 <= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 <= (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 <= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 <= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 <= *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown <= NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+bool operator>=(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval >= 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 >= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 >= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 >= *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 >= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 >= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 >= (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 >= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 >= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 >= *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown >= NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+bool operator<(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval < 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 < *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 < *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 < *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 < *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 < *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 < (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 < *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 < *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 < *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown < NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+bool operator>(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval > 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 > *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 > *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 > *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 > *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 > *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 > (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 > *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 > *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 > *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown > NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+bool operator!=(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ return (strcmp(a.mNameValueReference.string, b.mNameValueReference.string)) ? true : false;
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 != *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 != *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 != *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 != *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 != *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 != (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 != *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 != *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 != *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ return (*a.mNameValueReference.vec3 != *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown != NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+LLNameValue &operator-(const LLNameValue &a)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(-*a.mNameValueReference.f32);
+ break;
+ case NVT_S32:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(-*a.mNameValueReference.s32);
+ break;
+ case NVT_U32:
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ // Can't do unary minus on U32, doesn't work.
+ retval.mNameValueReference.s32 = new S32(-S32(*a.mNameValueReference.u32));
+ break;
+ case NVT_VEC3:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(-*a.mNameValueReference.vec3);
+ break;
+ default:
+ llerrs << "Unknown - NV type " << a.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
diff --git a/indra/llmessage/llnamevalue.h b/indra/llmessage/llnamevalue.h
new file mode 100644
index 0000000000..27355277ca
--- /dev/null
+++ b/indra/llmessage/llnamevalue.h
@@ -0,0 +1,186 @@
+/**
+ * @file llnamevalue.h
+ * @brief class for defining name value pairs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMEVALUE_H
+#define LL_LLNAMEVALUE_H
+
+#include <iostream>
+#include <string.h>
+
+#include "string_table.h"
+#include "llskipmap.h"
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "lldbstrings.h"
+
+class LLNameValue;
+typedef void (*TNameValueCallback)(LLNameValue *changed, void **user_data);
+
+void add_use_callback(char *name, TNameValueCallback ucb, void **user_data);
+
+typedef enum e_name_value_types
+{
+ NVT_NULL,
+ NVT_STRING,
+ NVT_F32,
+ NVT_S32,
+ NVT_VEC3,
+ NVT_U32,
+ NVT_CAMERA, // Deprecated, but leaving in case removing this will cause problems
+ NVT_ASSET,
+ NVT_U64,
+ NVT_EOF
+} ENameValueType;
+
+typedef enum e_name_value_class
+{
+ NVC_NULL,
+ NVC_READ_ONLY,
+ NVC_READ_WRITE,
+ NVC_CALLBACK,
+ NVC_EOF
+} ENameValueClass;
+
+typedef enum e_name_value_sento
+{
+ NVS_NULL,
+ NVS_SIM,
+ NVS_DATA_SIM,
+ NVS_SIM_VIEWER,
+ NVS_DATA_SIM_VIEWER,
+ NVS_EOF
+} ENameValueSendto;
+
+
+// NameValues can always be "printed" into a buffer of this length.
+const U32 NAME_VALUE_BUF_SIZE = 1024;
+
+
+const U32 NAME_VALUE_TYPE_STRING_LENGTH = 8;
+const U32 NAME_VALUE_CLASS_STRING_LENGTH = 16;
+const U32 NAME_VALUE_SENDTO_STRING_LENGTH = 18;
+const U32 NAME_VALUE_DATA_SIZE =
+ NAME_VALUE_BUF_SIZE -
+ ( DB_NV_NAME_BUF_SIZE +
+ NAME_VALUE_TYPE_STRING_LENGTH +
+ NAME_VALUE_CLASS_STRING_LENGTH +
+ NAME_VALUE_SENDTO_STRING_LENGTH );
+
+
+extern char NameValueTypeStrings[NVT_EOF][NAME_VALUE_TYPE_STRING_LENGTH]; /* Flawfinder: Ignore */
+extern char NameValueClassStrings[NVC_EOF][NAME_VALUE_CLASS_STRING_LENGTH]; /* Flawfinder: Ignore */
+extern char NameValueSendtoStrings[NVS_EOF][NAME_VALUE_SENDTO_STRING_LENGTH]; /* Flawfinder: Ignore */
+
+typedef union u_name_value_reference
+{
+ char *string;
+ F32 *f32;
+ S32 *s32;
+ LLVector3 *vec3;
+ U32 *u32;
+ U64 *u64;
+} UNameValueReference;
+
+
+class LLNameValue
+{
+public:
+ void baseInit();
+ void init(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+
+ LLNameValue();
+ LLNameValue(const char *data);
+ LLNameValue(const char *name, const char *type, const char *nvclass,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+ LLNameValue(const char *name, const char *data, const char *type, const char *nvclass,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+ LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+
+ ~LLNameValue();
+
+ char *getString();
+ const char *getAsset() const;
+ F32 *getF32();
+ S32 *getS32();
+ void getVec3(LLVector3 &vec);
+ LLVector3 *getVec3();
+ F32 magnitude();
+ U32 *getU32();
+ U64 *getU64();
+
+ const char *getType() const { return mStringType; }
+ const char *getClass() const { return mStringClass; }
+ const char *getSendto() const { return mStringSendto; }
+
+ BOOL sendToData() const;
+ BOOL sendToViewer() const;
+
+ void callCallback();
+ std::string printNameValue();
+ std::string printData();
+
+ ENameValueType getTypeEnum() const { return mType; }
+ ENameValueClass getClassEnum() const { return mClass; }
+ ENameValueSendto getSendtoEnum() const { return mSendto; }
+
+ LLNameValue &operator=(const LLNameValue &a);
+ void setString(const char *a);
+ void setAsset(const char *a);
+ void setF32(const F32 a);
+ void setS32(const S32 a);
+ void setVec3(const LLVector3 &a);
+ void setU32(const U32 a);
+
+ BOOL nonzero();
+
+ friend std::ostream& operator<<(std::ostream& s, const LLNameValue &a);
+
+ friend LLNameValue &operator+(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator-(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator*(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator/(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator%(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator*(const LLNameValue &a, F32 k);
+ friend LLNameValue &operator*(F32 k, const LLNameValue &a);
+
+ friend bool operator==(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator<=(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator>=(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator<(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator>(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator!=(const LLNameValue &a, const LLNameValue &b);
+
+ friend LLNameValue &operator-(const LLNameValue &a);
+
+private:
+ void printNameValue(std::ostream& s);
+
+public:
+ char *mName;
+
+ char *mStringType;
+ ENameValueType mType;
+ char *mStringClass;
+ ENameValueClass mClass;
+ char *mStringSendto;
+ ENameValueSendto mSendto;
+
+ UNameValueReference mNameValueReference;
+ S32 mNumberEntries;
+ LLStringTable *mNVNameTable;
+ TNameValueCallback mNameValueCB;
+ void **mUserData;
+};
+
+extern LLStringTable gNVNameTable;
+
+
+#endif
diff --git a/indra/llmessage/llnullcipher.cpp b/indra/llmessage/llnullcipher.cpp
new file mode 100644
index 0000000000..53bb748415
--- /dev/null
+++ b/indra/llmessage/llnullcipher.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file llnullcipher.cpp
+ * @brief Implementation of a cipher which does not encrypt.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcrypto.h"
+
+///----------------------------------------------------------------------------
+/// Class LLNullCipher
+///----------------------------------------------------------------------------
+
+BOOL LLNullCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ if((src_len == dst_len) && src && dst)
+ {
+ memmove(dst, src, src_len);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLNullCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ if((src_len == dst_len) && src && dst)
+ {
+ memmove(dst, src, src_len);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+U32 LLNullCipher::requiredEncryptionSpace(U32 len)
+{
+ return len;
+}
diff --git a/indra/llmessage/llpacketack.h b/indra/llmessage/llpacketack.h
new file mode 100644
index 0000000000..1b62dc9415
--- /dev/null
+++ b/indra/llmessage/llpacketack.h
@@ -0,0 +1,143 @@
+/**
+ * @file llpacketack.h
+ * @brief Reliable UDP helpers for the message system.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPACKETACK_H
+#define LL_LLPACKETACK_H
+
+#include <cstring>
+#include <stdio.h>
+
+#include "llerror.h"
+#include "lltimer.h"
+#include "llhost.h"
+
+//class LLPacketAck
+//{
+//public:
+// LLHost mHost;
+// TPACKETID mPacketID;
+//public:
+// LLPacketAck(const LLHost &host, TPACKETID packet_id)
+// {
+// mHost = host;
+// mPacketID = packet_id;
+// };
+// ~LLPacketAck(){};
+//};
+
+class LLReliablePacketParams
+{
+public:
+ LLHost mHost;
+ S32 mRetries;
+ BOOL mPingBasedRetry;
+ F32 mTimeout;
+ void (*mCallback)(void **,S32);
+ void **mCallbackData;
+ char *mMessageName;
+
+public:
+ LLReliablePacketParams()
+ {
+ mRetries = 0;
+ mPingBasedRetry = TRUE;
+ mTimeout = 0.f;
+ mCallback = NULL;
+ mCallbackData = NULL;
+ mMessageName = NULL;
+ };
+
+ ~LLReliablePacketParams() { };
+
+ void set ( const LLHost &host, S32 retries, BOOL ping_based_retry,
+ F32 timeout,
+ void (*callback)(void **,S32), void **callback_data, char *name )
+ {
+ mHost = host;
+ mRetries = retries;
+ mPingBasedRetry = ping_based_retry;
+ mTimeout = timeout;
+ mCallback = callback;
+ mCallbackData = callback_data;
+ mMessageName = name;
+ };
+};
+
+class LLReliablePacket
+{
+public:
+ LLReliablePacket(S32 socket, U8 *buf_ptr, S32 buf_len, LLReliablePacketParams *params) :
+ mBuffer(NULL),
+ mBufferLength(0)
+ {
+ if (params)
+ {
+ mHost = params->mHost;
+ mRetries = params->mRetries;
+ mPingBasedRetry = params->mPingBasedRetry;
+ mTimeout = params->mTimeout;
+ mCallback = params->mCallback;
+ mCallbackData = params->mCallbackData;
+ mMessageName = params->mMessageName;
+ }
+ else
+ {
+ mRetries = 0;
+ mPingBasedRetry = TRUE;
+ mTimeout = 0.f;
+ mCallback = NULL;
+ mCallbackData = NULL;
+ mMessageName = NULL;
+ }
+
+ mExpirationTime = (F64)((S64)totalTime())/1000000.0 + mTimeout;
+ mPacketID = buf_ptr[1] + ((buf_ptr[0] & 0x0f ) * 256);
+ if (sizeof(TPACKETID) == 4)
+ {
+ mPacketID *= 256;
+ mPacketID += buf_ptr[2];
+ mPacketID *= 256;
+ mPacketID += buf_ptr[3];
+ }
+
+ mSocket = socket;
+ if (mRetries)
+ {
+ mBuffer = new U8[buf_len];
+ if (mBuffer != NULL)
+ {
+ memcpy(mBuffer,buf_ptr,buf_len);
+ mBufferLength = buf_len;
+ }
+
+ }
+ };
+ ~LLReliablePacket(){ delete [] mBuffer; };
+
+ friend class LLCircuitData;
+protected:
+ S32 mSocket;
+ LLHost mHost;
+ S32 mRetries;
+ BOOL mPingBasedRetry;
+ F32 mTimeout;
+ void (*mCallback)(void **,S32);
+ void **mCallbackData;
+ char *mMessageName;
+
+ U8 *mBuffer;
+ S32 mBufferLength;
+
+ TPACKETID mPacketID;
+
+ F64 mExpirationTime;
+
+};
+
+#endif
+
diff --git a/indra/llmessage/llpacketbuffer.cpp b/indra/llmessage/llpacketbuffer.cpp
new file mode 100644
index 0000000000..95c2217a69
--- /dev/null
+++ b/indra/llmessage/llpacketbuffer.cpp
@@ -0,0 +1,75 @@
+/**
+ * @file llpacketbuffer.cpp
+ * @brief implementation of LLPacketBuffer class for a packet.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpacketbuffer.h"
+
+#include "net.h"
+#include "timing.h"
+#include "llhost.h"
+
+///////////////////////////////////////////////////////////
+
+LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32 size) : mHost(host)
+{
+ if (size > NET_BUFFER_SIZE)
+ {
+ llerrs << "Sending packet > " << NET_BUFFER_SIZE << " of size " << size << llendl;
+ }
+
+ if (datap != NULL)
+ {
+ memcpy(mData, datap, size);
+ mSize = size;
+ }
+
+}
+
+LLPacketBuffer::LLPacketBuffer (S32 hSocket)
+{
+ init(hSocket);
+}
+
+///////////////////////////////////////////////////////////
+
+LLPacketBuffer::~LLPacketBuffer ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLPacketBuffer::init (S32 hSocket)
+{
+ mSize = receive_packet(hSocket, mData);
+ mHost = ::get_sender();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLPacketBuffer::free ()
+{
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llpacketbuffer.h b/indra/llmessage/llpacketbuffer.h
new file mode 100644
index 0000000000..13841aaa08
--- /dev/null
+++ b/indra/llmessage/llpacketbuffer.h
@@ -0,0 +1,37 @@
+/**
+ * @file llpacketbuffer.h
+ * @brief definition of LLPacketBuffer class for implementing a
+ * resend, drop, or delay in packet transmissions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPACKETBUFFER_H
+#define LL_LLPACKETBUFFER_H
+
+#include "net.h" // for NET_BUFFER_SIZE
+#include "llhost.h"
+
+class LLPacketBuffer
+{
+public:
+ LLPacketBuffer(const LLHost &host, const char *datap, const S32 size);
+ LLPacketBuffer(S32 hSocket); // receive a packet
+ ~LLPacketBuffer();
+
+ S32 getSize() const { return mSize; }
+ const char *getData() const { return mData; }
+ LLHost getHost() const { return mHost; }
+ void init(S32 hSocket);
+ void free();
+
+protected:
+ char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */
+ S32 mSize; // size of buffer in bytes
+ LLHost mHost; // source/dest IP and port
+};
+
+#endif
+
+
diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp
new file mode 100644
index 0000000000..4f17d1ae5a
--- /dev/null
+++ b/indra/llmessage/llpacketring.cpp
@@ -0,0 +1,293 @@
+/**
+ * @file llpacketring.cpp
+ * @brief implementation of LLPacketRing class for a packet.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpacketring.h"
+
+// linden library includes
+#include "llerror.h"
+#include "lltimer.h"
+#include "timing.h"
+#include "llrand.h"
+#include "u64.h"
+
+///////////////////////////////////////////////////////////
+LLPacketRing::LLPacketRing () :
+ mUseInThrottle(FALSE),
+ mUseOutThrottle(FALSE),
+ mInThrottle(256000.f),
+ mOutThrottle(64000.f),
+ mActualBitsIn(0),
+ mActualBitsOut(0),
+ mMaxBufferLength(64000),
+ mInBufferLength(0),
+ mOutBufferLength(0),
+ mDropPercentage(0.0f),
+ mPacketsToDrop(0x0)
+{
+}
+
+///////////////////////////////////////////////////////////
+LLPacketRing::~LLPacketRing ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+void LLPacketRing::free ()
+{
+ LLPacketBuffer *packetp;
+
+ while (!mReceiveQueue.empty())
+ {
+ packetp = mReceiveQueue.front();
+ delete packetp;
+ mReceiveQueue.pop();
+ }
+
+ while (!mSendQueue.empty())
+ {
+ packetp = mSendQueue.front();
+ delete packetp;
+ mSendQueue.pop();
+ }
+}
+
+///////////////////////////////////////////////////////////
+void LLPacketRing::dropPackets (U32 num_to_drop)
+{
+ mPacketsToDrop += num_to_drop;
+}
+
+///////////////////////////////////////////////////////////
+void LLPacketRing::setDropPercentage (F32 percent_to_drop)
+{
+ mDropPercentage = percent_to_drop;
+}
+
+void LLPacketRing::setUseInThrottle(const BOOL use_throttle)
+{
+ mUseInThrottle = use_throttle;
+}
+
+void LLPacketRing::setUseOutThrottle(const BOOL use_throttle)
+{
+ mUseOutThrottle = use_throttle;
+}
+
+void LLPacketRing::setInBandwidth(const F32 bps)
+{
+ mInThrottle.setRate(bps);
+}
+
+void LLPacketRing::setOutBandwidth(const F32 bps)
+{
+ mOutThrottle.setRate(bps);
+}
+///////////////////////////////////////////////////////////
+S32 LLPacketRing::receiveFromRing (S32 socket, char *datap)
+{
+
+ if (mInThrottle.checkOverflow(0))
+ {
+ // We don't have enough bandwidth, don't give them a packet.
+ return 0;
+ }
+
+ LLPacketBuffer *packetp = NULL;
+ if (mReceiveQueue.empty())
+ {
+ // No packets on the queue, don't give them any.
+ return 0;
+ }
+
+ S32 packet_size = 0;
+ packetp = mReceiveQueue.front();
+ mReceiveQueue.pop();
+ packet_size = packetp->getSize();
+ if (packetp->getData() != NULL)
+ {
+ memcpy(datap, packetp->getData(), packet_size);
+ }
+ // need to set sender IP/port!!
+ mLastSender = packetp->getHost();
+ delete packetp;
+
+ this->mInBufferLength -= packet_size;
+
+ // Adjust the throttle
+ mInThrottle.throttleOverflow(packet_size * 8.f);
+ return packet_size;
+}
+
+///////////////////////////////////////////////////////////
+S32 LLPacketRing::receivePacket (S32 socket, char *datap)
+{
+ S32 packet_size = 0;
+
+ // If using the throttle, simulate a limited size input buffer.
+ if (mUseInThrottle)
+ {
+ BOOL done = FALSE;
+
+ // push any current net packet (if any) onto delay ring
+ while (!done)
+ {
+ LLPacketBuffer *packetp;
+ packetp = new LLPacketBuffer(socket);
+
+ if (packetp->getSize())
+ {
+ mActualBitsIn += packetp->getSize() * 8;
+
+ // Fake packet loss
+ if (mDropPercentage && (frand(100.f) < mDropPercentage))
+ {
+ mPacketsToDrop++;
+ }
+
+ if (mPacketsToDrop)
+ {
+ delete packetp;
+ packetp = NULL;
+ packet_size = 0;
+ mPacketsToDrop--;
+ }
+ }
+
+ // If we faked packet loss, then we don't have a packet
+ // to use for buffer overflow testing
+ if (packetp)
+ {
+ if (mInBufferLength + packetp->getSize() > mMaxBufferLength)
+ {
+ // Toss it.
+ llwarns << "Throwing away packet, overflowing buffer" << llendl;
+ delete packetp;
+ packetp = NULL;
+ }
+ else if (packetp->getSize())
+ {
+ mReceiveQueue.push(packetp);
+ mInBufferLength += packetp->getSize();
+ }
+ else
+ {
+ delete packetp;
+ packetp = NULL;
+ done = true;
+ }
+ }
+ else
+ {
+ // No packetp, keep going? - no packetp == faked packet loss
+ }
+ }
+
+ // Now, grab data off of the receive queue according to our
+ // throttled bandwidth settings.
+ packet_size = receiveFromRing(socket, datap);
+ }
+ else
+ {
+ // no delay, pull straight from net
+ packet_size = receive_packet(socket, datap);
+ mLastSender = ::get_sender();
+
+ if (packet_size) // did we actually get a packet?
+ {
+ if (mDropPercentage && (frand(100.f) < mDropPercentage))
+ {
+ mPacketsToDrop++;
+ }
+
+ if (mPacketsToDrop)
+ {
+ packet_size = 0;
+ mPacketsToDrop--;
+ }
+ }
+ }
+
+ return packet_size;
+}
+
+BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host)
+{
+ BOOL status = TRUE;
+ if (!mUseOutThrottle)
+ {
+ return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() );
+ }
+ else
+ {
+ mActualBitsOut += buf_size * 8;
+ LLPacketBuffer *packetp = NULL;
+ // See if we've got enough throttle to send a packet.
+ while (!mOutThrottle.checkOverflow(0.f))
+ {
+ // While we have enough bandwidth, send a packet from the queue or the current packet
+
+ S32 packet_size = 0;
+ if (!mSendQueue.empty())
+ {
+ // Send a packet off of the queue
+ LLPacketBuffer *packetp = mSendQueue.front();
+ mSendQueue.pop();
+
+ mOutBufferLength -= packetp->getSize();
+ packet_size = packetp->getSize();
+
+ status = send_packet(h_socket, packetp->getData(), packet_size, packetp->getHost().getAddress(), packetp->getHost().getPort());
+
+ delete packetp;
+ // Update the throttle
+ mOutThrottle.throttleOverflow(packet_size * 8.f);
+ }
+ else
+ {
+ // If the queue's empty, we can just send this packet right away.
+ status = send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() );
+ packet_size = buf_size;
+
+ // Update the throttle
+ mOutThrottle.throttleOverflow(packet_size * 8.f);
+
+ // This was the packet we're sending now, there are no other packets
+ // that we need to send
+ return status;
+ }
+
+ }
+
+ // We haven't sent the incoming packet, add it to the queue
+ if (mOutBufferLength + buf_size > mMaxBufferLength)
+ {
+ // Nuke this packet, we overflowed the buffer.
+ // Toss it.
+ llwarns << "Throwing away outbound packet, overflowing buffer" << llendl;
+ }
+ else
+ {
+ static LLTimer queue_timer;
+ if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f)
+ {
+ // Add it to the queue
+ llinfos << "Outbound packet queue " << mOutBufferLength << " bytes" << llendl;
+ queue_timer.reset();
+ }
+ packetp = new LLPacketBuffer(host, send_buffer, buf_size);
+
+ mOutBufferLength += packetp->getSize();
+ mSendQueue.push(packetp);
+ }
+ }
+
+ return status;
+}
diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h
new file mode 100644
index 0000000000..0327586d78
--- /dev/null
+++ b/indra/llmessage/llpacketring.h
@@ -0,0 +1,74 @@
+/**
+ * @file llpacketring.h
+ * @brief definition of LLPacketRing class for implementing a resend,
+ * drop, or delay in packet transmissions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPACKETRING_H
+#define LL_LLPACKETRING_H
+
+#include <queue>
+
+#include "llpacketbuffer.h"
+#include "linked_lists.h"
+#include "llhost.h"
+#include "net.h"
+#include "llthrottle.h"
+
+
+class LLPacketRing
+{
+public:
+ LLPacketRing();
+ ~LLPacketRing();
+
+ void free();
+
+ void dropPackets(U32);
+ void setDropPercentage (F32 percent_to_drop);
+ void setUseInThrottle(const BOOL use_throttle);
+ void setUseOutThrottle(const BOOL use_throttle);
+ void setInBandwidth(const F32 bps);
+ void setOutBandwidth(const F32 bps);
+ S32 receivePacket (S32 socket, char *datap);
+ S32 receiveFromRing (S32 socket, char *datap);
+
+ BOOL sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host);
+
+ inline LLHost getLastSender();
+
+ S32 getAndResetActualInBits() { S32 bits = mActualBitsIn; mActualBitsIn = 0; return bits;}
+ S32 getAndResetActualOutBits() { S32 bits = mActualBitsOut; mActualBitsOut = 0; return bits;}
+protected:
+ BOOL mUseInThrottle;
+ BOOL mUseOutThrottle;
+
+ // For simulating a lower-bandwidth connection - BPS
+ LLThrottle mInThrottle;
+ LLThrottle mOutThrottle;
+
+ S32 mActualBitsIn;
+ S32 mActualBitsOut;
+ S32 mMaxBufferLength; // How much data can we queue up before dropping data.
+ S32 mInBufferLength; // Current incoming buffer length
+ S32 mOutBufferLength; // Current outgoing buffer length
+
+ F32 mDropPercentage; // % of packets to drop
+ U32 mPacketsToDrop; // drop next n packets
+
+ std::queue<LLPacketBuffer *> mReceiveQueue;
+ std::queue<LLPacketBuffer *> mSendQueue;
+
+ LLHost mLastSender;
+};
+
+
+inline LLHost LLPacketRing::getLastSender()
+{
+ return mLastSender;
+}
+
+#endif
diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp
new file mode 100644
index 0000000000..4ce7bc1fcd
--- /dev/null
+++ b/indra/llmessage/llpartdata.cpp
@@ -0,0 +1,307 @@
+/**
+ * @file llpartdata.cpp
+ * @brief Particle system data packing
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpartdata.h"
+#include "message.h"
+
+#include "lldatapacker.h"
+#include "v4coloru.h"
+
+#include "llsdutil.h"
+
+
+const S32 PS_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; // 18
+const S32 PS_DATA_BLOCK_SIZE = 68 + PS_PART_DATA_BLOCK_SIZE; // 68 + 18 = 86
+
+
+const F32 MAX_PART_SCALE = 4.f;
+
+BOOL LLPartData::pack(LLDataPacker &dp)
+{
+ LLColor4U coloru;
+ dp.packU32(mFlags, "pdflags");
+ dp.packFixed(mMaxAge, "pdmaxage", FALSE, 8, 8);
+ coloru.setVec(mStartColor);
+ dp.packColor4U(coloru, "pdstartcolor");
+ coloru.setVec(mEndColor);
+ dp.packColor4U(coloru, "pdendcolor");
+ dp.packFixed(mStartScale.mV[0], "pdstartscalex", FALSE, 3, 5);
+ dp.packFixed(mStartScale.mV[1], "pdstartscaley", FALSE, 3, 5);
+ dp.packFixed(mEndScale.mV[0], "pdendscalex", FALSE, 3, 5);
+ dp.packFixed(mEndScale.mV[1], "pdendscaley", FALSE, 3, 5);
+ return TRUE;
+}
+
+LLSD LLPartData::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["pdflags"] = ll_sd_from_U32(mFlags);
+ sd["pdmaxage"] = mMaxAge;
+ sd["pdstartcolor"] = ll_sd_from_color4(mStartColor);
+ sd["pdendcolor"] = ll_sd_from_color4(mEndColor);
+ sd["pdstartscale"] = ll_sd_from_vector2(mStartScale);
+ sd["pdendscale"] = ll_sd_from_vector2(mEndScale);
+ return sd;
+}
+
+bool LLPartData::fromLLSD(LLSD& sd)
+{
+ mFlags = ll_U32_from_sd(sd["pdflags"]);
+ mMaxAge = (F32)sd["pdmaxage"].asReal();
+ mStartColor = ll_color4_from_sd(sd["pdstartcolor"]);
+ mEndColor = ll_color4_from_sd(sd["pdendcolor"]);
+ mStartScale = ll_vector2_from_sd(sd["pdstartscale"]);
+ mEndScale = ll_vector2_from_sd(sd["pdendscale"]);
+ return true;
+}
+
+
+BOOL LLPartData::unpack(LLDataPacker &dp)
+{
+ LLColor4U coloru;
+
+ dp.unpackU32(mFlags, "pdflags");
+ dp.unpackFixed(mMaxAge, "pdmaxage", FALSE, 8, 8);
+
+ dp.unpackColor4U(coloru, "pdstartcolor");
+ mStartColor.setVec(coloru);
+ dp.unpackColor4U(coloru, "pdendcolor");
+ mEndColor.setVec(coloru);
+ dp.unpackFixed(mStartScale.mV[0], "pdstartscalex", FALSE, 3, 5);
+ dp.unpackFixed(mStartScale.mV[1], "pdstartscaley", FALSE, 3, 5);
+ dp.unpackFixed(mEndScale.mV[0], "pdendscalex", FALSE, 3, 5);
+ dp.unpackFixed(mEndScale.mV[1], "pdendscaley", FALSE, 3, 5);
+ return TRUE;
+}
+
+
+void LLPartData::setFlags(const U32 flags)
+{
+ mFlags = flags;
+}
+
+
+void LLPartData::setMaxAge(const F32 max_age)
+{
+ mMaxAge = llclamp(max_age, 0.f, 30.f);
+}
+
+
+void LLPartData::setStartScale(const F32 xs, const F32 ys)
+{
+ mStartScale.mV[VX] = llmin(xs, MAX_PART_SCALE);
+ mStartScale.mV[VY] = llmin(ys, MAX_PART_SCALE);
+}
+
+
+void LLPartData::setEndScale(const F32 xs, const F32 ys)
+{
+ mEndScale.mV[VX] = llmin(xs, MAX_PART_SCALE);
+ mEndScale.mV[VY] = llmin(ys, MAX_PART_SCALE);
+}
+
+
+void LLPartData::setStartColor(const LLVector3 &rgb)
+{
+ mStartColor.setVec(rgb.mV[0], rgb.mV[1], rgb.mV[2]);
+}
+
+
+void LLPartData::setEndColor(const LLVector3 &rgb)
+{
+ mEndColor.setVec(rgb.mV[0], rgb.mV[1], rgb.mV[2]);
+}
+
+void LLPartData::setStartAlpha(const F32 alpha)
+{
+ mStartColor.mV[3] = alpha;
+}
+void LLPartData::setEndAlpha(const F32 alpha)
+{
+ mEndColor.mV[3] = alpha;
+}
+
+
+LLPartSysData::LLPartSysData()
+{
+ mCRC = 0;
+ mPartData.mFlags = 0;
+ mPartData.mStartColor = LLColor4(1.f, 1.f, 1.f, 1.f);
+ mPartData.mEndColor = LLColor4(1.f, 1.f, 1.f, 1.f);
+ mPartData.mStartScale = LLVector2(1.f, 1.f);
+ mPartData.mEndScale = LLVector2(1.f, 1.f);
+ mPartData.mMaxAge = 10.0;
+
+ mMaxAge = 0.0;
+ mStartAge = 0.0;
+ mPattern = LL_PART_SRC_PATTERN_DROP; // Pattern for particle velocity
+ mInnerAngle = 0.0; // Inner angle of PATTERN_ANGLE_*
+ mOuterAngle = 0.0; // Outer angle of PATTERN_ANGLE_*
+ mBurstRate = 0.1f; // How often to do a burst of particles
+ mBurstPartCount = 1; // How many particles in a burst
+ mBurstSpeedMin = 1.f; // Minimum particle velocity
+ mBurstSpeedMax = 1.f; // Maximum particle velocity
+ mBurstRadius = 0.f;
+}
+
+
+BOOL LLPartSysData::pack(LLDataPacker &dp)
+{
+ dp.packU32(mCRC, "pscrc");
+ dp.packU32(mFlags, "psflags");
+ dp.packU8(mPattern, "pspattern");
+ dp.packFixed(mMaxAge, "psmaxage", FALSE, 8, 8);
+ dp.packFixed(mStartAge, "psstartage", FALSE, 8, 8);
+ dp.packFixed(mInnerAngle, "psinnerangle", FALSE, 3, 5);
+ dp.packFixed(mOuterAngle, "psouterangle", FALSE, 3, 5);
+ dp.packFixed(mBurstRate, "psburstrate", FALSE, 8, 8);
+ dp.packFixed(mBurstRadius, "psburstradius", FALSE, 8, 8);
+ dp.packFixed(mBurstSpeedMin, "psburstspeedmin", FALSE, 8, 8);
+ dp.packFixed(mBurstSpeedMax, "psburstspeedmax", FALSE, 8, 8);
+ dp.packU8(mBurstPartCount, "psburstpartcount");
+
+ dp.packFixed(mAngularVelocity.mV[0], "psangvelx", TRUE, 8, 7);
+ dp.packFixed(mAngularVelocity.mV[1], "psangvely", TRUE, 8, 7);
+ dp.packFixed(mAngularVelocity.mV[2], "psangvelz", TRUE, 8, 7);
+
+ dp.packFixed(mPartAccel.mV[0], "psaccelx", TRUE, 8, 7);
+ dp.packFixed(mPartAccel.mV[1], "psaccely", TRUE, 8, 7);
+ dp.packFixed(mPartAccel.mV[2], "psaccelz", TRUE, 8, 7);
+
+ dp.packUUID(mPartImageID, "psuuid");
+ dp.packUUID(mTargetUUID, "pstargetuuid");
+ mPartData.pack(dp);
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::unpack(LLDataPacker &dp)
+{
+ dp.unpackU32(mCRC, "pscrc");
+ dp.unpackU32(mFlags, "psflags");
+ dp.unpackU8(mPattern, "pspattern");
+ dp.unpackFixed(mMaxAge, "psmaxage", FALSE, 8, 8);
+ dp.unpackFixed(mStartAge, "psstartage", FALSE, 8, 8);
+ dp.unpackFixed(mInnerAngle, "psinnerangle", FALSE, 3, 5);
+ dp.unpackFixed(mOuterAngle, "psouterangle", FALSE, 3, 5);
+ dp.unpackFixed(mBurstRate, "psburstrate", FALSE, 8, 8);
+ mBurstRate = llmax(0.01f, mBurstRate);
+ dp.unpackFixed(mBurstRadius, "psburstradius", FALSE, 8, 8);
+ dp.unpackFixed(mBurstSpeedMin, "psburstspeedmin", FALSE, 8, 8);
+ dp.unpackFixed(mBurstSpeedMax, "psburstspeedmax", FALSE, 8, 8);
+ dp.unpackU8(mBurstPartCount, "psburstpartcount");
+
+ dp.unpackFixed(mAngularVelocity.mV[0], "psangvelx", TRUE, 8, 7);
+ dp.unpackFixed(mAngularVelocity.mV[1], "psangvely", TRUE, 8, 7);
+ dp.unpackFixed(mAngularVelocity.mV[2], "psangvelz", TRUE, 8, 7);
+
+ dp.unpackFixed(mPartAccel.mV[0], "psaccelx", TRUE, 8, 7);
+ dp.unpackFixed(mPartAccel.mV[1], "psaccely", TRUE, 8, 7);
+ dp.unpackFixed(mPartAccel.mV[2], "psaccelz", TRUE, 8, 7);
+
+ dp.unpackUUID(mPartImageID, "psuuid");
+ dp.unpackUUID(mTargetUUID, "pstargetuuid");
+ mPartData.unpack(dp);
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::isNullPS(const S32 block_num)
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+ U32 crc;
+
+ S32 size;
+ // Check size of block
+ size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
+
+ if (!size)
+ {
+ return TRUE;
+ }
+ else if (size != PS_DATA_BLOCK_SIZE)
+ {
+ llwarns << "PSBlock is wrong size for particle system data - got " << size << ", expecting " << PS_DATA_BLOCK_SIZE << llendl;
+ return TRUE;
+ }
+ gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE, block_num, PS_DATA_BLOCK_SIZE);
+
+ LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
+ dp.unpackU32(crc, "crc");
+
+ if (crc == 0)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+//static
+BOOL LLPartSysData::packNull()
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+ gMessageSystem->addBinaryData("PSBlock", ps_data_block, 0);
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::packBlock()
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+
+ LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
+ pack(dp);
+
+ // Add to message
+ gMessageSystem->addBinaryData("PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE);
+
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::unpackBlock(const S32 block_num)
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+
+ // Check size of block
+ S32 size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
+
+ if (size != PS_DATA_BLOCK_SIZE)
+ {
+ llwarns << "PSBlock is wrong size for particle system data - got " << size << ", expecting " << PS_DATA_BLOCK_SIZE << llendl;
+ return FALSE;
+ }
+
+ // Get from message
+ gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE, block_num, PS_DATA_BLOCK_SIZE);
+
+ LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
+ unpack(dp);
+
+ return TRUE;
+}
+
+void LLPartSysData::clampSourceParticleRate()
+{
+ F32 particle_rate = 0;
+ particle_rate = mBurstPartCount/mBurstRate;
+ if (particle_rate > 256.f)
+ {
+ mBurstPartCount = llfloor(((F32)mBurstPartCount)*(256.f/particle_rate));
+ }
+}
+
+void LLPartSysData::setPartAccel(const LLVector3 &accel)
+{
+ mPartAccel.mV[VX] = llclamp(accel.mV[VX], -100.f, 100.f);
+ mPartAccel.mV[VY] = llclamp(accel.mV[VY], -100.f, 100.f);
+ mPartAccel.mV[VZ] = llclamp(accel.mV[VZ], -100.f, 100.f);
+}
diff --git a/indra/llmessage/llpartdata.h b/indra/llmessage/llpartdata.h
new file mode 100644
index 0000000000..ba3bdaf9b6
--- /dev/null
+++ b/indra/llmessage/llpartdata.h
@@ -0,0 +1,217 @@
+/**
+ * @file llpartdata.h
+ * @brief Particle system data packing
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPARTDATA_H
+#define LL_LLPARTDATA_H
+
+#include <stdio.h>
+
+#include "lluuid.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v2math.h"
+#include "v4color.h"
+
+class LLMessageSystem;
+class LLDataPacker;
+
+const S32 PS_CUR_VERSION = 18;
+
+//
+// These constants are used by the script code, not by the particle system itself
+//
+
+enum LLPSScriptFlags
+{
+ // Flags for the different parameters of individual particles
+ LLPS_PART_FLAGS,
+ LLPS_PART_START_COLOR,
+ LLPS_PART_START_ALPHA,
+ LLPS_PART_END_COLOR,
+ LLPS_PART_END_ALPHA,
+ LLPS_PART_START_SCALE,
+ LLPS_PART_END_SCALE,
+ LLPS_PART_MAX_AGE,
+
+ // Flags for the different parameters of the particle source
+ LLPS_SRC_ACCEL,
+ LLPS_SRC_PATTERN,
+ LLPS_SRC_INNERANGLE,
+ LLPS_SRC_OUTERANGLE,
+ LLPS_SRC_TEXTURE,
+ LLPS_SRC_BURST_RATE,
+ LLPS_SRC_BURST_DURATION,
+ LLPS_SRC_BURST_PART_COUNT,
+ LLPS_SRC_BURST_RADIUS,
+ LLPS_SRC_BURST_SPEED_MIN,
+ LLPS_SRC_BURST_SPEED_MAX,
+ LLPS_SRC_MAX_AGE,
+ LLPS_SRC_TARGET_UUID,
+ LLPS_SRC_OMEGA,
+ LLPS_SRC_ANGLE_BEGIN,
+ LLPS_SRC_ANGLE_END
+};
+
+
+class LLPartData
+{
+public:
+ LLPartData() :
+ mFlags(0),
+ mMaxAge(0)
+ {
+ }
+ BOOL unpack(LLDataPacker &dp);
+ BOOL pack(LLDataPacker &dp);
+ LLSD asLLSD() const;
+ operator LLSD() const {return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ // Masks for the different particle flags
+ enum
+ {
+ LL_PART_INTERP_COLOR_MASK = 0x01,
+ LL_PART_INTERP_SCALE_MASK = 0x02,
+ LL_PART_BOUNCE_MASK = 0x04,
+ LL_PART_WIND_MASK = 0x08,
+ LL_PART_FOLLOW_SRC_MASK = 0x10, // Follows source, no rotation following (expensive!)
+ LL_PART_FOLLOW_VELOCITY_MASK = 0x20, // Particles orient themselves with velocity
+ LL_PART_TARGET_POS_MASK = 0x40,
+ LL_PART_TARGET_LINEAR_MASK = 0x80, // Particle uses a direct linear interpolation
+ LL_PART_EMISSIVE_MASK = 0x100, // Particle is "emissive", instead of being lit
+ LL_PART_BEAM_MASK = 0x200, // Particle is a "beam" connecting source and target
+
+ // Not implemented yet!
+ //LL_PART_RANDOM_ACCEL_MASK = 0x100, // Patricles have random accelearation
+ //LL_PART_RANDOM_VEL_MASK = 0x200, // Particles have random velocity shifts"
+ //LL_PART_TRAIL_MASK = 0x400, // Particles have historical "trails"
+
+ // Viewer side use only!
+ LL_PART_DEAD_MASK = 0x80000000,
+ };
+
+ void setFlags(const U32 flags);
+ void setMaxAge(const F32 max_age);
+ void setStartScale(const F32 xs, F32 ys);
+ void setEndScale(const F32 xs, F32 ys);
+ void setStartColor(const LLVector3 &rgb);
+ void setEndColor(const LLVector3 &rgb);
+ void setStartAlpha(const F32 alpha);
+ void setEndAlpha(const F32 alpha);
+
+
+ friend class LLPartSysData;
+ friend class LLViewerPartSourceScript;
+
+ // These are public because I'm really lazy...
+public:
+ U32 mFlags; // Particle state/interpolators in effect
+ F32 mMaxAge; // Maximum age of the particle
+ LLColor4 mStartColor; // Start color
+ LLColor4 mEndColor; // End color
+ LLVector2 mStartScale; // Start scale
+ LLVector2 mEndScale; // End scale
+
+ LLVector3 mPosOffset; // Offset from source if using FOLLOW_SOURCE
+ F32 mParameter; // A single floating point parameter
+};
+
+
+class LLPartSysData
+{
+public:
+ LLPartSysData();
+
+ BOOL unpack(LLDataPacker &dp);
+ BOOL pack(LLDataPacker &dp);
+
+
+ BOOL unpackBlock(const S32 block_num);
+ BOOL packBlock();
+
+ static BOOL packNull();
+ static BOOL isNullPS(const S32 block_num); // Returns FALSE if this is a "NULL" particle system (i.e. no system)
+
+ // Different masks for effects on the source
+ enum
+ {
+ LL_PART_SRC_OBJ_REL_MASK = 0x01, // Accel and velocity for particles relative object rotation
+ LL_PART_USE_NEW_ANGLE = 0x02, // Particles uses new 'correct' angle parameters.
+ };
+
+ // The different patterns for how particles are created
+ enum
+ {
+ LL_PART_SRC_PATTERN_DROP = 0x01,
+ LL_PART_SRC_PATTERN_EXPLODE = 0x02,
+ // Not implemented fully yet
+ LL_PART_SRC_PATTERN_ANGLE = 0x04,
+ LL_PART_SRC_PATTERN_ANGLE_CONE = 0x08,
+ LL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY = 0x10,
+ };
+
+
+ void setBurstSpeedMin(const F32 spd) { mBurstSpeedMin = llclamp(spd, -100.f, 100.f); }
+ void setBurstSpeedMax(const F32 spd) { mBurstSpeedMax = llclamp(spd, -100.f, 100.f); }
+ void setBurstRadius(const F32 rad) { mBurstRadius = llclamp(rad, 0.f, 50.f); }
+ void setPartAccel(const LLVector3 &accel);
+ void setUseNewAngle() { mFlags |= LL_PART_USE_NEW_ANGLE; }
+ void unsetUseNewAngle() { mFlags &= ~LL_PART_USE_NEW_ANGLE; }
+
+ // Since the actual particle creation rate is
+ // a combination of multiple parameters, we
+ // need to clamp it using a separate method instead of an accessor.
+ void clampSourceParticleRate();
+public:
+ // Public because I'm lazy....
+
+ //
+ // There are two kinds of data for the particle system
+ // 1. Parameters which specify parameters of the source (mSource*)
+ // 2. Parameters which specify parameters of the particles generated by the source (mPart*)
+ //
+
+ U32 mCRC;
+ U32 mFlags;
+
+ U8 mPattern; // Pattern for particle velocity/output
+ F32 mInnerAngle; // Inner angle for PATTERN_ANGLE
+ F32 mOuterAngle; // Outer angle for PATTERN_ANGLE
+ LLVector3 mAngularVelocity; // Angular velocity for emission axis (for PATTERN_ANGLE)
+
+ F32 mBurstRate; // How often to do a burst of particles
+ U8 mBurstPartCount; // How many particles in a burst
+ F32 mBurstRadius;
+ F32 mBurstSpeedMin; // Minimum particle velocity
+ F32 mBurstSpeedMax; // Maximum particle velocity
+
+ F32 mMaxAge; // Maximum lifetime of this particle source
+
+ LLUUID mTargetUUID; // Target UUID for the particle system
+
+ F32 mStartAge; // Age at which to start the particle system (for an update after the
+ // particle system has started)
+
+
+ //
+ // These are actually particle properties, but can be mutated by the source,
+ // so are stored here instead
+ //
+ LLVector3 mPartAccel;
+ LLUUID mPartImageID;
+
+ //
+ // The "template" partdata where we actually store the non-mutable particle parameters
+ //
+ LLPartData mPartData;
+
+protected:
+ S32 mNumParticles; // Number of particles generated
+};
+
+#endif // LL_LLPARTDATA_H
diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp
new file mode 100644
index 0000000000..0c5bd1eaea
--- /dev/null
+++ b/indra/llmessage/llpumpio.cpp
@@ -0,0 +1,1006 @@
+/**
+ * @file llpumpio.cpp
+ * @author Phoenix
+ * @date 2004-11-21
+ * @brief Implementation of the i/o pump and related functions.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llpumpio.h"
+
+#include <set>
+#include "apr-1/apr_poll.h"
+
+#include "llapr.h"
+#include "llmemtype.h"
+#include "llstl.h"
+
+// This should not be in production, but it is intensely useful during
+// development.
+#if LL_LINUX
+#define LL_DEBUG_PIPE_TYPE_IN_PUMP 0
+#endif
+
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+#include <typeinfo>
+#endif
+
+// constants for poll timeout. if we are threading, we want to have a
+// longer poll timeout.
+#if LL_THREADS_APR
+static const S32 DEFAULT_POLL_TIMEOUT = 1000;
+#else
+static const S32 DEFAULT_POLL_TIMEOUT = 0;
+#endif
+
+// The default (and fallback) expiration time for chains
+const F32 DEFAULT_CHAIN_EXPIRY_SECS = 30.0f;
+extern const F32 SHORT_CHAIN_EXPIRY_SECS = 1.0f;
+extern const F32 NEVER_CHAIN_EXPIRY_SECS = 0.0f;
+
+// sorta spammy debug modes.
+//#define LL_DEBUG_SPEW_BUFFER_CHANNEL_IN_ON_ERROR 1
+//#define LL_DEBUG_PROCESS_LINK 1
+//#define LL_DEBUG_PROCESS_RETURN_VALUE 1
+
+// Super spammy debug mode.
+//#define LL_DEBUG_SPEW_BUFFER_CHANNEL_IN 1
+//#define LL_DEBUG_SPEW_BUFFER_CHANNEL_OUT 1
+
+/**
+ * @class
+ */
+class LLChainSleeper : public LLRunnable
+{
+public:
+ static LLRunner::run_ptr_t build(LLPumpIO* pump, S32 key)
+ {
+ return LLRunner::run_ptr_t(new LLChainSleeper(pump, key));
+ }
+
+ virtual void run(LLRunner* runner, S64 handle)
+ {
+ mPump->clearLock(mKey);
+ }
+
+protected:
+ LLChainSleeper(LLPumpIO* pump, S32 key) : mPump(pump), mKey(key) {}
+ LLPumpIO* mPump;
+ S32 mKey;
+};
+
+
+/**
+ * @struct ll_delete_apr_pollset_fd_client_data
+ * @brief This is a simple helper class to clean up our client data.
+ */
+struct ll_delete_apr_pollset_fd_client_data
+{
+ typedef std::pair<LLIOPipe::ptr_t, apr_pollfd_t> pipe_conditional_t;
+ void operator()(const pipe_conditional_t& conditional)
+ {
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ S32* client_id = (S32*)conditional.second.client_data;
+ delete client_id;
+ }
+};
+
+/**
+ * LLPumpIO
+ */
+LLPumpIO::LLPumpIO(apr_pool_t* pool) :
+ mState(LLPumpIO::NORMAL),
+ mRebuildPollset(false),
+ mPollset(NULL),
+ mPollsetClientID(0),
+ mNextLock(0),
+ mPool(NULL),
+ mCurrentPool(NULL),
+ mCurrentPoolReallocCount(0),
+ mChainsMutex(NULL),
+ mCallbackMutex(NULL)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ initialize(pool);
+}
+
+LLPumpIO::~LLPumpIO()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ cleanup();
+}
+
+bool LLPumpIO::prime(apr_pool_t* pool)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ cleanup();
+ initialize(pool);
+ return ((pool == NULL) ? false : true);
+}
+
+bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(chain.empty()) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ LLChainInfo info;
+ info.setTimeoutSeconds(timeout);
+ info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray);
+ LLLinkInfo link;
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "LLPumpIO::addChain() " << chain[0] << " '"
+ << typeid(*(chain[0])).name() << "'" << llendl;
+#else
+ lldebugs << "LLPumpIO::addChain() " << chain[0] <<llendl;
+#endif
+ chain_t::const_iterator it = chain.begin();
+ chain_t::const_iterator end = chain.end();
+ for(; it != end; ++it)
+ {
+ link.mPipe = (*it);
+ link.mChannels = info.mData->nextChannel();
+ info.mChainLinks.push_back(link);
+ }
+ mPendingChains.push_back(info);
+ return true;
+}
+
+bool LLPumpIO::addChain(
+ const LLPumpIO::links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context,
+ F32 timeout)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+
+ // remember that if the caller is providing a full link
+ // description, we need to have that description matched to a
+ // particular buffer.
+ if(!data) return false;
+ if(links.empty()) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << " '"
+ << typeid(*(links[0].mPipe)).name() << "'" << llendl;
+#else
+ lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << llendl;
+#endif
+ LLChainInfo info;
+ info.setTimeoutSeconds(timeout);
+ info.mChainLinks = links;
+ info.mData = data;
+ info.mContext = context;
+ mPendingChains.push_back(info);
+ return true;
+}
+
+bool LLPumpIO::setTimeoutSeconds(F32 timeout)
+{
+ // If no chain is running, return failure.
+ if(current_chain_t() == mCurrentChain)
+ {
+ return false;
+ }
+ (*mCurrentChain).setTimeoutSeconds(timeout);
+ return true;
+}
+
+bool LLPumpIO::setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ //lldebugs << "LLPumpIO::setConditional" << llendl;
+ if(pipe)
+ {
+ // remove any matching poll file descriptors for this pipe.
+ LLIOPipe::ptr_t pipe_ptr(pipe);
+
+ LLChainInfo::conditionals_t::iterator it = (*mCurrentChain).mDescriptors.begin();
+ LLChainInfo::conditionals_t::iterator end = (*mCurrentChain).mDescriptors.end();
+ while (it != end)
+ {
+ LLChainInfo::pipe_conditional_t& value = (*it);
+ if ( pipe_ptr == value.first )
+ {
+ ll_delete_apr_pollset_fd_client_data()(value);
+ (*mCurrentChain).mDescriptors.erase(it++);
+ mRebuildPollset = true;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ if(poll)
+ {
+ LLChainInfo::pipe_conditional_t value;
+ value.first = pipe_ptr;
+ value.second = *poll;
+ if(!poll->p)
+ {
+ // each fd needs a pool to work with, so if one was
+ // not specified, use this pool.
+ // *FIX: Should it always be this pool?
+ value.second.p = mPool;
+ }
+ value.second.client_data = new S32(++mPollsetClientID);
+ (*mCurrentChain).mDescriptors.push_back(value);
+ mRebuildPollset = true;
+ }
+ return true;
+ }
+ return false;
+}
+
+S32 LLPumpIO::setLock()
+{
+ // *NOTE: I do not think it is necessary to acquire a mutex here
+ // since this should only be called during the pump(), and should
+ // only change the running chain. Any other use of this method is
+ // incorrect usage. If it becomes necessary to acquire a lock
+ // here, be sure to lock here and call a protected method to get
+ // the lock, and sleepChain() should probably acquire the same
+ // lock while and calling the same protected implementation to
+ // lock the runner at the same time.
+
+ // If no chain is running, return failure.
+ if(current_chain_t() == mCurrentChain)
+ {
+ return 0;
+ }
+
+ // deal with wrap.
+ if(++mNextLock <= 0)
+ {
+ mNextLock = 1;
+ }
+
+ // set the lock
+ (*mCurrentChain).mLock = mNextLock;
+ return mNextLock;
+}
+
+void LLPumpIO::clearLock(S32 key)
+{
+ // We need to lock it here since we do not want to be iterating
+ // over the chains twice. We can safely call process() while this
+ // is happening since we should not be erasing a locked pipe, and
+ // therefore won't be treading into deleted memory. I think we can
+ // also clear the lock on the chain safely since the pump only
+ // reads that value.
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ mClearLocks.insert(key);
+}
+
+bool LLPumpIO::sleepChain(F64 seconds)
+{
+ // Much like the call to setLock(), this should only be called
+ // from one chain during processing, so there is no need to
+ // acquire a mutex.
+ if(seconds <= 0.0) return false;
+ S32 key = setLock();
+ if(!key) return false;
+ LLRunner::run_handle_t handle = mRunner.addRunnable(
+ LLChainSleeper::build(this, key),
+ LLRunner::RUN_IN,
+ seconds);
+ if(0 == handle) return false;
+ return true;
+}
+
+bool LLPumpIO::copyCurrentLinkInfo(links_t& links) const
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(current_chain_t() == mCurrentChain)
+ {
+ return false;
+ }
+ std::copy(
+ (*mCurrentChain).mChainLinks.begin(),
+ (*mCurrentChain).mChainLinks.end(),
+ std::back_insert_iterator<links_t>(links));
+ return true;
+}
+
+void LLPumpIO::pump()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ //llinfos << "LLPumpIO::pump()" << llendl;
+
+ // Run any pending runners.
+ mRunner.run();
+
+ // We need to move all of the pending heads over to the running
+ // chains.
+ PUMP_DEBUG;
+ if(true)
+ {
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ // bail if this pump is paused.
+ if(PAUSING == mState)
+ {
+ mState = PAUSED;
+ }
+ if(PAUSED == mState)
+ {
+ return;
+ }
+
+ PUMP_DEBUG;
+ // Move the pending chains over to the running chaings
+ if(!mPendingChains.empty())
+ {
+ PUMP_DEBUG;
+ //lldebugs << "Pushing " << mPendingChains.size() << "." << llendl;
+ std::copy(
+ mPendingChains.begin(),
+ mPendingChains.end(),
+ std::back_insert_iterator<running_chains_t>(mRunningChains));
+ mPendingChains.clear();
+ PUMP_DEBUG;
+ }
+
+ // Clear any locks. This needs to be done here so that we do
+ // not clash during a call to clearLock().
+ if(!mClearLocks.empty())
+ {
+ PUMP_DEBUG;
+ running_chains_t::iterator it = mRunningChains.begin();
+ running_chains_t::iterator end = mRunningChains.end();
+ std::set<S32>::iterator not_cleared = mClearLocks.end();
+ for(; it != end; ++it)
+ {
+ if((*it).mLock && mClearLocks.find((*it).mLock) != not_cleared)
+ {
+ (*it).mLock = 0;
+ }
+ }
+ PUMP_DEBUG;
+ mClearLocks.clear();
+ }
+ }
+
+ PUMP_DEBUG;
+ // rebuild the pollset if necessary
+ if(mRebuildPollset)
+ {
+ PUMP_DEBUG;
+ rebuildPollset();
+ mRebuildPollset = false;
+ }
+
+ // Poll based on the last known pollset
+ // *FIX: may want to pass in a poll timeout so it works correctly
+ // in single and multi threaded processes.
+ PUMP_DEBUG;
+ typedef std::set<S32> signal_client_t;
+ signal_client_t signalled_client;
+ if(mPollset)
+ {
+ PUMP_DEBUG;
+ //llinfos << "polling" << llendl;
+ S32 count = 0;
+ S32 client_id = 0;
+ const apr_pollfd_t* poll_fd = NULL;
+ apr_pollset_poll(mPollset, DEFAULT_POLL_TIMEOUT, &count, &poll_fd);
+ PUMP_DEBUG;
+ for(S32 i = 0; i < count; ++i)
+ {
+ client_id = *((S32*)poll_fd[i].client_data);
+ signalled_client.insert(client_id);
+ }
+ PUMP_DEBUG;
+ }
+
+ PUMP_DEBUG;
+ // set up for a check to see if each one was signalled
+ signal_client_t::iterator not_signalled = signalled_client.end();
+
+ // Process everything as appropriate
+ //lldebugs << "Running chain count: " << mRunningChains.size() << llendl;
+ running_chains_t::iterator run_chain = mRunningChains.begin();
+ bool process_this_chain = false;
+ for(; run_chain != mRunningChains.end(); )
+ {
+ PUMP_DEBUG;
+ if((*run_chain).mInit
+ && (*run_chain).mTimer.getStarted()
+ && (*run_chain).mTimer.hasExpired())
+ {
+ PUMP_DEBUG;
+ if(handleChainError(*run_chain, LLIOPipe::STATUS_EXPIRED))
+ {
+ // the pipe probably handled the error. If the handler
+ // forgot to reset the expiration then we need to do
+ // that here.
+ if((*run_chain).mTimer.getStarted()
+ && (*run_chain).mTimer.hasExpired())
+ {
+ PUMP_DEBUG;
+ llinfos << "Error handler forgot to reset timeout. "
+ << "Resetting to " << DEFAULT_CHAIN_EXPIRY_SECS
+ << " seconds." << llendl;
+ (*run_chain).setTimeoutSeconds(DEFAULT_CHAIN_EXPIRY_SECS);
+ }
+ }
+ else
+ {
+ PUMP_DEBUG;
+ // it timed out and no one handled it, so we need to
+ // retire the chain
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Removing chain "
+ << (*run_chain).mChainLinks[0].mPipe
+ << " '"
+ << typeid(*((*run_chain).mChainLinks[0].mPipe)).name()
+ << "' because it timed out." << llendl;
+#else
+// lldebugs << "Removing chain "
+// << (*run_chain).mChainLinks[0].mPipe
+// << " because we reached the end." << llendl;
+#endif
+ mRunningChains.erase(run_chain++);
+ continue;
+ }
+ }
+ PUMP_DEBUG;
+ if((*run_chain).mLock)
+ {
+ ++run_chain;
+ continue;
+ }
+ PUMP_DEBUG;
+ mCurrentChain = run_chain;
+ if((*run_chain).mDescriptors.empty())
+ {
+ // if there are no conditionals, just process this chain.
+ process_this_chain = true;
+ //lldebugs << "no conditionals - processing" << llendl;
+ }
+ else
+ {
+ PUMP_DEBUG;
+ //lldebugs << "checking conditionals" << llendl;
+ // Check if this run chain was signalled. If any file
+ // descriptor is ready for something, then go ahead and
+ // process this chian.
+ process_this_chain = false;
+ if(!signalled_client.empty())
+ {
+ PUMP_DEBUG;
+ LLChainInfo::conditionals_t::iterator it;
+ it = (*run_chain).mDescriptors.begin();
+ LLChainInfo::conditionals_t::iterator end;
+ end = (*run_chain).mDescriptors.end();
+ S32 client_id = 0;
+ for(; it != end; ++it)
+ {
+ PUMP_DEBUG;
+ client_id = *((S32*)((*it).second.client_data));
+ if(signalled_client.find(client_id) != not_signalled)
+ {
+ process_this_chain = true;
+ break;
+ }
+ //llinfos << "no fd ready for this one." << llendl;
+ }
+ }
+ }
+ if(process_this_chain)
+ {
+ PUMP_DEBUG;
+ if(!((*run_chain).mInit))
+ {
+ (*run_chain).mHead = (*run_chain).mChainLinks.begin();
+ (*run_chain).mInit = true;
+ }
+ PUMP_DEBUG;
+ processChain(*run_chain);
+ }
+
+ PUMP_DEBUG;
+ if((*run_chain).mHead == (*run_chain).mChainLinks.end())
+ {
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Removing chain " << (*run_chain).mChainLinks[0].mPipe
+ << " '"
+ << typeid(*((*run_chain).mChainLinks[0].mPipe)).name()
+ << "' because we reached the end." << llendl;
+#else
+// lldebugs << "Removing chain " << (*run_chain).mChainLinks[0].mPipe
+// << " because we reached the end." << llendl;
+#endif
+
+ PUMP_DEBUG;
+ // This chain is done. Clean up any allocated memory and
+ // erase the chain info.
+ std::for_each(
+ (*run_chain).mDescriptors.begin(),
+ (*run_chain).mDescriptors.end(),
+ ll_delete_apr_pollset_fd_client_data());
+ mRunningChains.erase(run_chain++);
+
+ // *NOTE: may not always need to rebuild the pollset.
+ mRebuildPollset = true;
+ }
+ else
+ {
+ PUMP_DEBUG;
+ // this chain needs more processing - just go to the next
+ // chain.
+ ++run_chain;
+ }
+ }
+
+ PUMP_DEBUG;
+ // null out the chain
+ mCurrentChain = current_chain_t();
+ END_PUMP_DEBUG;
+}
+
+//bool LLPumpIO::respond(const chain_t& pipes)
+//{
+//#if LL_THREADS_APR
+// LLScopedLock lock(mCallbackMutex);
+//#endif
+// LLChainInfo info;
+// links_t links;
+//
+// mPendingCallbacks.push_back(info);
+// return true;
+//}
+
+bool LLPumpIO::respond(LLIOPipe* pipe)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(NULL == pipe) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mCallbackMutex);
+#endif
+ LLChainInfo info;
+ LLLinkInfo link;
+ link.mPipe = pipe;
+ info.mChainLinks.push_back(link);
+ mPendingCallbacks.push_back(info);
+ return true;
+}
+
+bool LLPumpIO::respond(
+ const links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ // if the caller is providing a full link description, we need to
+ // have that description matched to a particular buffer.
+ if(!data) return false;
+ if(links.empty()) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mCallbackMutex);
+#endif
+
+ // Add the callback response
+ LLChainInfo info;
+ info.mChainLinks = links;
+ info.mData = data;
+ info.mContext = context;
+ mPendingCallbacks.push_back(info);
+ return true;
+}
+
+void LLPumpIO::callback()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ //llinfos << "LLPumpIO::callback()" << llendl;
+ if(true)
+ {
+#if LL_THREADS_APR
+ LLScopedLock lock(mCallbackMutex);
+#endif
+ std::copy(
+ mPendingCallbacks.begin(),
+ mPendingCallbacks.end(),
+ std::back_insert_iterator<callbacks_t>(mCallbacks));
+ mPendingCallbacks.clear();
+ }
+ if(!mCallbacks.empty())
+ {
+ callbacks_t::iterator it = mCallbacks.begin();
+ callbacks_t::iterator end = mCallbacks.end();
+ for(; it != end; ++it)
+ {
+ // it's always the first and last time for respone chains
+ (*it).mHead = (*it).mChainLinks.begin();
+ (*it).mInit = true;
+ (*it).mEOS = true;
+ processChain(*it);
+ }
+ mCallbacks.clear();
+ }
+}
+
+void LLPumpIO::control(LLPumpIO::EControl op)
+{
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ switch(op)
+ {
+ case PAUSE:
+ mState = PAUSING;
+ break;
+ case RESUME:
+ mState = NORMAL;
+ break;
+ default:
+ // no-op
+ break;
+ }
+}
+
+void LLPumpIO::initialize(apr_pool_t* pool)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(!pool) return;
+#if LL_THREADS_APR
+ apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_DEFAULT, pool);
+ apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_DEFAULT, pool);
+#endif
+ mPool = pool;
+}
+
+void LLPumpIO::cleanup()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+#if LL_THREADS_APR
+ if(mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
+ if(mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
+#endif
+ mChainsMutex = NULL;
+ mCallbackMutex = NULL;
+ if(mPollset)
+ {
+// lldebugs << "cleaning up pollset" << llendl;
+ apr_pollset_destroy(mPollset);
+ mPollset = NULL;
+ }
+ if(mCurrentPool)
+ {
+ apr_pool_destroy(mCurrentPool);
+ mCurrentPool = NULL;
+ }
+ mPool = NULL;
+}
+
+void LLPumpIO::rebuildPollset()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+// lldebugs << "LLPumpIO::rebuildPollset()" << llendl;
+ if(mPollset)
+ {
+ //lldebugs << "destroying pollset" << llendl;
+ apr_pollset_destroy(mPollset);
+ mPollset = NULL;
+ }
+ U32 size = 0;
+ running_chains_t::iterator run_it = mRunningChains.begin();
+ running_chains_t::iterator run_end = mRunningChains.end();
+ for(; run_it != run_end; ++run_it)
+ {
+ size += (*run_it).mDescriptors.size();
+ }
+ //lldebugs << "found " << size << " descriptors." << llendl;
+ if(size)
+ {
+ // Recycle the memory pool
+ const S32 POLLSET_POOL_RECYCLE_COUNT = 100;
+ if(mCurrentPool
+ && (0 == (++mCurrentPoolReallocCount % POLLSET_POOL_RECYCLE_COUNT)))
+ {
+ apr_pool_destroy(mCurrentPool);
+ mCurrentPool = NULL;
+ mCurrentPoolReallocCount = 0;
+ }
+ if(!mCurrentPool)
+ {
+ apr_status_t status = apr_pool_create(&mCurrentPool, mPool);
+ (void)ll_apr_warn_status(status);
+ }
+
+ // add all of the file descriptors
+ run_it = mRunningChains.begin();
+ LLChainInfo::conditionals_t::iterator fd_it;
+ LLChainInfo::conditionals_t::iterator fd_end;
+ apr_pollset_create(&mPollset, size, mCurrentPool, 0);
+ for(; run_it != run_end; ++run_it)
+ {
+ fd_it = (*run_it).mDescriptors.begin();
+ fd_end = (*run_it).mDescriptors.end();
+ for(; fd_it != fd_end; ++fd_it)
+ {
+ apr_pollset_add(mPollset, &((*fd_it).second));
+ }
+ }
+ }
+}
+
+void LLPumpIO::processChain(LLChainInfo& chain)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
+ links_t::iterator it = chain.mHead;
+ links_t::iterator end = chain.mChainLinks.end();
+ bool need_process_signaled = false;
+ bool keep_going = true;
+ do
+ {
+#if LL_DEBUG_PROCESS_LINK
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ llinfos << "Processing " << typeid(*((*it).mPipe)).name() << "."
+ << llendl;
+#else
+ llinfos << "Processing link " << (*it).mPipe << "." << llendl;
+#endif
+#endif
+#if LL_DEBUG_SPEW_BUFFER_CHANNEL_IN
+ if(chain.mData)
+ {
+ char* buf = NULL;
+ S32 bytes = chain.mData->countAfter((*it).mChannels.in(), NULL);
+ if(bytes)
+ {
+ buf = new char[bytes + 1];
+ chain.mData->readAfter(
+ (*it).mChannels.in(),
+ NULL,
+ (U8*)buf,
+ bytes);
+ buf[bytes] = '\0';
+ llinfos << "CHANNEL IN(" << (*it).mChannels.in() << "): "
+ << buf << llendl;
+ delete[] buf;
+ buf = NULL;
+ }
+ else
+ {
+ llinfos << "CHANNEL IN(" << (*it).mChannels.in()<< "): (null)"
+ << llendl;
+ }
+ }
+#endif
+ PUMP_DEBUG;
+ status = (*it).mPipe->process(
+ (*it).mChannels,
+ chain.mData,
+ chain.mEOS,
+ chain.mContext,
+ this);
+#if LL_DEBUG_SPEW_BUFFER_CHANNEL_OUT
+ if(chain.mData)
+ {
+ char* buf = NULL;
+ S32 bytes = chain.mData->countAfter((*it).mChannels.out(), NULL);
+ if(bytes)
+ {
+ buf = new char[bytes + 1];
+ chain.mData->readAfter(
+ (*it).mChannels.out(),
+ NULL,
+ (U8*)buf,
+ bytes);
+ buf[bytes] = '\0';
+ llinfos << "CHANNEL OUT(" << (*it).mChannels.out()<< "): "
+ << buf << llendl;
+ delete[] buf;
+ buf = NULL;
+ }
+ else
+ {
+ llinfos << "CHANNEL OUT(" << (*it).mChannels.out()<< "): (null)"
+ << llendl;
+ }
+ }
+#endif
+
+#if LL_DEBUG_PROCESS_RETURN_VALUE
+ // Only bother with the success codes - error codes are logged
+ // below.
+ if(LLIOPipe::isSuccess(status))
+ {
+ llinfos << "Pipe returned: '"
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ << typeid(*((*it).mPipe)).name() << "':'"
+#endif
+ << LLIOPipe::lookupStatusString(status) << "'" << llendl;
+ }
+#endif
+
+ PUMP_DEBUG;
+ switch(status)
+ {
+ case LLIOPipe::STATUS_OK:
+ // no-op
+ break;
+ case LLIOPipe::STATUS_STOP:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ chain.mHead = end;
+ keep_going = false;
+ break;
+ case LLIOPipe::STATUS_DONE:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ chain.mHead = (it + 1);
+ chain.mEOS = true;
+ break;
+ case LLIOPipe::STATUS_BREAK:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ keep_going = false;
+ break;
+ case LLIOPipe::STATUS_NEED_PROCESS:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ if(!need_process_signaled)
+ {
+ need_process_signaled = true;
+ chain.mHead = it;
+ }
+ break;
+ default:
+ PUMP_DEBUG;
+ if(LLIOPipe::isError(status))
+ {
+ llinfos << "Pump generated pipe error: '"
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ << typeid(*((*it).mPipe)).name() << "':'"
+#endif
+ << LLIOPipe::lookupStatusString(status)
+ << "'" << llendl;
+#if LL_DEBUG_SPEW_BUFFER_CHANNEL_IN_ON_ERROR
+ if(chain.mData)
+ {
+ char* buf = NULL;
+ S32 bytes = chain.mData->countAfter(
+ (*it).mChannels.in(),
+ NULL);
+ if(bytes)
+ {
+ buf = new char[bytes + 1];
+ chain.mData->readAfter(
+ (*it).mChannels.in(),
+ NULL,
+ (U8*)buf,
+ bytes);
+ buf[bytes] = '\0';
+ llinfos << "Input After Error: " << buf << llendl;
+ delete[] buf;
+ buf = NULL;
+ }
+ else
+ {
+ llinfos << "Input After Error: (null)" << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Input After Error: (null)" << llendl;
+ }
+#endif
+ keep_going = false;
+ chain.mHead = it;
+ if(!handleChainError(chain, status))
+ {
+ chain.mHead = end;
+ }
+ }
+ else
+ {
+ llinfos << "Unhandled status code: " << status << ":"
+ << LLIOPipe::lookupStatusString(status) << llendl;
+ }
+ break;
+ }
+ PUMP_DEBUG;
+ } while(keep_going && (++it != end));
+ PUMP_DEBUG;
+}
+
+bool LLPumpIO::handleChainError(
+ LLChainInfo& chain,
+ LLIOPipe::EStatus error)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ links_t::reverse_iterator rit;
+ if(chain.mHead == chain.mChainLinks.end())
+ {
+ rit = links_t::reverse_iterator(chain.mHead);
+ }
+ else
+ {
+ rit = links_t::reverse_iterator(chain.mHead + 1);
+ }
+
+ links_t::reverse_iterator rend = chain.mChainLinks.rend();
+ bool handled = false;
+ bool keep_going = true;
+ do
+ {
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Passing error to " << typeid(*((*rit).mPipe)).name()
+ << "." << llendl;
+#endif
+ error = (*rit).mPipe->handleError(error, this);
+ switch(error)
+ {
+ case LLIOPipe::STATUS_OK:
+ handled = true;
+ chain.mHead = rit.base();
+ break;
+ case LLIOPipe::STATUS_STOP:
+ case LLIOPipe::STATUS_DONE:
+ case LLIOPipe::STATUS_BREAK:
+ case LLIOPipe::STATUS_NEED_PROCESS:
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Pipe " << typeid(*((*rit).mPipe)).name()
+ << " returned code to stop error handler." << llendl;
+#endif
+ keep_going = false;
+ break;
+ default:
+ if(LLIOPipe::isSuccess(error))
+ {
+ llinfos << "Unhandled status code: " << error << ":"
+ << LLIOPipe::lookupStatusString(error) << llendl;
+ error = LLIOPipe::STATUS_ERROR;
+ keep_going = false;
+ }
+ break;
+ }
+ } while(keep_going && !handled && (++rit != rend));
+ return handled;
+}
+
+/**
+ * LLPumpIO::LLChainInfo
+ */
+
+LLPumpIO::LLChainInfo::LLChainInfo() :
+ mInit(false),
+ mLock(0),
+ mEOS(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ mTimer.setTimerExpirySec(DEFAULT_CHAIN_EXPIRY_SECS);
+}
+
+void LLPumpIO::LLChainInfo::setTimeoutSeconds(F32 timeout)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(timeout > 0.0f)
+ {
+ mTimer.start();
+ mTimer.reset();
+ mTimer.setTimerExpirySec(timeout);
+ }
+ else
+ {
+ mTimer.stop();
+ }
+}
diff --git a/indra/llmessage/llpumpio.h b/indra/llmessage/llpumpio.h
new file mode 100644
index 0000000000..50f7411298
--- /dev/null
+++ b/indra/llmessage/llpumpio.h
@@ -0,0 +1,406 @@
+/**
+ * @file llpumpio.h
+ * @author Phoenix
+ * @date 2004-11-19
+ * @brief Declaration of pump class which manages io chains.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPUMPIO_H
+#define LL_LLPUMPIO_H
+
+#include <set>
+#if LL_LINUX // needed for PATH_MAX in APR.
+#include <sys/param.h>
+#endif
+
+#include "apr-1/apr_pools.h"
+#include "llbuffer.h"
+#include "llframetimer.h"
+#include "lliopipe.h"
+#include "llrun.h"
+
+// Define this to enable use with the APR thread library.
+//#define LL_THREADS_APR 1
+
+// some simple constants to help with timeouts
+extern const F32 DEFAULT_CHAIN_EXPIRY_SECS;
+extern const F32 SHORT_CHAIN_EXPIRY_SECS;
+extern const F32 NEVER_CHAIN_EXPIRY_SECS;
+
+/**
+ * @class LLPumpIO
+ * @brief Class to manage sets of io chains.
+ *
+ * The pump class provides a thread abstraction for doing IO based
+ * communication between two threads in a structured and optimized for
+ * processor time. The primary usage is to create a pump, and call
+ * <code>pump()</code> on a thread used for IO and call
+ * <code>respond()</code> on a thread that is expected to do higher
+ * level processing. You can call almost any other method from any
+ * thread - see notes for each method for details. In order for the
+ * threading abstraction to work, you need to call <code>prime()</code>
+ * with a valid apr pool.
+ * A pump instance manages much of the state for the pipe, including
+ * the list of pipes in the chain, the channel for each element in the
+ * chain, the buffer, and if any pipe has marked the stream or process
+ * as done. Pipes can also set file descriptor based conditional
+ * statements so that calls to process do not happen until data is
+ * ready to be read or written. Pipes control execution of calls to
+ * process by returning a status code such as STATUS_OK or
+ * STATUS_BREAK.
+ * One way to conceptualize the way IO will work is that a pump
+ * combines the unit processing of pipes to behave like file pipes on
+ * the unix command line.
+ */
+class LLPumpIO
+{
+public:
+ /**
+ * @brief Constructor.
+ */
+ LLPumpIO(apr_pool_t* pool);
+
+ /**
+ * @brief Destructor.
+ */
+ ~LLPumpIO();
+
+ /**
+ * @brief Prepare this pump for usage.
+ *
+ * If you fail to call this method prior to use, the pump will
+ * try to work, but will not come with any thread locking
+ * mechanisms.
+ * @param pool The apr pool to use.
+ * @return Returns true if the pump is primed.
+ */
+ bool prime(apr_pool_t* pool);
+
+ /**
+ * @brief Typedef for having a chain of pipes.
+ */
+ typedef std::vector<LLIOPipe::ptr_t> chain_t;
+
+ /**
+ * @brief Add a chain to this pump and process in the next cycle.
+ *
+ * This method will automatically generate a buffer and assign
+ * each link in the chain as if it were the consumer to the
+ * previous.
+ * @param chain The pipes for the chain
+ * @param timeout The number of seconds in the future to
+ * expire. Pass in 0.0f to never expire.
+ * @return Returns true if anything was added to the pump.
+ */
+ bool addChain(const chain_t& chain, F32 timeout);
+
+ /**
+ * @brief Struct to associate a pipe with it's buffer io indexes.
+ */
+ struct LLLinkInfo
+ {
+ LLIOPipe::ptr_t mPipe;
+ LLChannelDescriptors mChannels;
+ };
+
+ /**
+ * @brief Typedef for having a chain of <code>LLLinkInfo</code>
+ * instances.
+ */
+ typedef std::vector<LLLinkInfo> links_t;
+
+ /**
+ * @brief Add a chain to this pump and process in the next cycle.
+ *
+ * This method provides a slightly more sophisticated method for
+ * adding a chain where the caller can specify which link elements
+ * are on what channels. This method will fail if no buffer is
+ * provided since any calls to generate new channels for the
+ * buffers will cause unpredictable interleaving of data.
+ * @param links The pipes and io indexes for the chain
+ * @param data Shared pointer to data buffer
+ * @param context Potentially undefined context meta-data for chain.
+ * @param timeout The number of seconds in the future to
+ * expire. Pass in 0.0f to never expire.
+ * @return Returns true if anything was added to the pump.
+ */
+ bool addChain(
+ const links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context,
+ F32 timeout);
+
+ /**
+ * @brief Set or clear a timeout for the running chain
+ *
+ * @param timeout The number of seconds in the future to
+ * expire. Pass in 0.0f to never expire.
+ * @return Returns true if the timer was set.
+ */
+ bool setTimeoutSeconds(F32 timeout);
+
+ /**
+ * @brief Set up file descriptors for for the running chain.
+ * @see rebuildPollset()
+ *
+ * There is currently a limit of one conditional per pipe.
+ * *NOTE: The internal mechanism for building a pollset based on
+ * pipe/pollfd/chain generates an epoll error on linux (and
+ * probably behaves similarly on other platforms) because the
+ * pollset rebuilder will add each apr_pollfd_t serially. This
+ * does not matter for pipes on the same chain, since any
+ * signalled pipe will eventually invoke a call to process(), but
+ * is a problem if the same apr_pollfd_t is on different
+ * chains. Once we have more than just network i/o on the pump,
+ * this might matter.
+ * *FIX: Given the structure of the pump and pipe relationship,
+ * this should probably go through a different mechanism than the
+ * pump. I think it would be best if the pipe had some kind of
+ * controller which was passed into <code>process()</code> rather
+ * than the pump which exposed this interface.
+ * @param pipe The pipe which is setting a conditional
+ * @param poll The entire socket and read/write condition - null to remove
+ * @return Returns true if the poll state was set.
+ */
+ bool setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll);
+
+ /**
+ * @brief Lock the current chain.
+ * @see sleepChain() since it relies on the implementation of this method.
+ *
+ * This locks the currently running chain so that no more calls to
+ * <code>process()</code> until you call <code>clearLock()</code>
+ * with the lock identifier.
+ * *FIX: Given the structure of the pump and pipe relationship,
+ * this should probably go through a different mechanism than the
+ * pump. I think it would be best if the pipe had some kind of
+ * controller which was passed into <code>process()</code> rather
+ * than the pump which exposed this interface.
+ * @return Returns the lock identifer to be used in
+ * <code>clearLock()</code> or 0 on failure.
+ */
+ S32 setLock();
+
+ /**
+ * @brief Clears the identified lock.
+ *
+ * @param links A container for the links which will be appended
+ */
+ void clearLock(S32 key);
+
+ /**
+ * @brief Stop processing a chain for a while.
+ * @see setLock()
+ *
+ * This method will <em>not</em> update the timeout for this
+ * chain, so it is possible to sleep the chain until it is
+ * collected by the pump during a timeout cleanup.
+ * @param seconds The number of seconds in the future to
+ * resume processing.
+ * @return Returns true if the
+ */
+ bool sleepChain(F64 seconds);
+
+ /**
+ * @brief Copy the currently running chain link info
+ *
+ * *FIX: Given the structure of the pump and pipe relationship,
+ * this should probably go through a different mechanism than the
+ * pump. I think it would be best if the pipe had some kind of
+ * controller which was passed into <code>process()</code> rather
+ * than the pump which exposed this interface.
+ * @param links A container for the links which will be appended
+ * @return Returns true if the currently running chain was copied.
+ */
+ bool copyCurrentLinkInfo(links_t& links) const;
+
+ /**
+ * @brief Call this method to call process on all running chains.
+ *
+ * This method iterates through the running chains, and if all
+ * pipe on a chain are unconditionally ready or if any pipe has
+ * any conditional processiong condition then process will be
+ * called on every chain which has requested processing. that
+ * chain has a file descriptor ready, <code>process()</code> will
+ * be called for all pipes which have requested it.
+ */
+ void pump();
+
+ /**
+ * @brief Add a chain to a special queue which will be called
+ * during the next call to <code>callback()</code> and then
+ * dropped from the queue.
+ *
+ * @param chain The IO chain that will get one <code>process()</code>.
+ */
+ //void respond(const chain_t& pipes);
+
+ /**
+ * @brief Add pipe to a special queue which will be called
+ * during the next call to <code>callback()</code> and then dropped
+ * from the queue.
+ *
+ * This call will add a single pipe, with no buffer, context, or
+ * channel information to the callback queue. It will be called
+ * once, and then dropped.
+ * @param pipe A single io pipe which will be called
+ * @return Returns true if anything was added to the pump.
+ */
+ bool respond(LLIOPipe* pipe);
+
+ /**
+ * @brief Add a chain to a special queue which will be called
+ * during the next call to <code>callback()</code> and then
+ * dropped from the queue.
+ *
+ * It is important to remember that you should not add a data
+ * buffer or context which may still be in another chain - that
+ * will almost certainly lead to a problems. Ensure that you are
+ * done reading and writing to those parameters, have new
+ * generated, or empty pointers.
+ * @param links The pipes and io indexes for the chain
+ * @param data Shared pointer to data buffer
+ * @param context Potentially undefined context meta-data for chain.
+ * @return Returns true if anything was added to the pump.
+ */
+ bool respond(
+ const links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context);
+
+ /**
+ * @brief Run through the callback queue and call <code>process()</code>.
+ *
+ * This call will process all prending responses and call process
+ * on each. This method will then drop all processed callback
+ * requests which may lead to deleting the referenced objects.
+ */
+ void callback();
+
+ /**
+ * @brief Enumeration to send commands to the pump.
+ */
+ enum EControl
+ {
+ PAUSE,
+ RESUME,
+ };
+
+ /**
+ * @brief Send a command to the pump.
+ *
+ * @param op What control to send to the pump.
+ */
+ void control(EControl op);
+
+protected:
+ /**
+ * @brief State of the pump
+ */
+ enum EState
+ {
+ NORMAL,
+ PAUSING,
+ PAUSED
+ };
+
+ // instance data
+ EState mState;
+ bool mRebuildPollset;
+ apr_pollset_t* mPollset;
+ S32 mPollsetClientID;
+ S32 mNextLock;
+ std::set<S32> mClearLocks;
+
+ // This is the pump's runnable scheduler used for handling
+ // expiring locks.
+ LLRunner mRunner;
+
+ // This structure is the stuff we track while running chains.
+ struct LLChainInfo
+ {
+ // methods
+ LLChainInfo();
+ void setTimeoutSeconds(F32 timeout);
+
+ // basic member data
+ bool mInit;
+ S32 mLock;
+ LLFrameTimer mTimer;
+ links_t::iterator mHead;
+ links_t mChainLinks;
+ LLIOPipe::buffer_ptr_t mData;
+ bool mEOS;
+ LLSD mContext;
+
+ // tracking inside the pump
+ typedef std::pair<LLIOPipe::ptr_t, apr_pollfd_t> pipe_conditional_t;
+ typedef std::vector<pipe_conditional_t> conditionals_t;
+ conditionals_t mDescriptors;
+ };
+
+ // All the running chains & info
+ typedef std::vector<LLChainInfo> pending_chains_t;
+ pending_chains_t mPendingChains;
+ typedef std::list<LLChainInfo> running_chains_t;
+ running_chains_t mRunningChains;
+
+ typedef running_chains_t::iterator current_chain_t;
+ current_chain_t mCurrentChain;
+
+ // structures necessary for doing callbacks
+ // since the callbacks only get one chance to run, we do not have
+ // to maintain a list.
+ typedef std::vector<LLChainInfo> callbacks_t;
+ callbacks_t mPendingCallbacks;
+ callbacks_t mCallbacks;
+
+ // memory allocator for pollsets & mutexes.
+ apr_pool_t* mPool;
+ apr_pool_t* mCurrentPool;
+ S32 mCurrentPoolReallocCount;
+
+#if LL_THREADS_APR
+ apr_thread_mutex_t* mChainsMutex;
+ apr_thread_mutex_t* mCallbackMutex;
+#else
+ int* mChainsMutex;
+ int* mCallbackMutex;
+#endif
+
+protected:
+ void initialize(apr_pool_t* pool);
+ void cleanup();
+
+ /**
+ * @brief Given the internal state of the chains, rebuild the pollset
+ * @see setConditional()
+ */
+ void rebuildPollset();
+
+ /**
+ * @brief Process the chain passed in.
+ *
+ * This method will potentially modify the internals of the
+ * chain. On end, the chain.mHead will equal
+ * chain.mChainLinks.end().
+ * @param chain The LLChainInfo object to work on.
+ */
+ void processChain(LLChainInfo& chain);
+
+ /**
+ * @brief Rewind through the chain to try to recover from an error.
+ *
+ * This method will potentially modify the internals of the
+ * chain.
+ * @param chain The LLChainInfo object to work on.
+ * @return Retuns true if someone handled the error
+ */
+ bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error);
+};
+
+
+#endif // LL_LLPUMPIO_H
diff --git a/indra/llmessage/llqueryflags.h b/indra/llmessage/llqueryflags.h
new file mode 100644
index 0000000000..be6c9acf67
--- /dev/null
+++ b/indra/llmessage/llqueryflags.h
@@ -0,0 +1,32 @@
+/**
+ * @file llqueryflags.h
+ * @brief Flags for directory queries
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLQUERYFLAGS_H
+#define LL_LLQUERYFLAGS_H
+
+// Binary flags used for Find queries, shared between viewer and dataserver.
+
+// DirFindQuery flags
+const U32 DFQ_PEOPLE = 0x0001;
+const U32 DFQ_ONLINE = 0x0002;
+//const U32 DFQ_PLACES = 0x0004;
+const U32 DFQ_EVENTS = 0x0008;
+const U32 DFQ_GROUPS = 0x0010;
+const U32 DFQ_DATE_EVENTS = 0x0020;
+
+const U32 DFQ_AGENT_OWNED = 0x0040;
+const U32 DFQ_FOR_SALE = 0x0080;
+const U32 DFQ_GROUP_OWNED = 0x0100;
+//const U32 DFQ_AUCTION = 0x0200;
+const U32 DFQ_DWELL_SORT = 0x0400;
+const U32 DFQ_PG_SIMS_ONLY = 0x0800;
+const U32 DFQ_PICTURES_ONLY = 0x1000;
+const U32 DFQ_PG_EVENTS_ONLY = 0x2000;
+const U32 DFQ_MATURE_SIMS_ONLY = 0x4000;
+
+#endif
diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h
new file mode 100644
index 0000000000..3f37c72cee
--- /dev/null
+++ b/indra/llmessage/llregionflags.h
@@ -0,0 +1,157 @@
+/**
+ * @file llregionflags.h
+ * @brief Flags that are sent in the statistics message region_flags field.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLREGIONFLAGS_H
+#define LL_LLREGIONFLAGS_H
+
+// Can you be hurt here? Should health be on?
+const U32 REGION_FLAGS_ALLOW_DAMAGE = (1 << 0);
+
+// Can you make landmarks here?
+const U32 REGION_FLAGS_ALLOW_LANDMARK = (1 << 1);
+
+// Do we reset the home position when someone teleports away from here?
+const U32 REGION_FLAGS_ALLOW_SET_HOME = (1 << 2);
+
+// Do we reset the home position when someone teleports away from here?
+const U32 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3);
+
+// Does the sun move?
+const U32 REGION_FLAGS_SUN_FIXED = (1 << 4);
+
+// Tax free zone (no taxes on objects, land, etc.)
+const U32 REGION_FLAGS_TAX_FREE = (1 << 5);
+
+// Can't change the terrain heightfield, even on owned parcels,
+// but can plant trees and grass.
+const U32 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6);
+
+// Can't release, sell, or buy land.
+const U32 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7);
+
+// All content wiped once per night
+const U32 REGION_FLAGS_SANDBOX = (1 << 8);
+
+const U32 REGION_FLAGS_SKIP_AGENT_ACTION = (1 << 10);
+const U32 REGION_FLAGS_SKIP_UPDATE_INTEREST_LIST= (1 << 11);
+const U32 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies
+const U32 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
+const U32 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
+const U32 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
+const U32 REGION_FLAGS_MAINLAND_VISIBLE = (1 << 16);
+const U32 REGION_FLAGS_PUBLIC_ALLOWED = (1 << 17);
+const U32 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
+
+// Is flight allowed?
+const U32 REGION_FLAGS_BLOCK_FLY = (1 << 19);
+
+// Is direct teleport (p2p) allowed?
+const U32 REGION_FLAGS_ALLOW_DIRECT_TELEPORT = (1 << 20);
+
+// Is there an administrative override on scripts in the region at the
+// moment. This is the similar skip scripts, except this flag is
+// presisted in the database on an estate level.
+const U32 REGION_FLAGS_ESTATE_SKIP_SCRIPTS = (1 << 21);
+
+const U32 REGION_FLAGS_RESTRICT_PUSHOBJECT = (1 << 22);
+
+const U32 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23);
+const U32 REGION_FLAGS_DENY_IDENTIFIED = (1 << 24);
+const U32 REGION_FLAGS_DENY_TRANSACTED = (1 << 25);
+
+const U32 REGION_FLAGS_ALLOW_PARCEL_CHANGES = (1 << 26);
+
+const U32 REGION_FLAGS_NULL_LAYER = (1 << 9);
+
+const U32 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK |
+ REGION_FLAGS_ALLOW_SET_HOME |
+ REGION_FLAGS_ALLOW_PARCEL_CHANGES;
+
+const U32 REGION_FLAGS_PRELUDE_SET = REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+const U32 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK
+ | REGION_FLAGS_ALLOW_SET_HOME;
+
+const U32 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
+ | REGION_FLAGS_MAINLAND_VISIBLE
+ | REGION_FLAGS_PUBLIC_ALLOWED
+ | REGION_FLAGS_SUN_FIXED
+ | REGION_FLAGS_DENY_ANONYMOUS
+ | REGION_FLAGS_DENY_IDENTIFIED
+ | REGION_FLAGS_DENY_TRANSACTED;
+
+inline BOOL is_prelude( U32 flags )
+{
+ // definition of prelude does not depend on fixed-sun
+ return 0 == (flags & REGION_FLAGS_PRELUDE_UNSET)
+ && 0 != (flags & REGION_FLAGS_PRELUDE_SET);
+}
+
+inline U32 set_prelude_flags(U32 flags)
+{
+ // also set the sun-fixed flag
+ return ((flags & ~REGION_FLAGS_PRELUDE_UNSET)
+ | (REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED));
+}
+
+inline U32 unset_prelude_flags(U32 flags)
+{
+ // also unset the fixed-sun flag
+ return ((flags | REGION_FLAGS_PRELUDE_UNSET)
+ & ~(REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED));
+}
+
+// estate constants. Need to match first few etries in indra.estate table.
+const U32 ESTATE_ALL = 0; // will not match in db, reserved key for logic
+const U32 ESTATE_MAINLAND = 1;
+const U32 ESTATE_ORIENTATION = 2;
+const U32 ESTATE_INTERNAL = 3;
+const U32 ESTATE_SHOWCASE = 4;
+const U32 ESTATE_KIDGRID = 5;
+const U32 ESTATE_LAST_LINDEN = 5; // last linden owned/managed estate
+
+// for EstateOwnerRequest, setaccess message
+const U32 ESTATE_ACCESS_ALLOWED_AGENTS = 1 << 0;
+const U32 ESTATE_ACCESS_ALLOWED_GROUPS = 1 << 1;
+const U32 ESTATE_ACCESS_BANNED_AGENTS = 1 << 2;
+const U32 ESTATE_ACCESS_MANAGERS = 1 << 3;
+
+//maximum number of access list entries we can fit in one packet
+const S32 ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET = 63;
+
+// for reply to "getinfo", don't need to forward to all sims in estate
+const U32 ESTATE_ACCESS_SEND_TO_AGENT_ONLY = 1 << 4;
+
+const U32 ESTATE_ACCESS_ALL = ESTATE_ACCESS_ALLOWED_AGENTS
+ | ESTATE_ACCESS_ALLOWED_GROUPS
+ | ESTATE_ACCESS_BANNED_AGENTS
+ | ESTATE_ACCESS_MANAGERS;
+
+// for EstateOwnerRequest, estateaccessdelta message
+const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1 << 0;
+const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1 << 1;
+
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1 << 2;
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1 << 3;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1 << 4;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1 << 5;
+const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1 << 6;
+const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1 << 7;
+const U32 ESTATE_ACCESS_MANAGER_ADD = 1 << 8;
+const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9;
+
+const S32 ESTATE_MAX_MANAGERS = 10;
+const S32 ESTATE_MAX_ACCESS_IDS = 300; // max for access, banned
+const S32 ESTATE_MAX_GROUP_IDS = (S32) ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET;
+
+// 'Sim Wide Delete' flags
+const U32 SWD_OTHERS_LAND_ONLY = (1 << 0);
+const U32 SWD_ALWAYS_RETURN_OBJECTS = (1 << 1);
+const U32 SWD_SCRIPTED_ONLY = (1 << 2);
+
+#endif
+
diff --git a/indra/llmessage/llregionhandle.h b/indra/llmessage/llregionhandle.h
new file mode 100644
index 0000000000..41d104231c
--- /dev/null
+++ b/indra/llmessage/llregionhandle.h
@@ -0,0 +1,110 @@
+/**
+ * @file llregionhandle.h
+ * @brief Routines for converting positions to/from region handles.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLREGIONHANDLE_H
+#define LL_LLREGIONHANDLE_H
+
+#include <math.h>
+
+#include "indra_constants.h"
+#include "v3math.h"
+#include "v3dmath.h"
+
+inline U64 to_region_handle(const U32 x_origin, const U32 y_origin)
+{
+ U64 region_handle;
+ region_handle = ((U64)x_origin) << 32;
+ region_handle |= (U64) y_origin;
+ return region_handle;
+}
+
+inline U64 to_region_handle(const LLVector3d& pos_global)
+{
+ U32 global_x = (U32)pos_global.mdV[VX];
+ global_x -= global_x % 256;
+
+ U32 global_y = (U32)pos_global.mdV[VY];
+ global_y -= global_y % 256;
+
+ return to_region_handle(global_x, global_y);
+}
+
+inline U64 to_region_handle_global(const F32 x_global, const F32 y_global)
+{
+ // Round down to the nearest origin
+ U32 x_origin = (U32)x_global;
+ x_origin -= x_origin % REGION_WIDTH_U32;
+ U32 y_origin = (U32)y_global;
+ y_origin -= y_origin % REGION_WIDTH_U32;
+ U64 region_handle;
+ region_handle = ((U64)x_origin) << 32;
+ region_handle |= (U64) y_origin;
+ return region_handle;
+}
+
+inline BOOL to_region_handle(const F32 x_pos, const F32 y_pos, U64 *region_handle)
+{
+ U32 x_int, y_int;
+ if (x_pos < 0.f)
+ {
+// llwarns << "to_region_handle:Clamping negative x position " << x_pos << " to zero!" << llendl;
+ return FALSE;
+ }
+ else
+ {
+ x_int = (U32)llround(x_pos);
+ }
+ if (y_pos < 0.f)
+ {
+// llwarns << "to_region_handle:Clamping negative y position " << y_pos << " to zero!" << llendl;
+ return FALSE;
+ }
+ else
+ {
+ y_int = (U32)llround(y_pos);
+ }
+ *region_handle = to_region_handle(x_int, y_int);
+ return TRUE;
+}
+
+// stuff the word-frame XY location of sim's SouthWest corner in x_pos, y_pos
+inline void from_region_handle(const U64 &region_handle, F32 *x_pos, F32 *y_pos)
+{
+ *x_pos = (F32)((U32)(region_handle >> 32));
+ *y_pos = (F32)((U32)(region_handle & 0xFFFFFFFF));
+}
+
+// stuff the word-frame XY location of sim's SouthWest corner in x_pos, y_pos
+inline void from_region_handle(const U64 &region_handle, U32 *x_pos, U32 *y_pos)
+{
+ *x_pos = ((U32)(region_handle >> 32));
+ *y_pos = ((U32)(region_handle & 0xFFFFFFFF));
+}
+
+// return the word-frame XY location of sim's SouthWest corner in LLVector3d
+inline LLVector3d from_region_handle(const U64 &region_handle)
+{
+ return LLVector3d(((U32)(region_handle >> 32)), (U32)(region_handle & 0xFFFFFFFF), 0.f);
+}
+
+// grid-based region handle encoding. pass in a grid position
+// (eg: 1000,1000) and this will return the region handle.
+inline U64 grid_to_region_handle(U32 grid_x, U32 grid_y)
+{
+ return to_region_handle(grid_x * REGION_WIDTH_UNITS,
+ grid_y * REGION_WIDTH_UNITS);
+}
+
+inline void grid_from_region_handle(const U64& region_handle, U32* grid_x, U32* grid_y)
+{
+ from_region_handle(region_handle, grid_x, grid_y);
+ *grid_x /= REGION_WIDTH_UNITS;
+ *grid_y /= REGION_WIDTH_UNITS;
+}
+
+#endif
diff --git a/indra/llmessage/llsdappservices.cpp b/indra/llmessage/llsdappservices.cpp
new file mode 100644
index 0000000000..c10e9bd113
--- /dev/null
+++ b/indra/llmessage/llsdappservices.cpp
@@ -0,0 +1,259 @@
+/**
+ * @file llsdappservices.cpp
+ * @author Phoenix
+ * @date 2006-09-12
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdappservices.h"
+
+#include "llapp.h"
+#include "llhttpnode.h"
+#include "llsdserialize.h"
+
+void LLSDAppServices::useServices()
+{
+ /*
+ Having this function body here, causes the classes and globals in this
+ file to be linked into any program that uses the llmessage library.
+ */
+}
+
+class LLHTTPConfigService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("GET an array of all the options in priority order.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD get() const
+ {
+ LLSD result;
+ LLApp* app = LLApp::instance();
+ for(int ii = 0; ii < LLApp::PRIORITY_COUNT; ++ii)
+ {
+ result.append(app->getOptionData((LLApp::OptionPriority)ii));
+ }
+ return result;
+ }
+};
+
+LLHTTPRegistration<LLHTTPConfigService>
+ gHTTPRegistratiAppConfig("/app/config");
+
+class LLHTTPConfigRuntimeService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Manipulate a map of runtime-override options.");
+ desc.getAPI();
+ desc.postAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD get() const
+ {
+ return LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ }
+
+ virtual void post(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ LLSD result = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ LLSD::map_const_iterator iter = input.beginMap();
+ LLSD::map_const_iterator end = input.endMap();
+ for(; iter != end; ++iter)
+ {
+ result[(*iter).first] = (*iter).second;
+ }
+ LLApp::instance()->setOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE,
+ result);
+ response->result(result);
+ }
+};
+
+LLHTTPRegistration<LLHTTPConfigRuntimeService>
+ gHTTPRegistrationRuntimeConfig("/app/config/runtime-override");
+
+class LLHTTPConfigRuntimeSingleService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Manipulate a single runtime-override option.");
+ desc.getAPI();
+ desc.putAPI();
+ desc.delAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual bool validate(const std::string& name, LLSD& context) const
+ {
+ //llinfos << "validate: " << name << ", "
+ // << LLSDOStreamer<LLSDNotationFormatter>(context) << llendl;
+ if((std::string("PUT") == context["request"]["verb"].asString()) && !name.empty())
+ {
+ return true;
+ }
+ else
+ {
+ // This is for GET and DELETE
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ if(options.has(name)) return true;
+ else return false;
+ }
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ response->result(options[name]);
+ }
+
+ virtual void put(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ options[name] = input;
+ LLApp::instance()->setOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE,
+ options);
+ response->result(input);
+ }
+
+ virtual void del(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ options.erase(name);
+ LLApp::instance()->setOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE,
+ options);
+ response->result(LLSD());
+ }
+};
+
+LLHTTPRegistration<LLHTTPConfigRuntimeSingleService>
+ gHTTPRegistrationRuntimeSingleConfig(
+ "/app/config/runtime-override/<option-name>");
+
+template<int PRIORITY>
+class LLHTTPConfigPriorityService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Get a map of the options at this priority.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ response->result(LLApp::instance()->getOptionData(
+ (LLApp::OptionPriority)PRIORITY));
+ }
+};
+
+LLHTTPRegistration< LLHTTPConfigPriorityService<LLApp::PRIORITY_COMMAND_LINE> >
+ gHTTPRegistrationCommandLineConfig("/app/config/command-line");
+LLHTTPRegistration<
+ LLHTTPConfigPriorityService<LLApp::PRIORITY_SPECIFIC_CONFIGURATION> >
+ gHTTPRegistrationSpecificConfig("/app/config/specific");
+LLHTTPRegistration<
+ LLHTTPConfigPriorityService<LLApp::PRIORITY_GENERAL_CONFIGURATION> >
+ gHTTPRegistrationGeneralConfig("/app/config/general");
+LLHTTPRegistration< LLHTTPConfigPriorityService<LLApp::PRIORITY_DEFAULT> >
+ gHTTPRegistrationDefaultConfig("/app/config/default");
+
+class LLHTTPLiveConfigService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Get a map of the currently live options.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ LLSD result;
+ LLApp* app = LLApp::instance();
+ LLSD::map_const_iterator iter;
+ LLSD::map_const_iterator end;
+ for(int ii = LLApp::PRIORITY_COUNT - 1; ii >= 0; --ii)
+ {
+ LLSD options = app->getOptionData((LLApp::OptionPriority)ii);
+ iter = options.beginMap();
+ end = options.endMap();
+ for(; iter != end; ++iter)
+ {
+ result[(*iter).first] = (*iter).second;
+ }
+ }
+ response->result(result);
+ }
+};
+
+LLHTTPRegistration<LLHTTPLiveConfigService>
+ gHTTPRegistrationLiveConfig("/app/config/live");
+
+class LLHTTPLiveConfigSingleService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Get the named live option.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual bool validate(const std::string& name, LLSD& context) const
+ {
+ llinfos << "LLHTTPLiveConfigSingleService::validate(" << name
+ << ")" << llendl;
+ LLSD option = LLApp::instance()->getOption(name);
+ if(option) return true;
+ else return false;
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ response->result(LLApp::instance()->getOption(name));
+ }
+};
+
+LLHTTPRegistration<LLHTTPLiveConfigSingleService>
+ gHTTPRegistrationLiveSingleConfig("/app/config/live/<option-name>");
diff --git a/indra/llmessage/llsdappservices.h b/indra/llmessage/llsdappservices.h
new file mode 100644
index 0000000000..c9bc9570df
--- /dev/null
+++ b/indra/llmessage/llsdappservices.h
@@ -0,0 +1,40 @@
+/**
+ * @file llsdappservices.h
+ * @author Phoenix
+ * @date 2006-09-12
+ * @brief Header file to declare the /app common web services.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDAPPSERVICES_H
+#define LL_LLSDAPPSERVICES_H
+
+/**
+ * @class LLSDAppServices
+ * @brief This class forces a link to llsdappservices if the static
+ * method is called which declares the /app web services.
+ */
+class LLSDAppServices
+{
+public:
+ /**
+ * @brief Call this method to declare the /app common web services.
+ *
+ * This will register:
+ * /app/config
+ * /app/config/runtime-override
+ * /app/config/runtime-override/<option-name>
+ * /app/config/command-line
+ * /app/config/specific
+ * /app/config/general
+ * /app/config/default
+ * /app/config/live
+ * /app/config/live/<option-name>
+ */
+ static void useServices();
+};
+
+
+#endif // LL_LLSDAPPSERVICES_H
diff --git a/indra/llmessage/llsdhttpserver.cpp b/indra/llmessage/llsdhttpserver.cpp
new file mode 100644
index 0000000000..0eda0e69cb
--- /dev/null
+++ b/indra/llmessage/llsdhttpserver.cpp
@@ -0,0 +1,132 @@
+/**
+ * @file llsdhttpserver.cpp
+ * @brief Standard LLSD services
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdhttpserver.h"
+
+#include "llhttpnode.h"
+
+/**
+ * This module implements common services that should be included
+ * in all server URL trees. These services facilitate debugging and
+ * introsepction.
+ */
+
+void LLHTTPStandardServices::useServices()
+{
+ /*
+ Having this function body here, causes the classes and globals in this
+ file to be linked into any program that uses the llmessage library.
+ */
+}
+
+
+
+class LLHTTPHelloService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("says hello");
+ desc.getAPI();
+ desc.output("\"hello\"");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD get() const
+ {
+ LLSD result = "hello";
+ return result;
+ }
+};
+
+LLHTTPRegistration<LLHTTPHelloService>
+ gHTTPRegistrationWebHello("/web/hello");
+
+
+
+class LLHTTPEchoService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("echo input");
+ desc.postAPI();
+ desc.input("<any>");
+ desc.output("<the input>");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD post(const LLSD& params) const
+ {
+ return params;
+ }
+};
+
+LLHTTPRegistration<LLHTTPEchoService>
+ gHTTPRegistrationWebEcho("/web/echo");
+
+
+
+class LLAPIService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("information about the URLs this server supports");
+ desc.getAPI();
+ desc.output("a list of URLs supported");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual bool handles(const LLSD& remainder, LLSD& context) const
+ {
+ return followRemainder(remainder) != NULL;
+ }
+
+ virtual void get(ResponsePtr response, const LLSD& context) const
+ {
+ const LLSD& remainder = context["request"]["remainder"];
+
+ if (remainder.size() > 0)
+ {
+ const LLHTTPNode* node = followRemainder(remainder);
+ if (!node)
+ {
+ response->notFound();
+ return;
+ }
+
+ Description desc;
+ node->describe(desc);
+ response->result(desc.getInfo());
+ return;
+ }
+
+ response->result(rootNode()->allNodePaths());
+ }
+
+private:
+ const LLHTTPNode* followRemainder(const LLSD& remainder) const
+ {
+ const LLHTTPNode* node = rootNode();
+
+ LLSD::array_const_iterator i = remainder.beginArray();
+ LLSD::array_const_iterator end = remainder.endArray();
+ for (; node && i != end; ++i)
+ {
+ node = node->findNode(*i);
+ }
+
+ return node;
+ }
+};
+
+LLHTTPRegistration<LLAPIService>
+ gHTTPRegistrationWebServerApi("/web/server/api");
+
diff --git a/indra/llmessage/llsdhttpserver.h b/indra/llmessage/llsdhttpserver.h
new file mode 100644
index 0000000000..11ff38955b
--- /dev/null
+++ b/indra/llmessage/llsdhttpserver.h
@@ -0,0 +1,33 @@
+/**
+ * @file llsdhttpserver.h
+ * @brief Standard LLSD services
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDHTTPSERVER_H
+#define LL_LLSDHTTPSERVER_H
+
+/**
+ * This module implements and defines common services that should be included
+ * in all server URL trees. These services facilitate debugging and
+ * introsepction.
+ */
+
+class LLHTTPStandardServices
+{
+public:
+ static void useServices();
+ /**<
+ Having a call to this function causes the following services to be
+ registered:
+ /web/echo -- echo input
+ /web/hello -- return "hello"
+ /web/server/api -- return a list of url paths on the server
+ /web/server/api/<..path..>
+ -- return description of the path
+ */
+};
+
+#endif // LL_LLSDHTTPSERVER_H
diff --git a/indra/llmessage/llsdrpcclient.cpp b/indra/llmessage/llsdrpcclient.cpp
new file mode 100644
index 0000000000..4832ddaa58
--- /dev/null
+++ b/indra/llmessage/llsdrpcclient.cpp
@@ -0,0 +1,230 @@
+/**
+ * @file llsdrpcclient.cpp
+ * @author Phoenix
+ * @date 2005-11-05
+ * @brief Implementation of the llsd client classes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdrpcclient.h"
+
+#include "llbufferstream.h"
+#include "llfiltersd2xmlrpc.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llurlrequest.h"
+
+/**
+ * String constants
+ */
+static std::string LLSDRPC_RESPONSE_NAME("response");
+static std::string LLSDRPC_FAULT_NAME("fault");
+
+/**
+ * LLSDRPCResponse
+ */
+LLSDRPCResponse::LLSDRPCResponse() :
+ mIsError(false),
+ mIsFault(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+// virtual
+LLSDRPCResponse::~LLSDRPCResponse()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+bool LLSDRPCResponse::extractResponse(const LLSD& sd)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ bool rv = true;
+ if(sd.has(LLSDRPC_RESPONSE_NAME))
+ {
+ mReturnValue = sd[LLSDRPC_RESPONSE_NAME];
+ mIsFault = false;
+ }
+ else if(sd.has(LLSDRPC_FAULT_NAME))
+ {
+ mReturnValue = sd[LLSDRPC_FAULT_NAME];
+ mIsFault = true;
+ }
+ else
+ {
+ mReturnValue.clear();
+ mIsError = true;
+ rv = false;
+ }
+ return rv;
+}
+
+// virtual
+LLIOPipe::EStatus LLSDRPCResponse::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ if(mIsError)
+ {
+ error(pump);
+ }
+ else if(mIsFault)
+ {
+ fault(pump);
+ }
+ else
+ {
+ response(pump);
+ }
+ PUMP_DEBUG;
+ return STATUS_DONE;
+}
+
+/**
+ * LLSDRPCClient
+ */
+
+LLSDRPCClient::LLSDRPCClient() :
+ mState(STATE_NONE),
+ mQueue(EPBQ_PROCESS)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+// virtual
+LLSDRPCClient::~LLSDRPCClient()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+bool LLSDRPCClient::call(
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ //llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")"
+ // << llendl;
+ if(method.empty() || !response)
+ {
+ return false;
+ }
+ mState = STATE_READY;
+ mURI.assign(uri);
+ std::stringstream req;
+ req << LLSDRPC_REQUEST_HEADER_1 << method
+ << LLSDRPC_REQUEST_HEADER_2;
+ LLSDSerialize::toNotation(parameter, req);
+ req << LLSDRPC_REQUEST_FOOTER;
+ mRequest = req.str();
+ mQueue = queue;
+ mResponse = response;
+ return true;
+}
+
+bool LLSDRPCClient::call(
+ const std::string& uri,
+ const std::string& method,
+ const std::string& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ //llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")"
+ // << llendl;
+ if(method.empty() || parameter.empty() || !response)
+ {
+ return false;
+ }
+ mState = STATE_READY;
+ mURI.assign(uri);
+ std::stringstream req;
+ req << LLSDRPC_REQUEST_HEADER_1 << method
+ << LLSDRPC_REQUEST_HEADER_2 << parameter
+ << LLSDRPC_REQUEST_FOOTER;
+ mRequest = req.str();
+ mQueue = queue;
+ mResponse = response;
+ return true;
+}
+
+// virtual
+LLIOPipe::EStatus LLSDRPCClient::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ if((STATE_NONE == mState) || (!pump))
+ {
+ // You should have called the call() method already.
+ return STATUS_PRECONDITION_NOT_MET;
+ }
+ EStatus rv = STATUS_DONE;
+ switch(mState)
+ {
+ case STATE_READY:
+ {
+ PUMP_DEBUG;
+// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl;
+ buffer->append(
+ channels.out(),
+ (U8*)mRequest.c_str(),
+ mRequest.length());
+ context[CONTEXT_DEST_URI_SD_LABEL] = mURI;
+ mState = STATE_WAITING_FOR_RESPONSE;
+ break;
+ }
+ case STATE_WAITING_FOR_RESPONSE:
+ {
+ PUMP_DEBUG;
+ // The input channel has the sd response in it.
+ //lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE"
+ // << llendl;
+ LLBufferStream resp(channels, buffer.get());
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, resp);
+ LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get();
+ if (!response)
+ {
+ mState = STATE_DONE;
+ break;
+ }
+ response->extractResponse(sd);
+ if(EPBQ_PROCESS == mQueue)
+ {
+ LLPumpIO::chain_t chain;
+ chain.push_back(mResponse);
+ pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
+ }
+ else
+ {
+ pump->respond(mResponse.get());
+ }
+ mState = STATE_DONE;
+ break;
+ }
+ case STATE_DONE:
+ default:
+ PUMP_DEBUG;
+ llinfos << "invalid state to process" << llendl;
+ rv = STATUS_ERROR;
+ break;
+ }
+ return rv;
+}
diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h
new file mode 100644
index 0000000000..173a0d1dbb
--- /dev/null
+++ b/indra/llmessage/llsdrpcclient.h
@@ -0,0 +1,291 @@
+/**
+ * @file llsdrpcclient.h
+ * @author Phoenix
+ * @date 2005-11-05
+ * @brief Implementation and helpers for structure data RPC clients.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDRPCCLIENT_H
+#define LL_LLSDRPCCLIENT_H
+
+/**
+ * This file declares classes to encapsulate a basic structured data
+ * remote procedure client.
+ */
+
+#include "llchainio.h"
+#include "llfiltersd2xmlrpc.h"
+#include "lliopipe.h"
+#include "llurlrequest.h"
+
+/**
+ * @class LLSDRPCClientResponse
+ * @brief Abstract base class to represent a response from an SD server.
+ *
+ * This is used as a base class for callbacks generated from an
+ * structured data remote procedure call. The
+ * <code>extractResponse</code> method will deal with the llsdrpc method
+ * call overhead, and keep track of what to call during the next call
+ * into <code>process</code>. If you use this as a base class, you
+ * need to implement <code>response</code>, <code>fault</code>, and
+ * <code>error</code> to do something useful. When in those methods,
+ * you can parse and utilize the mReturnValue member data.
+ */
+class LLSDRPCResponse : public LLIOPipe
+{
+public:
+ LLSDRPCResponse();
+ virtual ~LLSDRPCResponse();
+
+ /**
+ * @brief This method extracts the response out of the sd passed in
+ *
+ * Any appropriate data found in the sd passed in will be
+ * extracted and managed by this object - not copied or cloned. It
+ * will still be up to the caller to delete the pointer passed in.
+ * @param sd The raw structured data response from the remote server.
+ * @return Returns true if this was able to parse the structured data.
+ */
+ bool extractResponse(const LLSD& sd);
+
+protected:
+ /**
+ * @brief Method called when the response is ready.
+ */
+ virtual bool response(LLPumpIO* pump) = 0;
+
+ /**
+ * @brief Method called when a fault is generated by the remote server.
+ */
+ virtual bool fault(LLPumpIO* pump) = 0;
+
+ /**
+ * @brief Method called when there was an error
+ */
+ virtual bool error(LLPumpIO* pump) = 0;
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLSD mReturnValue;
+ bool mIsError;
+ bool mIsFault;
+};
+
+/**
+ * @class LLSDRPCClient
+ * @brief Client class for a structured data remote procedure call.
+ *
+ * This class helps deal with making structured data calls to a remote
+ * server. You can visualize the calls as:
+ * <code>
+ * response = uri.method(parameter)
+ * </code>
+ * where you pass in everything to <code>call</code> and this class
+ * takes care of the rest of the details.
+ * In typical usage, you will derive a class from this class and
+ * provide an API more useful for the specific application at
+ * hand. For example, if you were writing a service to send an instant
+ * message, you could create an API for it to send the messsage, and
+ * that class would do the work of translating it into the method and
+ * parameter, find the destination, and invoke <code>call</call> with
+ * a useful implementation of LLSDRPCResponse passed in to handle the
+ * response from the network.
+ */
+class LLSDRPCClient : public LLIOPipe
+{
+public:
+ LLSDRPCClient();
+ virtual ~LLSDRPCClient();
+
+ /**
+ * @brief Enumeration for tracking which queue to process the
+ * response.
+ */
+ enum EPassBackQueue
+ {
+ EPBQ_PROCESS,
+ EPBQ_CALLBACK,
+ };
+
+ /**
+ * @brief Call a method on a remote LLSDRPCServer
+ *
+ * @param uri The remote object to call, eg,
+ * http://localhost/usher. If you are using a factory with a fixed
+ * url, the uri passed in will probably be ignored.
+ * @param method The method to call on the remote object
+ * @param parameter The parameter to pass into the remote
+ * object. It is up to the caller to delete the value passed in.
+ * @param response The object which gets the response.
+ * @param queue Specifies to call the response on the process or
+ * callback queue.
+ * @return Returns true if this object will be able to make the RPC call.
+ */
+ bool call(
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue);
+
+ /**
+ * @brief Call a method on a remote LLSDRPCServer
+ *
+ * @param uri The remote object to call, eg,
+ * http://localhost/usher. If you are using a factory with a fixed
+ * url, the uri passed in will probably be ignored.
+ * @param method The method to call on the remote object
+ * @param parameter The seriailized parameter to pass into the
+ * remote object.
+ * @param response The object which gets the response.
+ * @param queue Specifies to call the response on the process or
+ * callback queue.
+ * @return Returns true if this object will be able to make the RPC call.
+ */
+ bool call(
+ const std::string& uri,
+ const std::string& method,
+ const std::string& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue);
+
+protected:
+ /**
+ * @brief Enumeration for tracking client state.
+ */
+ enum EState
+ {
+ STATE_NONE,
+ STATE_READY,
+ STATE_WAITING_FOR_RESPONSE,
+ STATE_DONE
+ };
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ EState mState;
+ std::string mURI;
+ std::string mRequest;
+ EPassBackQueue mQueue;
+ LLIOPipe::ptr_t mResponse;
+};
+
+/**
+ * @class LLSDRPCClientFactory
+ * @brief Basic implementation for making an SD RPC client factory
+ *
+ * This class eases construction of a basic sd rpc client. Here is an
+ * example of it's use:
+ * <code>
+ * class LLUsefulService : public LLService { ... }
+ * LLService::registerCreator(
+ * "useful",
+ * LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>))
+ * </code>
+ */
+template<class Client>
+class LLSDRPCClientFactory : public LLChainIOFactory
+{
+public:
+ LLSDRPCClientFactory() {}
+ LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLSDRPCClientFactory::build" << llendl;
+ LLIOPipe::ptr_t service(new Client);
+ chain.push_back(service);
+ LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+ LLIOPipe::ptr_t http_pipe(http);
+ http->addHeader("Content-Type: text/llsd");
+ if(mURL.empty())
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
+ }
+ else
+ {
+ http->setURL(mURL);
+ }
+ chain.push_back(http_pipe);
+ chain.push_back(service);
+ return true;
+ }
+protected:
+ std::string mURL;
+};
+
+/**
+ * @class LLXMLSDRPCClientFactory
+ * @brief Basic implementation for making an XMLRPC to SD RPC client factory
+ *
+ * This class eases construction of a basic sd rpc client which uses
+ * xmlrpc as a serialization grammar. Here is an example of it's use:
+ * <code>
+ * class LLUsefulService : public LLService { ... }
+ * LLService::registerCreator(
+ * "useful",
+ * LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>))
+ * </code>
+ */
+template<class Client>
+class LLXMLSDRPCClientFactory : public LLChainIOFactory
+{
+public:
+ LLXMLSDRPCClientFactory() {}
+ LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLXMLSDRPCClientFactory::build" << llendl;
+ LLIOPipe::ptr_t service(new Client);
+ chain.push_back(service);
+ LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+ LLIOPipe::ptr_t http_pipe(http);
+ http->addHeader("Content-Type: text/xml");
+ if(mURL.empty())
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
+ }
+ else
+ {
+ http->setURL(mURL);
+ }
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL)));
+ chain.push_back(http_pipe);
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
+ chain.push_back(service);
+ return true;
+ }
+protected:
+ std::string mURL;
+};
+
+#endif // LL_LLSDRPCCLIENT_H
diff --git a/indra/llmessage/llsdrpcserver.cpp b/indra/llmessage/llsdrpcserver.cpp
new file mode 100644
index 0000000000..0348147a71
--- /dev/null
+++ b/indra/llmessage/llsdrpcserver.cpp
@@ -0,0 +1,322 @@
+/**
+ * @file llsdrpcserver.cpp
+ * @author Phoenix
+ * @date 2005-10-11
+ * @brief Implementation of the LLSDRPCServer and related classes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdrpcserver.h"
+
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+#include "llsdserialize.h"
+#include "llstl.h"
+
+static const char FAULT_PART_1[] = "{'fault':{'code':i";
+static const char FAULT_PART_2[] = ", 'description':'";
+static const char FAULT_PART_3[] = "'}}";
+
+static const char RESPONSE_PART_1[] = "{'response':";
+static const char RESPONSE_PART_2[] = "}";
+
+static const S32 FAULT_GENERIC = 1000;
+static const S32 FAULT_METHOD_NOT_FOUND = 1001;
+
+static const std::string LLSDRPC_METHOD_SD_NAME("method");
+static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter");
+
+
+/**
+ * LLSDRPCServer
+ */
+LLSDRPCServer::LLSDRPCServer() :
+ mState(LLSDRPCServer::STATE_NONE),
+ mPump(NULL),
+ mLock(0)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+}
+
+LLSDRPCServer::~LLSDRPCServer()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ std::for_each(
+ mMethods.begin(),
+ mMethods.end(),
+ llcompose1(
+ DeletePointerFunctor<LLSDRPCMethodCallBase>(),
+ llselect2nd<method_map_t::value_type>()));
+ std::for_each(
+ mCallbackMethods.begin(),
+ mCallbackMethods.end(),
+ llcompose1(
+ DeletePointerFunctor<LLSDRPCMethodCallBase>(),
+ llselect2nd<method_map_t::value_type>()));
+}
+
+
+// virtual
+ESDRPCSStatus LLSDRPCServer::deferredResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data) {
+ // subclass should provide a sane implementation
+ return ESDRPCS_DONE;
+}
+
+void LLSDRPCServer::clearLock()
+{
+ if(mLock && mPump)
+ {
+ mPump->clearLock(mLock);
+ mPump = NULL;
+ mLock = 0;
+ }
+}
+
+// virtual
+LLIOPipe::EStatus LLSDRPCServer::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+// lldebugs << "LLSDRPCServer::process_impl" << llendl;
+ // Once we have all the data, We need to read the sd on
+ // the the in channel, and respond on the out channel
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ std::string method_name;
+ LLIOPipe::EStatus status = STATUS_DONE;
+
+ switch(mState)
+ {
+ case STATE_DEFERRED:
+ PUMP_DEBUG;
+ if(ESDRPCS_DONE != deferredResponse(channels, buffer.get()))
+ {
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "deferred response failed.");
+ }
+ mState = STATE_DONE;
+ return STATUS_DONE;
+
+ case STATE_DONE:
+// lldebugs << "STATE_DONE" << llendl;
+ break;
+ case STATE_CALLBACK:
+// lldebugs << "STATE_CALLBACK" << llendl;
+ PUMP_DEBUG;
+ method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
+ if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
+ {
+ if(ESDRPCS_DONE != callbackMethod(
+ method_name,
+ mRequest[LLSDRPC_PARAMETER_SD_NAME],
+ channels,
+ buffer.get()))
+ {
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Callback method call failed.");
+ }
+ }
+ else
+ {
+ // this should never happen, since we should not be in
+ // this state unless we originally found a method and
+ // params during the first call to process.
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Invalid LLSDRPC sever state - callback without method.");
+ }
+ pump->clearLock(mLock);
+ mLock = 0;
+ mState = STATE_DONE;
+ break;
+ case STATE_NONE:
+// lldebugs << "STATE_NONE" << llendl;
+ default:
+ {
+ // First time we got here - process the SD request, and call
+ // the method.
+ PUMP_DEBUG;
+ LLBufferStream istr(channels, buffer.get());
+ mRequest.clear();
+ LLSDSerialize::fromNotation(mRequest, istr);
+
+ // { 'method':'...', 'parameter': ... }
+ method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
+ if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
+ {
+ ESDRPCSStatus rv = callMethod(
+ method_name,
+ mRequest[LLSDRPC_PARAMETER_SD_NAME],
+ channels,
+ buffer.get());
+ switch(rv)
+ {
+ case ESDRPCS_DEFERRED:
+ mPump = pump;
+ mLock = pump->setLock();
+ mState = STATE_DEFERRED;
+ status = STATUS_BREAK;
+ break;
+
+ case ESDRPCS_CALLBACK:
+ {
+ mState = STATE_CALLBACK;
+ LLPumpIO::LLLinkInfo link;
+ link.mPipe = LLIOPipe::ptr_t(this);
+ link.mChannels = channels;
+ LLPumpIO::links_t links;
+ links.push_back(link);
+ pump->respond(links, buffer, context);
+ mLock = pump->setLock();
+ status = STATUS_BREAK;
+ break;
+ }
+ case ESDRPCS_DONE:
+ mState = STATE_DONE;
+ break;
+ case ESDRPCS_ERROR:
+ default:
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Method call failed.");
+ break;
+ }
+ }
+ else
+ {
+ // send a fault
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Unable to find method and parameter in request.");
+ }
+ break;
+ }
+ }
+
+ PUMP_DEBUG;
+ return status;
+}
+
+// virtual
+ESDRPCSStatus LLSDRPCServer::callMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* response)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ // Try to find the method in the method table.
+ ESDRPCSStatus rv = ESDRPCS_DONE;
+ method_map_t::iterator it = mMethods.find(method);
+ if(it != mMethods.end())
+ {
+ rv = (*it).second->call(params, channels, response);
+ }
+ else
+ {
+ it = mCallbackMethods.find(method);
+ if(it == mCallbackMethods.end())
+ {
+ // method not found.
+ std::ostringstream message;
+ message << "rpc server unable to find method: " << method;
+ buildFault(
+ channels,
+ response,
+ FAULT_METHOD_NOT_FOUND,
+ message.str());
+ }
+ else
+ {
+ // we found it in the callback methods - tell the process
+ // to coordinate calling on the pump callback.
+ return ESDRPCS_CALLBACK;
+ }
+ }
+ return rv;
+}
+
+// virtual
+ESDRPCSStatus LLSDRPCServer::callbackMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* response)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ // Try to find the method in the callback method table.
+ ESDRPCSStatus rv = ESDRPCS_DONE;
+ method_map_t::iterator it = mCallbackMethods.find(method);
+ if(it != mCallbackMethods.end())
+ {
+ rv = (*it).second->call(params, channels, response);
+ }
+ else
+ {
+ std::ostringstream message;
+ message << "pcserver unable to find callback method: " << method;
+ buildFault(
+ channels,
+ response,
+ FAULT_METHOD_NOT_FOUND,
+ message.str());
+ }
+ return rv;
+}
+
+// static
+void LLSDRPCServer::buildFault(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ S32 code,
+ const std::string& msg)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ LLBufferStream ostr(channels, data);
+ ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3;
+ llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl;
+}
+
+// static
+void LLSDRPCServer::buildResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ const LLSD& response)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ LLBufferStream ostr(channels, data);
+ ostr << RESPONSE_PART_1;
+ LLSDSerialize::toNotation(response, ostr);
+ ostr << RESPONSE_PART_2;
+#if LL_DEBUG
+ std::ostringstream debug_ostr;
+ debug_ostr << "LLSDRPCServer::buildResponse: ";
+ LLSDSerialize::toNotation(response, debug_ostr);
+ llinfos << debug_ostr.str() << llendl;
+#endif
+}
diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h
new file mode 100644
index 0000000000..abb291d007
--- /dev/null
+++ b/indra/llmessage/llsdrpcserver.h
@@ -0,0 +1,342 @@
+/**
+ * @file llsdrpcserver.h
+ * @author Phoenix
+ * @date 2005-10-11
+ * @brief Declaration of the structured data remote procedure call server.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDRPCSERVER_H
+#define LL_LLSDRPCSERVER_H
+
+/**
+ * I've set this up to be pretty easy to use when you want to make a
+ * structured data rpc server which responds to methods by
+ * name. Derive a class from the LLSDRPCServer, and during
+ * construction (or initialization if you have the luxury) map method
+ * names to pointers to member functions. This will look a lot like:
+ *
+ * <code>
+ * class LLMessageAgents : public LLSDRPCServer {<br>
+ * public:<br>
+ * typedef LLSDRPCServer<LLUsher> mem_fn_t;<br>
+ * LLMessageAgents() {<br>
+ * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br>
+ * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br>
+ * }<br>
+ * protected:<br>
+ * rpc_IM(const LLSD& params,
+ * const LLChannelDescriptors& channels,
+ * LLBufferArray* data)
+ * {...}<br>
+ * rpc_Alert(const LLSD& params,
+ * const LLChannelDescriptors& channels,
+ * LLBufferArray* data)
+ * {...}<br>
+ * };<br>
+ * </code>
+ *
+ * The params are an array where each element in the array is a single
+ * parameter in the call.
+ *
+ * It is up to you to pack a valid serialized llsd response into the
+ * data object passed into the method, but you can use the helper
+ * methods below to help.
+ */
+
+#include <map>
+#include "lliopipe.h"
+#include "lliohttpserver.h"
+#include "llfiltersd2xmlrpc.h"
+
+class LLSD;
+
+/**
+ * @brief Enumeration for specifying server method call status. This
+ * enumeration controls how the server class will manage the pump
+ * process/callback mechanism.
+ */
+enum ESDRPCSStatus
+{
+ // The call went ok, but the response is not yet ready. The
+ // method will arrange for the clearLock() call to be made at
+ // a later date, after which, once the chain is being pumped
+ // again, deferredResponse() will be called to gather the result
+ ESDRPCS_DEFERRED,
+
+ // The LLSDRPCServer would like to handle the method on the
+ // callback queue of the pump.
+ ESDRPCS_CALLBACK,
+
+ // The method call finished and generated output.
+ ESDRPCS_DONE,
+
+ // Method failed for some unspecified reason - you should avoid
+ // this. A generic fault will be sent to the output.
+ ESDRPCS_ERROR,
+
+ ESDRPCS_COUNT,
+};
+
+/**
+ * @class LLSDRPCMethodCallBase
+ * @brief Base class for calling a member function in an sd rpcserver
+ * implementation.
+ */
+class LLSDRPCMethodCallBase
+{
+public:
+ LLSDRPCMethodCallBase() {}
+ virtual ~LLSDRPCMethodCallBase() {}
+
+ virtual ESDRPCSStatus call(
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* response) = 0;
+protected:
+};
+
+/**
+ * @class LLSDRPCMethodCall
+ * @brief Class which implements member function calls.
+ */
+template<class Server>
+class LLSDRPCMethodCall : public LLSDRPCMethodCallBase
+{
+public:
+ typedef ESDRPCSStatus (Server::*mem_fn)(
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+ LLSDRPCMethodCall(Server* s, mem_fn fn) :
+ mServer(s),
+ mMemFn(fn)
+ {
+ }
+ virtual ~LLSDRPCMethodCall() {}
+ virtual ESDRPCSStatus call(
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data)
+ {
+ return (*mServer.*mMemFn)(params, channels, data);
+ }
+
+protected:
+ Server* mServer;
+ mem_fn mMemFn;
+ //bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data);
+};
+
+
+/**
+ * @class LLSDRPCServer
+ * @brief Basic implementation of a structure data rpc server
+ *
+ * The rpc server is also designed to appropriately straddle the pump
+ * <code>process()</code> and <code>callback()</code> to specify which
+ * thread you want to work on when handling a method call. The
+ * <code>mMethods</code> methods are called from
+ * <code>process()</code>, while the <code>mCallbackMethods</code> are
+ * called when a pump is in a <code>callback()</code> cycle.
+ */
+class LLSDRPCServer : public LLIOPipe
+{
+public:
+ LLSDRPCServer();
+ virtual ~LLSDRPCServer();
+
+ /**
+ * enumeration for generic fault codes
+ */
+ enum
+ {
+ FAULT_BAD_REQUEST = 2000,
+ FAULT_NO_RESPONSE = 2001,
+ };
+
+ /**
+ * @brief Call this method to return an rpc fault.
+ *
+ * @param channel The channel for output on the data buffer
+ * @param data buffer which will recieve the final output
+ * @param code The fault code
+ * @param msg The fault message
+ */
+ static void buildFault(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ S32 code,
+ const std::string& msg);
+
+ /**
+ * @brief Call this method to build an rpc response.
+ *
+ * @param channel The channel for output on the data buffer
+ * @param data buffer which will recieve the final output
+ * @param response The return value from the method call
+ */
+ static void buildResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ const LLSD& response);
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+
+ /**
+ * @brief Enumeration to track the state of the rpc server instance
+ */
+ enum EState
+ {
+ STATE_NONE,
+ STATE_CALLBACK,
+ STATE_DEFERRED,
+ STATE_DONE
+ };
+
+ /**
+ * @brief This method is called when an http post comes in.
+ *
+ * The default behavior is to look at the method name, look up the
+ * method in the method table, and call it. If the method is not
+ * found, this function will build a fault response. You can
+ * implement your own version of this function if you want to hard
+ * wire some behavior or optimize things a bit.
+ * @param method The method name being called
+ * @param params The parameters
+ * @param channel The channel for output on the data buffer
+ * @param data The http data
+ * @return Returns the status of the method call, done/deferred/etc
+ */
+ virtual ESDRPCSStatus callMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+
+ /**
+ * @brief This method is called when a pump callback is processed.
+ *
+ * The default behavior is to look at the method name, look up the
+ * method in the callback method table, and call it. If the method
+ * is not found, this function will build a fault response. You
+ * can implement your own version of this function if you want to
+ * hard wire some behavior or optimize things a bit.
+ * @param method The method name being called
+ * @param params The parameters
+ * @param channel The channel for output on the data buffer
+ * @param data The http data
+ * @return Returns the status of the method call, done/deferred/etc
+ */
+ virtual ESDRPCSStatus callbackMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+
+ /**
+ * @brief Called after a deferred service is unlocked
+ *
+ * If a method returns ESDRPCS_DEFERRED, then the service chain
+ * will be locked and not processed until some other system calls
+ * clearLock() on the service instance again. At that point,
+ * once the pump starts processing the chain again, this method
+ * will be called so the service can output the final result
+ * into the buffers.
+ */
+ virtual ESDRPCSStatus deferredResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+
+ // donovan put this public here 7/27/06
+public:
+ /**
+ * @brief unlock a service that as ESDRPCS_DEFERRED
+ */
+ void clearLock();
+
+protected:
+ EState mState;
+ LLSD mRequest;
+ LLPumpIO* mPump;
+ S32 mLock;
+ typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t;
+ method_map_t mMethods;
+ method_map_t mCallbackMethods;
+};
+
+/**
+ * @name Helper Templates for making LLHTTPNodes
+ *
+ * These templates help in creating nodes for handing a service from
+ * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer.
+ *
+ * To use it:
+ * \code
+ * class LLUsefulServer : public LLSDRPCServer { ... }
+ *
+ * LLHTTPNode& root = LLCreateHTTPWireServer(...);
+ * root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>);
+ * root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>);
+ * \endcode
+ */
+//@{
+
+template<class Server>
+class LLSDRPCServerFactory : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
+ chain.push_back(LLIOPipe::ptr_t(new Server));
+ return true;
+ }
+};
+
+template<class Server>
+class LLSDRPCNode : public LLHTTPNodeForFactory<
+ LLSDRPCServerFactory<Server> >
+{
+};
+
+template<class Server>
+class LLXMLRPCServerFactory : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
+ chain.push_back(LLIOPipe::ptr_t(new Server));
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
+ return true;
+ }
+};
+
+template<class Server>
+class LLXMLRPCNode : public LLHTTPNodeForFactory<
+ LLXMLRPCServerFactory<Server> >
+{
+};
+
+//@}
+
+#endif // LL_LLSDRPCSERVER_H
diff --git a/indra/llmessage/llservice.cpp b/indra/llmessage/llservice.cpp
new file mode 100644
index 0000000000..03b04054dc
--- /dev/null
+++ b/indra/llmessage/llservice.cpp
@@ -0,0 +1,93 @@
+/**
+ * @file llservice.cpp
+ * @author Phoenix
+ * @date 2005-04-20
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llservice.h"
+
+LLService::creators_t LLService::sCreatorFunctors;
+
+LLService::LLService()
+{
+}
+
+LLService::~LLService()
+{
+}
+
+// static
+bool LLService::registerCreator(const std::string& name, creator_t fn)
+{
+ llinfos << "LLService::registerCreator(" << name << ")" << llendl;
+ if(name.empty())
+ {
+ return false;
+ }
+
+ creators_t::value_type vt(name, fn);
+ std::pair<creators_t::iterator, bool> rv = sCreatorFunctors.insert(vt);
+ return rv.second;
+
+ // alternately...
+ //std::string name_str(name);
+ //sCreatorFunctors[name_str] = fn;
+}
+
+// static
+LLIOPipe* LLService::activate(
+ const std::string& name,
+ LLPumpIO::chain_t& chain,
+ LLSD context)
+{
+ if(name.empty())
+ {
+ llinfos << "LLService::activate - no service specified." << llendl;
+ return NULL;
+ }
+ creators_t::iterator it = sCreatorFunctors.find(name);
+ LLIOPipe* rv = NULL;
+ if(it != sCreatorFunctors.end())
+ {
+ if((*it).second->build(chain, context))
+ {
+ rv = chain[0].get();
+ }
+ else
+ {
+ // empty out the chain, because failed service creation
+ // should just discard this stuff.
+ llwarns << "LLService::activate - unable to build chain: " << name
+ << llendl;
+ chain.clear();
+ }
+ }
+ else
+ {
+ llwarns << "LLService::activate - unable find factory: " << name
+ << llendl;
+ }
+ return rv;
+}
+
+// static
+bool LLService::discard(const std::string& name)
+{
+ if(name.empty())
+ {
+ return false;
+ }
+ creators_t::iterator it = sCreatorFunctors.find(name);
+ if(it != sCreatorFunctors.end())
+ {
+ //(*it).second->discard();
+ sCreatorFunctors.erase(it);
+ return true;
+ }
+ return false;
+}
+
diff --git a/indra/llmessage/llservice.h b/indra/llmessage/llservice.h
new file mode 100644
index 0000000000..e243e710d6
--- /dev/null
+++ b/indra/llmessage/llservice.h
@@ -0,0 +1,167 @@
+/**
+ * @file llservice.h
+ * @author Phoenix
+ * @date 2004-11-21
+ * @brief Declaration file for LLService and related classes.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSERVICE_H
+#define LL_LLSERVICE_H
+
+#include <string>
+#include <map>
+//#include <boost/intrusive_ptr.hpp>
+//#include <boost/shared_ptr.hpp>
+
+//#include "llframetimer.h"
+#include "lliopipe.h"
+#include "llchainio.h"
+
+#if 0
+class LLServiceCreator;
+/**
+ * intrusive pointer support
+ */
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLServiceCreator* p);
+ void intrusive_ptr_release(LLServiceCreator* p);
+};
+#endif
+
+/**
+ * @class LLServiceCreator
+ * @brief This class is an abstract base class for classes which create
+ * new <code>LLService</code> instances.
+ *
+ * Derive classes from this class which appropriately implement the
+ * <code>operator()</code> and destructor.
+ * @see LLService
+ */
+#if 0
+class LLServiceCreator
+{
+public:
+ typedef boost::intrusive_ptr<LLService> service_t;
+ virtual ~LLServiceCreator() {}
+ virtual service_t activate() = 0;
+ virtual void discard() = 0;
+
+protected:
+ LLServiceCreator() : mReferenceCount(0)
+ {
+ }
+
+private:
+ friend void boost::intrusive_ptr_add_ref(LLServiceCreator* p);
+ friend void boost::intrusive_ptr_release(LLServiceCreator* p);
+ U32 mReferenceCount;
+};
+#endif
+
+#if 0
+namespace boost
+{
+ inline void intrusive_ptr_add_ref(LLServiceCreator* p)
+ {
+ ++p->mReferenceCount;
+ }
+ inline void intrusive_ptr_release(LLServiceCreator* p)
+ {
+ if(0 == --p->mReferenceCount)
+ {
+ delete p;
+ }
+ }
+};
+#endif
+
+/**
+ * @class LLService
+ * @brief This class is the base class for the service classes.
+ * @see LLIOPipe
+ *
+ * The services map a string to a chain factory with a known interface
+ * at the front of the chain. So, to activate a service, call
+ * <code>activate()</code> with the name of the service needed which
+ * will call the associated factory, and return a pointer to the
+ * known interface.
+ * <b>NOTE:</b> If you are implementing a service factory, it is
+ * vitally important that the service pipe is at the front of the
+ * chain.
+ */
+class LLService : public LLIOPipe
+{
+public:
+ //typedef boost::intrusive_ptr<LLServiceCreator> creator_t;
+ //typedef boost::intrusive_ptr<LLService> service_t;
+ typedef boost::shared_ptr<LLChainIOFactory> creator_t;
+
+ /**
+ * @brief This method is used to register a protocol name with a
+ * a functor that creates the service.
+ *
+ * THOROUGH_DESCRIPTION
+ * @param aParameter A brief description of aParameter.
+ * @return Returns true if a service was successfully registered.
+ */
+ static bool registerCreator(const std::string& name, creator_t fn);
+
+ /**
+ * @brief This method connects to a service by name.
+ *
+ * @param name The name of the service to connect to.
+ * @param chain The constructed chain including the service instance.
+ * @param context Context for the activation.
+ * @return An instance of the service for use or NULL on failure.
+ */
+ static LLIOPipe* activate(
+ const std::string& name,
+ LLPumpIO::chain_t& chain,
+ LLSD context);
+
+ /**
+ * @brief
+ *
+ * @param name The name of the service to discard.
+ * @return true if service creator was found and discarded.
+ */
+ static bool discard(const std::string& name);
+
+protected:
+ // The creation factory static data.
+ typedef std::map<std::string, creator_t> creators_t;
+ static creators_t sCreatorFunctors;
+
+protected:
+ // construction & destruction. since this class is an abstract
+ // base class, it is up to Service implementations to actually
+ // deal with construction and not a public method. How that
+ // construction takes place will be handled by the service
+ // creators.
+ LLService();
+ virtual ~LLService();
+
+protected:
+ // This frame timer records how long this service has
+ // existed. Useful for derived services to give themselves a
+ // lifetime and expiration.
+ // *NOTE: Phoenix - This functionaity has been moved to the
+ // pump. 2005-12-13
+ //LLFrameTimer mTimer;
+
+ // Since services are designed in an 'ask now, respond later'
+ // idiom which probably crosses thread boundaries, almost all
+ // services will need a handle to a response pipe. It will usually
+ // be the job of the service author to derive a useful
+ // implementation of response, and up to the service subscriber to
+ // further derive that class to do something useful when the
+ // response comes in.
+ LLIOPipe::ptr_t mResponse;
+};
+
+
+#endif // LL_LLSERVICE_H
diff --git a/indra/llmessage/lltaskname.h b/indra/llmessage/lltaskname.h
new file mode 100644
index 0000000000..2a5a823e08
--- /dev/null
+++ b/indra/llmessage/lltaskname.h
@@ -0,0 +1,42 @@
+/**
+ * @file lltaskname.h
+ * @brief This contains the current list of valid tasks and is inluded
+ * into both simulator and viewer
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTASKNAME_H
+#define LL_LLTASKNAME_H
+
+// Current valid tasks
+// If you add a taskname here you will have to
+// 1) Add an initializer to init_object() in llscript.cpp
+// 1.1) Add to object_type_to_task_name() in llregion.cpp
+// 2) Add display code to LLStupidObject::render2(LLAgent* agentp) in llstupidobject.cpp
+// 3) Add any additional code to support new opcodes you create
+
+typedef enum e_lltask_name
+{
+ LLTASK_NULL = 0, // Not a valid task
+ LLTASK_AGENT = 1, // The player's agent in Linden World
+ LLTASK_CHILD_AGENT = 2, // Child agents sent to adjacent regions
+// LLTASK_BASIC_SHOT, // Simple shot that moves in a straight line
+// LLTASK_BIG_SHOT, // Big shot that uses gravity
+ LLTASK_TREE = 5, // A tree
+// LLTASK_BIRD, // a bird
+// LLTASK_ATOR, // a predator
+// LLTASK_SMOKE, // Smoke poof
+// LLTASK_SPARK, // Little spark
+// LLTASK_ROCK, // Rock
+ LLTASK_GRASS = 11, // Grass
+ LLTASK_PSYS = 12, // particle system test example
+// LLTASK_ORACLE,
+// LLTASK_DEMON, // Maxwell's demon
+// LLTASK_LSL_TEST, // Linden Scripting Language Test Object
+ LLTASK_PRIMITIVE = 16,
+// LLTASK_GHOST = 17, // a ghost (Boo!)
+ LLTASK_TREE_NEW = 18
+} ELLTaskName;
+#endif
diff --git a/indra/llmessage/llteleportflags.h b/indra/llmessage/llteleportflags.h
new file mode 100644
index 0000000000..001d05d109
--- /dev/null
+++ b/indra/llmessage/llteleportflags.h
@@ -0,0 +1,43 @@
+/**
+ * @file llteleportflags.h
+ * @brief Teleport flags
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTELEPORTFLAGS_H
+#define LL_LLTELEPORTFLAGS_H
+
+const U32 TELEPORT_FLAGS_DEFAULT = 0;
+const U32 TELEPORT_FLAGS_SET_HOME_TO_TARGET = 1 << 0; // newbie leaving prelude
+const U32 TELEPORT_FLAGS_SET_LAST_TO_TARGET = 1 << 1;
+const U32 TELEPORT_FLAGS_VIA_LURE = 1 << 2;
+const U32 TELEPORT_FLAGS_VIA_LANDMARK = 1 << 3;
+const U32 TELEPORT_FLAGS_VIA_LOCATION = 1 << 4;
+const U32 TELEPORT_FLAGS_VIA_HOME = 1 << 5;
+const U32 TELEPORT_FLAGS_VIA_TELEHUB = 1 << 6;
+const U32 TELEPORT_FLAGS_VIA_LOGIN = 1 << 7;
+const U32 TELEPORT_FLAGS_VIA_GODLIKE_LURE = 1 << 8;
+const U32 TELEPORT_FLAGS_GODLIKE = 1 << 9;
+const U32 TELEPORT_FLAGS_911 = 1 << 10;
+const U32 TELEPORT_FLAGS_DISABLE_CANCEL = 1 << 11; // Used for llTeleportAgentHome()
+const U32 TELEPORT_FLAGS_VIA_REGION_ID = 1 << 12;
+const U32 TELEPORT_FLAGS_IS_FLYING = 1 << 13;
+
+const U32 TELEPORT_FLAGS_MASK_VIA = TELEPORT_FLAGS_VIA_LURE
+ | TELEPORT_FLAGS_VIA_LANDMARK
+ | TELEPORT_FLAGS_VIA_LOCATION
+ | TELEPORT_FLAGS_VIA_HOME
+ | TELEPORT_FLAGS_VIA_TELEHUB
+ | TELEPORT_FLAGS_VIA_LOGIN
+ | TELEPORT_FLAGS_VIA_REGION_ID;
+
+
+
+
+const U32 LURE_FLAG_NORMAL_LURE = 1 << 0;
+const U32 LURE_FLAG_GODLIKE_LURE = 1 << 1;
+const U32 LURE_FLAG_GODLIKE_PURSUIT = 1 << 2;
+
+#endif
diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp
new file mode 100644
index 0000000000..01e83ca5cd
--- /dev/null
+++ b/indra/llmessage/llthrottle.cpp
@@ -0,0 +1,542 @@
+/**
+ * @file llthrottle.cpp
+ * @brief LLThrottle class used for network bandwidth control.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llthrottle.h"
+#include "llmath.h"
+#include "lldatapacker.h"
+#include "message.h"
+
+
+LLThrottle::LLThrottle(const F32 rate)
+{
+ mRate = rate;
+ mAvailable = 0.f;
+ mLookaheadSecs = 0.25f;
+ mLastSendTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+
+void LLThrottle::setRate(const F32 rate)
+{
+ // Need to accumulate available bits when adjusting the rate.
+ mAvailable = getAvailable();
+ mLastSendTime = LLMessageSystem::getMessageTimeSeconds();
+ mRate = rate;
+}
+
+F32 LLThrottle::getAvailable()
+{
+ // use a temporary bits_available
+ // since we don't want to change mBitsAvailable every time
+ F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
+ return mAvailable + (mRate * elapsed_time);
+}
+
+BOOL LLThrottle::checkOverflow(const F32 amount)
+{
+ BOOL retval = TRUE;
+
+ F32 lookahead_amount = mRate * mLookaheadSecs;
+
+ // use a temporary bits_available
+ // since we don't want to change mBitsAvailable every time
+ F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
+ F32 amount_available = mAvailable + (mRate * elapsed_time);
+
+ if ((amount_available >= lookahead_amount) || (amount_available > amount))
+ {
+ // ...enough space to send this message
+ // Also do if > lookahead so we can use if amount > capped amount.
+ retval = FALSE;
+ }
+
+ return retval;
+}
+
+BOOL LLThrottle::throttleOverflow(const F32 amount)
+{
+ F32 elapsed_time;
+ F32 lookahead_amount;
+ BOOL retval = TRUE;
+
+ lookahead_amount = mRate * mLookaheadSecs;
+
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ elapsed_time = (F32)(mt_sec - mLastSendTime);
+ mLastSendTime = mt_sec;
+
+ mAvailable += mRate * elapsed_time;
+
+ if (mAvailable >= lookahead_amount)
+ {
+ // ...channel completely open, so allow send regardless
+ // of size. This allows sends on very low BPS channels.
+ mAvailable = lookahead_amount;
+ retval = FALSE;
+ }
+ else if (mAvailable > amount)
+ {
+ // ...enough space to send this message
+ retval = FALSE;
+ }
+
+ // We actually already sent the bits.
+ mAvailable -= amount;
+
+ // What if bitsavailable goes negative?
+ // That's OK, because it means someone is banging on the channel,
+ // so we need some time to recover.
+
+ return retval;
+}
+
+
+
+const F32 THROTTLE_LOOKAHEAD_TIME = 1.f; // seconds
+
+// Make sure that we don't set above these
+// values, even if the client asks to be set
+// higher
+// Note that these values are replicated on the
+// client side to set max bandwidth throttling there,
+// in llviewerthrottle.cpp. These values are the sum
+// of the top two tiers of bandwidth there.
+
+F32 gThrottleMaximumBPS[TC_EOF] =
+{
+ 150000.f, // TC_RESEND
+ 170000.f, // TC_LAND
+ 34000.f, // TC_WIND
+ 34000.f, // TC_CLOUD
+ 446000.f, // TC_TASK
+ 446000.f, // TC_TEXTURE
+ 220000.f, // TC_ASSET
+};
+
+// Start low until viewer informs us of capability
+// Asset and resend get high values, since they
+// aren't used JUST by the viewer necessarily.
+// This is a HACK and should be dealt with more properly on
+// circuit creation.
+
+F32 gThrottleDefaultBPS[TC_EOF] =
+{
+ 100000.f, // TC_RESEND
+ 4000.f, // TC_LAND
+ 4000.f, // TC_WIND
+ 4000.f, // TC_CLOUD
+ 4000.f, // TC_TASK
+ 4000.f, // TC_TEXTURE
+ 100000.f, // TC_ASSET
+};
+
+// Don't throttle down lower than this
+// This potentially wastes 50 kbps, but usually
+// wont.
+F32 gThrottleMinimumBPS[TC_EOF] =
+{
+ 10000.f, // TC_RESEND
+ 10000.f, // TC_LAND
+ 4000.f, // TC_WIND
+ 4000.f, // TC_CLOUD
+ 20000.f, // TC_TASK
+ 10000.f, // TC_TEXTURE
+ 10000.f, // TC_ASSET
+};
+
+const char* THROTTLE_NAMES[TC_EOF] =
+{
+ "Resend ",
+ "Land ",
+ "Wind ",
+ "Cloud ",
+ "Task ",
+ "Texture",
+ "Asset "
+};
+
+LLThrottleGroup::LLThrottleGroup()
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mThrottleTotal[i] = gThrottleDefaultBPS[i];
+ mNominalBPS[i] = gThrottleDefaultBPS[i];
+ }
+
+ resetDynamicAdjust();
+}
+
+void LLThrottleGroup::packThrottle(LLDataPacker &dp) const
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ dp.packF32(mThrottleTotal[i], "Throttle");
+ }
+}
+
+void LLThrottleGroup::unpackThrottle(LLDataPacker &dp)
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ F32 temp_throttle;
+ dp.unpackF32(temp_throttle, "Throttle");
+ temp_throttle = llclamp(temp_throttle, 0.f, 2250000.f);
+ mThrottleTotal[i] = temp_throttle;
+ if(mThrottleTotal[i] > gThrottleMaximumBPS[i])
+ {
+ mThrottleTotal[i] = gThrottleMaximumBPS[i];
+ }
+ }
+}
+
+// Call this whenever mNominalBPS changes. Need to reset
+// the measurement systems. In the future, we should look
+// into NOT resetting the system.
+void LLThrottleGroup::resetDynamicAdjust()
+{
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mCurrentBPS[i] = mNominalBPS[i];
+ mBitsAvailable[i] = mNominalBPS[i] * THROTTLE_LOOKAHEAD_TIME;
+ mLastSendTime[i] = mt_sec;
+ mBitsSentThisPeriod[i] = 0;
+ mBitsSentHistory[i] = 0;
+ }
+ mDynamicAdjustTime = mt_sec;
+}
+
+
+BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec)
+{
+ BOOL changed = FALSE;
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mNominalBPS[i] != throttle_vec[i])
+ {
+ changed = TRUE;
+ mNominalBPS[i] = throttle_vec[i];
+ }
+ }
+
+ // If we changed the nominal settings, reset the dynamic
+ // adjustment subsystem.
+ if (changed)
+ {
+ resetDynamicAdjust();
+ }
+
+ return changed;
+}
+
+
+BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits)
+{
+ BOOL retval = TRUE;
+
+ F32 category_bps = mCurrentBPS[throttle_cat];
+ F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
+
+ // use a temporary bits_available
+ // since we don't want to change mBitsAvailable every time
+ F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]);
+ F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time);
+
+ if (bits_available >= lookahead_bits)
+ {
+ // ...channel completely open, so allow send regardless
+ // of size. This allows sends on very low BPS channels.
+ mBitsAvailable[throttle_cat] = lookahead_bits;
+ retval = FALSE;
+ }
+ else if ( bits_available > bits )
+ {
+ // ...enough space to send this message
+ retval = FALSE;
+ }
+
+ return retval;
+}
+
+BOOL LLThrottleGroup::throttleOverflow(S32 throttle_cat, F32 bits)
+{
+ F32 elapsed_time;
+ F32 category_bps;
+ F32 lookahead_bits;
+ BOOL retval = TRUE;
+
+ category_bps = mCurrentBPS[throttle_cat];
+ lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
+
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ elapsed_time = (F32)(mt_sec - mLastSendTime[throttle_cat]);
+ mLastSendTime[throttle_cat] = mt_sec;
+ mBitsAvailable[throttle_cat] += category_bps * elapsed_time;
+
+ if (mBitsAvailable[throttle_cat] >= lookahead_bits)
+ {
+ // ...channel completely open, so allow send regardless
+ // of size. This allows sends on very low BPS channels.
+ mBitsAvailable[throttle_cat] = lookahead_bits;
+ retval = FALSE;
+ }
+ else if ( mBitsAvailable[throttle_cat] > bits )
+ {
+ // ...enough space to send this message
+ retval = FALSE;
+ }
+
+ // We actually already sent the bits.
+ mBitsAvailable[throttle_cat] -= bits;
+
+ mBitsSentThisPeriod[throttle_cat] += bits;
+
+ // What if bitsavailable goes negative?
+ // That's OK, because it means someone is banging on the channel,
+ // so we need some time to recover.
+
+ return retval;
+}
+
+
+BOOL LLThrottleGroup::dynamicAdjust()
+{
+ const F32 DYNAMIC_ADJUST_TIME = 1.0f; // seconds
+ const F32 CURRENT_PERIOD_WEIGHT = .25f; // how much weight to give to last period while determining BPS utilization
+ const F32 BUSY_PERCENT = 0.75f; // if use more than this fraction of BPS, you are busy
+ const F32 IDLE_PERCENT = 0.70f; // if use less than this fraction, you are "idle"
+ const F32 TRANSFER_PERCENT = 0.90f; // how much unused bandwidth to take away each adjustment
+ const F32 RECOVER_PERCENT = 0.25f; // how much to give back during recovery phase
+
+ S32 i;
+
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+
+ // Only dynamically adjust every few seconds
+ if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME)
+ {
+ return FALSE;
+ }
+ mDynamicAdjustTime = mt_sec;
+
+ S32 total = 0;
+ // Update historical information
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mBitsSentHistory[i] == 0)
+ {
+ // first run, just copy current period
+ mBitsSentHistory[i] = mBitsSentThisPeriod[i];
+ }
+ else
+ {
+ // have some history, so weight accordingly
+ mBitsSentHistory[i] = (1.f - CURRENT_PERIOD_WEIGHT) * mBitsSentHistory[i]
+ + CURRENT_PERIOD_WEIGHT * mBitsSentThisPeriod[i];
+ }
+
+ mBitsSentThisPeriod[i] = 0;
+ total += llround(mBitsSentHistory[i]);
+ }
+
+ // Look for busy channels
+ // TODO: Fold into loop above.
+ BOOL channels_busy = FALSE;
+ F32 busy_nominal_sum = 0;
+ BOOL channel_busy[TC_EOF];
+ BOOL channel_idle[TC_EOF];
+ BOOL channel_over_nominal[TC_EOF];
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ // Is this a busy channel?
+ if (mBitsSentHistory[i] >= BUSY_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i])
+ {
+ // this channel is busy
+ channels_busy = TRUE;
+ busy_nominal_sum += mNominalBPS[i]; // use for allocation of pooled idle bandwidth
+ channel_busy[i] = TRUE;
+ }
+ else
+ {
+ channel_busy[i] = FALSE;
+ }
+
+ // Is this an idle channel?
+ if ((mBitsSentHistory[i] < IDLE_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i]) &&
+ (mBitsAvailable[i] > 0))
+ {
+ channel_idle[i] = TRUE;
+ }
+ else
+ {
+ channel_idle[i] = FALSE;
+ }
+
+ // Is this an overpumped channel?
+ if (mCurrentBPS[i] > mNominalBPS[i])
+ {
+ channel_over_nominal[i] = TRUE;
+ }
+ else
+ {
+ channel_over_nominal[i] = FALSE;
+ }
+
+ //if (total)
+ //{
+ // llinfos << i << ": B" << channel_busy[i] << " I" << channel_idle[i] << " N" << channel_over_nominal[i];
+ // llcont << " Nom: " << mNominalBPS[i] << " Cur: " << mCurrentBPS[i] << " BS: " << mBitsSentHistory[i] << llendl;
+ //}
+ }
+
+ if (channels_busy)
+ {
+ // Some channels are busy. Let's see if we can get them some bandwidth.
+ F32 used_bps;
+ F32 avail_bps;
+ F32 transfer_bps;
+
+ F32 pool_bps = 0;
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (channel_idle[i] || channel_over_nominal[i] )
+ {
+ // Either channel i is idle, or has been overpumped.
+ // Therefore it's a candidate to give up some bandwidth.
+ // Figure out how much bandwidth it has been using, and how
+ // much is available to steal.
+ used_bps = mBitsSentHistory[i] / DYNAMIC_ADJUST_TIME;
+
+ // CRO make sure to keep a minimum amount of throttle available
+ // CRO NB: channels set to < MINIMUM_BPS will never give up bps,
+ // which is correct I think
+ if (used_bps < gThrottleMinimumBPS[i])
+ {
+ used_bps = gThrottleMinimumBPS[i];
+ }
+
+ if (channel_over_nominal[i])
+ {
+ F32 unused_current = mCurrentBPS[i] - used_bps;
+ avail_bps = llmax(mCurrentBPS[i] - mNominalBPS[i], unused_current);
+ }
+ else
+ {
+ avail_bps = mCurrentBPS[i] - used_bps;
+ }
+
+ //llinfos << i << " avail " << avail_bps << llendl;
+
+ // Historically, a channel could have used more than its current share,
+ // even if it's idle right now.
+ // Make sure we don't steal too much.
+ if (avail_bps < 0)
+ {
+ continue;
+ }
+
+ // Transfer some bandwidth from this channel into the global pool.
+ transfer_bps = avail_bps * TRANSFER_PERCENT;
+ mCurrentBPS[i] -= transfer_bps;
+ pool_bps += transfer_bps;
+ }
+ }
+
+ //llinfos << "Pool BPS: " << pool_bps << llendl;
+ // Now redistribute the bandwidth to busy channels.
+ F32 unused_bps = 0.f;
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (channel_busy[i])
+ {
+ F32 add_amount = pool_bps * (mNominalBPS[i] / busy_nominal_sum);
+ //llinfos << "Busy " << i << " gets " << pool_bps << llendl;
+ mCurrentBPS[i] += add_amount;
+
+ // CRO: make sure this doesn't get too huge
+ // JC - Actually, need to let mCurrentBPS go less than nominal, otherwise
+ // you aren't allowing bandwidth to actually be moved from one channel
+ // to another.
+ // FIXME: If clamping high end, would be good to re-
+ // allocate to other channels in the above code.
+ const F32 MAX_BPS = 4 * mNominalBPS[i];
+ if (mCurrentBPS[i] > MAX_BPS)
+ {
+ F32 overage = mCurrentBPS[i] - MAX_BPS;
+ mCurrentBPS[i] -= overage;
+ unused_bps += overage;
+ }
+
+ // Paranoia
+ if (mCurrentBPS[i] < gThrottleMinimumBPS[i])
+ {
+ mCurrentBPS[i] = gThrottleMinimumBPS[i];
+ }
+ }
+ }
+
+ // For fun, add the overage back in to objects
+ if (unused_bps > 0.f)
+ {
+ mCurrentBPS[TC_TASK] += unused_bps;
+ }
+ }
+ else
+ {
+ // No one is busy.
+ // Make the channel allocations seek toward nominal.
+
+ // Look for overpumped channels
+ F32 starved_nominal_sum = 0;
+ F32 avail_bps = 0;
+ F32 transfer_bps = 0;
+ F32 pool_bps = 0;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mCurrentBPS[i] > mNominalBPS[i])
+ {
+ avail_bps = (mCurrentBPS[i] - mNominalBPS[i]);
+ transfer_bps = avail_bps * RECOVER_PERCENT;
+
+ mCurrentBPS[i] -= transfer_bps;
+ pool_bps += transfer_bps;
+ }
+ }
+
+ // Evenly distribute bandwidth to channels currently
+ // using less than nominal.
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mCurrentBPS[i] < mNominalBPS[i])
+ {
+ // We're going to weight allocations by nominal BPS.
+ starved_nominal_sum += mNominalBPS[i];
+ }
+ }
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mCurrentBPS[i] < mNominalBPS[i])
+ {
+ // Distribute bandwidth according to nominal allocation ratios.
+ mCurrentBPS[i] += pool_bps * (mNominalBPS[i] / starved_nominal_sum);
+ }
+ }
+ }
+ return TRUE;
+}
diff --git a/indra/llmessage/llthrottle.h b/indra/llmessage/llthrottle.h
new file mode 100644
index 0000000000..1ee772e687
--- /dev/null
+++ b/indra/llmessage/llthrottle.h
@@ -0,0 +1,81 @@
+/**
+ * @file llthrottle.h
+ * @brief LLThrottle class used for network bandwidth control
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTHROTTLE_H
+#define LL_LLTHROTTLE_H
+
+#include "lltimer.h"
+
+const S32 MAX_THROTTLE_SIZE = 32;
+
+class LLDataPacker;
+
+// Single instance of a generic throttle
+class LLThrottle
+{
+public:
+ LLThrottle(const F32 throttle = 1.f);
+ ~LLThrottle() { }
+
+ void setRate(const F32 rate);
+ BOOL checkOverflow(const F32 amount); // I'm about to add an amount, TRUE if would overflow throttle
+ BOOL throttleOverflow(const F32 amount); // I just sent amount, TRUE if that overflowed the throttle
+
+ F32 getAvailable(); // Return the available bits
+ F32 getRate() const { return mRate; }
+private:
+ F32 mLookaheadSecs; // Seconds to look ahead, maximum
+ F32 mRate; // BPS available, dynamically adjusted
+ F32 mAvailable; // Bits available to send right now on each channel
+ F64 mLastSendTime; // Time since last send on this channel
+};
+
+typedef enum e_throttle_categories
+{
+ TC_RESEND,
+ TC_LAND,
+ TC_WIND,
+ TC_CLOUD,
+ TC_TASK,
+ TC_TEXTURE,
+ TC_ASSET,
+ TC_EOF
+} EThrottleCats;
+
+
+class LLThrottleGroup
+{
+public:
+ LLThrottleGroup();
+ ~LLThrottleGroup() { }
+
+ void resetDynamicAdjust();
+ BOOL checkOverflow(S32 throttle_cat, F32 bits); // I'm about to send bits, TRUE if would overflow channel
+ BOOL throttleOverflow(S32 throttle_cat, F32 bits); // I just sent bits, TRUE if that overflowed the channel
+ BOOL dynamicAdjust(); // Shift bandwidth from idle channels to busy channels, TRUE if adjustment occurred
+ BOOL setNominalBPS(F32* throttle_vec); // TRUE if any value was different, resets adjustment system if was different
+
+ void packThrottle(LLDataPacker &dp) const;
+ void unpackThrottle(LLDataPacker &dp);
+public:
+ F32 mThrottleTotal[TC_EOF]; // BPS available, sent by viewer, sum for all simulators
+
+protected:
+ F32 mNominalBPS[TC_EOF]; // BPS available, adjusted to be just this simulator
+ F32 mCurrentBPS[TC_EOF]; // BPS available, dynamically adjusted
+
+ F32 mBitsAvailable[TC_EOF]; // Bits available to send right now on each channel
+ F32 mBitsSentThisPeriod[TC_EOF]; // Sent in this dynamic allocation period
+ F32 mBitsSentHistory[TC_EOF]; // Sent before this dynamic allocation period, adjusted to one period length
+
+ F64 mLastSendTime[TC_EOF]; // Time since last send on this channel
+ F64 mDynamicAdjustTime; // Only dynamic adjust every 2 seconds or so.
+
+};
+
+#endif
diff --git a/indra/llmessage/lltransfermanager.cpp b/indra/llmessage/lltransfermanager.cpp
new file mode 100644
index 0000000000..46fc386d71
--- /dev/null
+++ b/indra/llmessage/lltransfermanager.cpp
@@ -0,0 +1,1270 @@
+/**
+ * @file lltransfermanager.cpp
+ * @brief Improved transfer mechanism for moving data through the
+ * message system.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfermanager.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "lldatapacker.h"
+
+#include "lltransfersourcefile.h"
+#include "lltransfersourceasset.h"
+#include "lltransfertargetfile.h"
+#include "lltransfertargetvfile.h"
+
+const S32 MAX_PACKET_DATA_SIZE = 2048;
+const S32 MAX_PARAMS_SIZE = 1024;
+
+LLTransferManager gTransferManager;
+LLTransferSource::stype_scfunc_map LLTransferSource::sSourceCreateMap;
+
+//
+// LLTransferManager implementation
+//
+
+LLTransferManager::LLTransferManager() :
+ mValid(FALSE)
+{
+ S32 i;
+ for (i = 0; i < LLTTT_NUM_TYPES; i++)
+ {
+ mTransferBitsIn[i] = 0;
+ mTransferBitsOut[i] = 0;
+ }
+}
+
+
+LLTransferManager::~LLTransferManager()
+{
+ if (mValid)
+ {
+ llwarns << "LLTransferManager::~LLTransferManager - Should have been cleaned up by message system shutdown process" << llendl;
+ cleanup();
+ }
+}
+
+
+void LLTransferManager::init()
+{
+ if (mValid)
+ {
+ llerrs << "Double initializing LLTransferManager!" << llendl;
+ }
+ mValid = TRUE;
+
+ // Register message system handlers
+ gMessageSystem->setHandlerFunc("TransferRequest", processTransferRequest, NULL);
+ gMessageSystem->setHandlerFunc("TransferInfo", processTransferInfo, NULL);
+ gMessageSystem->setHandlerFunc("TransferPacket", processTransferPacket, NULL);
+ gMessageSystem->setHandlerFunc("TransferAbort", processTransferAbort, NULL);
+}
+
+
+void LLTransferManager::cleanup()
+{
+ mValid = FALSE;
+
+ host_tc_map::iterator iter;
+ for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
+ {
+ delete iter->second;
+ }
+ mTransferConnections.clear();
+}
+
+
+void LLTransferManager::updateTransfers()
+{
+ host_tc_map::iterator iter;
+ for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
+ {
+ iter->second->updateTransfers();
+ }
+}
+
+
+void LLTransferManager::cleanupConnection(const LLHost &host)
+{
+ host_tc_map::iterator iter;
+ iter = mTransferConnections.find(host);
+ if (iter == mTransferConnections.end())
+ {
+ // This can happen legitimately if we've never done a transfer, and we're
+ // cleaning up a circuit.
+ //llwarns << "Cleaning up nonexistent transfer connection to " << host << llendl;
+ return;
+ }
+ LLTransferConnection *connp = iter->second;
+ delete connp;
+ mTransferConnections.erase(iter);
+}
+
+
+LLTransferConnection *LLTransferManager::getTransferConnection(const LLHost &host)
+{
+ host_tc_map::iterator iter;
+ iter = mTransferConnections.find(host);
+ if (iter == mTransferConnections.end())
+ {
+ mTransferConnections[host] = new LLTransferConnection(host);
+ return mTransferConnections[host];
+ }
+
+ return iter->second;
+}
+
+
+LLTransferSourceChannel *LLTransferManager::getSourceChannel(const LLHost &host, const LLTransferChannelType type)
+{
+ LLTransferConnection *tcp = getTransferConnection(host);
+ if (!tcp)
+ {
+ return NULL;
+ }
+ return tcp->getSourceChannel(type);
+}
+
+
+
+LLTransferTargetChannel *LLTransferManager::getTargetChannel(const LLHost &host, const LLTransferChannelType type)
+{
+ LLTransferConnection *tcp = getTransferConnection(host);
+ if (!tcp)
+ {
+ return NULL;
+ }
+ return tcp->getTargetChannel(type);
+}
+
+// virtual
+LLTransferSourceParams::~LLTransferSourceParams()
+{ }
+
+
+LLTransferSource *LLTransferManager::findTransferSource(const LLUUID &transfer_id)
+{
+ // This linear traversal could screw us later if we do lots of
+ // searches for sources. However, this ONLY happens right now
+ // in asset transfer callbacks, so this should be relatively quick.
+ host_tc_map::iterator iter;
+ for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
+ {
+ LLTransferConnection *tcp = iter->second;
+ LLTransferConnection::tsc_iter sc_iter;
+ for (sc_iter = tcp->mTransferSourceChannels.begin(); sc_iter != tcp->mTransferSourceChannels.end(); sc_iter++)
+ {
+ LLTransferSourceChannel *scp = *sc_iter;
+ LLTransferSource *sourcep = scp->findTransferSource(transfer_id);
+ if (sourcep)
+ {
+ return sourcep;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//
+// Message handlers
+//
+
+//static
+void LLTransferManager::processTransferRequest(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferRequest" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferSourceType source_type;
+ LLTransferChannelType channel_type;
+ F32 priority;
+
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "SourceType", (S32 &)source_type);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+ msgp->getF32("TransferInfo", "Priority", priority);
+
+ LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
+
+ if (!tscp)
+ {
+ llwarns << "Source channel not found" << llendl;
+ return;
+ }
+
+ if (tscp->findTransferSource(transfer_id))
+ {
+ llwarns << "Duplicate request for transfer " << transfer_id << ", aborting!" << llendl;
+ return;
+ }
+
+
+ //llinfos << transfer_id << ":" << source_type << ":" << channel_type << ":" << priority << llendl;
+ LLTransferSource *tsp = LLTransferSource::createSource(source_type, transfer_id, priority);
+ if (!tsp)
+ {
+ llwarns << "LLTransferManager::processTransferRequest couldn't create transfer source!" << llendl;
+ return;
+ }
+ tscp->addTransferSource(tsp);
+
+ U8 tmp[MAX_PARAMS_SIZE];
+ S32 size = msgp->getSize("TransferInfo", "Params");
+ gMessageSystem->getBinaryData("TransferInfo", "Params", tmp, size);
+
+ LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
+ BOOL unpack_ok = tsp->unpackParams(dpb);
+ if (!unpack_ok)
+ {
+ llwarns << "Got bad parameters for a transfer request!" << llendl;
+ }
+
+ tsp->initTransfer();
+ // Don't use the status code from initTransfer for anything right now, was used before but the logic
+ // changed.
+}
+
+
+//static
+void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferInfo" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferTargetType target_type;
+ LLTransferChannelType channel_type;
+ LLTSCode status;
+ S32 size;
+
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "TargetType", (S32 &)target_type);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+ msgp->getS32("TransferInfo", "Status", (S32 &)status);
+ msgp->getS32("TransferInfo", "Size", size);
+
+ //llinfos << transfer_id << ":" << target_type<< ":" << channel_type << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
+ if (!ttcp)
+ {
+ llwarns << "Target channel not found" << llendl;
+ // Should send a message to abort the transfer.
+ return;
+ }
+
+ LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
+ if (!ttp)
+ {
+ llwarns << "TransferInfo for unknown transfer! Not able to handle this yet!" << llendl;
+ // This could happen if we're doing a push transfer, although to avoid confusion,
+ // maybe it should be a different message.
+ return;
+ }
+
+ if (status != LLTS_OK)
+ {
+ llwarns << transfer_id << ": Non-ok status, cleaning up" << llendl;
+ ttp->completionCallback(status);
+ // Clean up the transfer.
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+
+ llinfos << "Receiving " << transfer_id << ", size " << size << " bytes" << llendl;
+ ttp->setSize(size);
+ ttp->setGotInfo(TRUE);
+
+ // OK, at this point we to handle any delayed transfer packets (which could happen
+ // if this packet was lost)
+
+ // This is a lame cut and paste of code down below. If we change the logic down there,
+ // we HAVE to change the logic up here.
+
+ while (1)
+ {
+ S32 packet_id = 0;
+ U8 tmp_data[MAX_PACKET_DATA_SIZE];
+ // See if we've got any delayed packets
+ packet_id = ttp->getNextPacketID();
+ if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
+ {
+ // Perhaps this stuff should be inside a method in LLTransferPacket?
+ // I'm too lazy to do it now, though.
+ llinfos << "Playing back delayed packet " << packet_id << llendl;
+ LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
+
+ // This is somewhat inefficient, but avoids us having to duplicate
+ // code between the off-the-wire and delayed paths.
+ packet_id = packetp->mPacketID;
+ size = packetp->mSize;
+ if (size)
+ {
+ if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
+ {
+ memcpy(tmp_data, packetp->mDatap, size);
+ }
+ }
+ status = packetp->mStatus;
+ ttp->mDelayedPacketMap.erase(packet_id);
+ delete packetp;
+ }
+ else
+ {
+ // No matching delayed packet, we're done.
+ break;
+ }
+
+ LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
+ if (ret_code == LLTS_OK)
+ {
+ ttp->setLastPacketID(packet_id);
+ }
+
+ if (status != LLTS_OK)
+ {
+ if (status != LLTS_DONE)
+ {
+ llwarns << "LLTransferManager::processTransferInfo Error in playback!" << llendl;
+ }
+ else
+ {
+ llinfos << "LLTransferManager::processTransferInfo replay FINISHED for " << transfer_id << llendl;
+ }
+ // This transfer is done, either via error or not.
+ ttp->completionCallback(status);
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+ }
+}
+
+
+//static
+void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferPacket" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferChannelType channel_type;
+ S32 packet_id;
+ LLTSCode status;
+ S32 size;
+ msgp->getUUID("TransferData", "TransferID", transfer_id);
+ msgp->getS32("TransferData", "ChannelType", (S32 &)channel_type);
+ msgp->getS32("TransferData", "Packet", packet_id);
+ msgp->getS32("TransferData", "Status", (S32 &)status);
+
+ // Find the transfer associated with this packet.
+ //llinfos << transfer_id << ":" << channel_type << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
+ if (!ttcp)
+ {
+ llwarns << "Target channel not found" << llendl;
+ return;
+ }
+
+ LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
+ if (!ttp)
+ {
+ llwarns << "Didn't find matching transfer for " << transfer_id << ", aborting!" << llendl;
+ llwarns << "Packet ID: " << packet_id << llendl;
+ llwarns << "Should notify source of this!" << llendl;
+ return;
+ }
+
+ size = msgp->getSize("TransferData", "Data");
+
+ S32 msg_bytes = 0;
+ if (msgp->getReceiveCompressedSize())
+ {
+ msg_bytes = msgp->getReceiveCompressedSize();
+ }
+ else
+ {
+ msg_bytes = msgp->getReceiveSize();
+ }
+ gTransferManager.addTransferBitsIn(ttcp->mChannelType, msg_bytes*8);
+
+ if ((size < 0) || (size > MAX_PACKET_DATA_SIZE))
+ {
+ llwarns << "Invalid transfer packet size " << size << llendl;
+ return;
+ }
+
+ U8 tmp_data[MAX_PACKET_DATA_SIZE];
+ if (size > 0)
+ {
+ // Only pull the data out if the size is > 0
+ msgp->getBinaryData("TransferData", "Data", tmp_data, size);
+ }
+
+ if ((!ttp->gotInfo()) || (ttp->getNextPacketID() != packet_id))
+ {
+
+ llwarns << "Out of order packet in transfer " << transfer_id << ", got " << packet_id << " expecting " << ttp->getNextPacketID() << llendl;
+
+ // Put this on a list of packets to be delivered later.
+ ttp->addDelayedPacket(packet_id, status, tmp_data, size);
+ return;
+ }
+
+ // Loop through this until we're done with all delayed packets
+
+ //
+ // NOTE: THERE IS A CUT AND PASTE OF THIS CODE IN THE TRANSFERINFO HANDLER
+ // SO WE CAN PLAY BACK DELAYED PACKETS THERE!!!!!!!!!!!!!!!!!!!!!!!!!
+ //
+ BOOL done = FALSE;
+ while (!done)
+ {
+ LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
+ if (ret_code == LLTS_OK)
+ {
+ ttp->setLastPacketID(packet_id);
+ }
+
+ if (status != LLTS_OK)
+ {
+ if (status != LLTS_DONE)
+ {
+ llwarns << "LLTransferManager::processTransferPacket Error in transfer!" << llendl;
+ }
+ else
+ {
+// llinfos << "LLTransferManager::processTransferPacket done for " << transfer_id << llendl;
+ }
+ // This transfer is done, either via error or not.
+ ttp->completionCallback(status);
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+
+ // See if we've got any delayed packets
+ packet_id = ttp->getNextPacketID();
+ if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
+ {
+ // Perhaps this stuff should be inside a method in LLTransferPacket?
+ // I'm too lazy to do it now, though.
+ llinfos << "Playing back delayed packet " << packet_id << llendl;
+ LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
+
+ // This is somewhat inefficient, but avoids us having to duplicate
+ // code between the off-the-wire and delayed paths.
+ packet_id = packetp->mPacketID;
+ size = packetp->mSize;
+ if (size)
+ {
+ if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
+ {
+ memcpy(tmp_data, packetp->mDatap, size);
+ }
+ }
+ status = packetp->mStatus;
+ ttp->mDelayedPacketMap.erase(packet_id);
+ delete packetp;
+ }
+ else
+ {
+ // No matching delayed packet, abort it.
+ done = TRUE;
+ }
+ }
+}
+
+
+//static
+void LLTransferManager::processTransferAbort(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferPacket" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferChannelType channel_type;
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+
+
+ // See if it's a target that we're trying to abort
+ // Find the transfer associated with this packet.
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
+ if (ttcp)
+ {
+ LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
+ if (ttp)
+ {
+ ttp->abortTransfer();
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+ }
+
+ // Hmm, not a target. Maybe it's a source.
+ LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
+ if (tscp)
+ {
+ LLTransferSource *tsp = tscp->findTransferSource(transfer_id);
+ if (tsp)
+ {
+ tsp->abortTransfer();
+ tscp->deleteTransfer(tsp);
+ return;
+ }
+ }
+
+ llwarns << "Couldn't find transfer " << transfer_id << " to abort!" << llendl;
+}
+
+
+//static
+void LLTransferManager::processTransferPriority(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferPacket" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferChannelType channel_type;
+ F32 priority = 0.f;
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+ msgp->getF32("TransferInfo", "Priority", priority);
+
+ // Hmm, not a target. Maybe it's a source.
+ LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
+ if (tscp)
+ {
+ LLTransferSource *tsp = tscp->findTransferSource(transfer_id);
+ if (tsp)
+ {
+ tscp->updatePriority(tsp, priority);
+ return;
+ }
+ }
+
+ llwarns << "Couldn't find transfer " << transfer_id << " to set priority!" << llendl;
+}
+
+
+//static
+void LLTransferManager::reliablePacketCallback(void **user_data, S32 result)
+{
+ LLUUID *transfer_idp = (LLUUID *)user_data;
+ if (result)
+ {
+ llwarns << "Aborting reliable transfer " << *transfer_idp << " due to failed reliable resends!" << llendl;
+ LLTransferSource *tsp = gTransferManager.findTransferSource(*transfer_idp);
+ if (tsp)
+ {
+ LLTransferSourceChannel *tscp = tsp->mChannelp;
+ tsp->abortTransfer();
+ tscp->deleteTransfer(tsp);
+ }
+ }
+ delete transfer_idp;
+}
+
+//
+// LLTransferConnection implementation
+//
+
+LLTransferConnection::LLTransferConnection(const LLHost &host)
+{
+ mHost = host;
+}
+
+LLTransferConnection::~LLTransferConnection()
+{
+ tsc_iter itersc;
+ for (itersc = mTransferSourceChannels.begin(); itersc != mTransferSourceChannels.end(); itersc++)
+ {
+ delete *itersc;
+ }
+ mTransferSourceChannels.clear();
+
+ ttc_iter itertc;
+ for (itertc = mTransferTargetChannels.begin(); itertc != mTransferTargetChannels.end(); itertc++)
+ {
+ delete *itertc;
+ }
+ mTransferTargetChannels.clear();
+}
+
+
+void LLTransferConnection::updateTransfers()
+{
+ // Do stuff for source transfers (basically, send data out).
+ tsc_iter iter;
+ for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++)
+ {
+ (*iter)->updateTransfers();
+ }
+
+ // Do stuff for target transfers
+ // Primarily, we should be aborting transfers that are irredeemably broken
+ // (large packet gaps that don't appear to be getting filled in, most likely)
+ // Probably should NOT be doing timeouts for other things, as new priority scheme
+ // means that a high priority transfer COULD block a transfer for a long time.
+}
+
+
+LLTransferSourceChannel *LLTransferConnection::getSourceChannel(const LLTransferChannelType channel_type)
+{
+ tsc_iter iter;
+ for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++)
+ {
+ if ((*iter)->getChannelType() == channel_type)
+ {
+ return *iter;
+ }
+ }
+
+ LLTransferSourceChannel *tscp = new LLTransferSourceChannel(channel_type, mHost);
+ mTransferSourceChannels.push_back(tscp);
+ return tscp;
+}
+
+
+LLTransferTargetChannel *LLTransferConnection::getTargetChannel(const LLTransferChannelType channel_type)
+{
+ ttc_iter iter;
+ for (iter = mTransferTargetChannels.begin(); iter != mTransferTargetChannels.end(); iter++)
+ {
+ if ((*iter)->getChannelType() == channel_type)
+ {
+ return *iter;
+ }
+ }
+
+ LLTransferTargetChannel *ttcp = new LLTransferTargetChannel(channel_type, mHost);
+ mTransferTargetChannels.push_back(ttcp);
+ return ttcp;
+}
+
+
+//
+// LLTransferSourceChannel implementation
+//
+
+const S32 DEFAULT_PACKET_SIZE = 1000;
+
+
+LLTransferSourceChannel::LLTransferSourceChannel(const LLTransferChannelType channel_type, const LLHost &host) :
+ mChannelType(channel_type),
+ mHost(host),
+ mTransferSources(LLTransferSource::sSetPriority, LLTransferSource::sGetPriority),
+ mThrottleID(TC_ASSET)
+{
+}
+
+
+LLTransferSourceChannel::~LLTransferSourceChannel()
+{
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+ for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
+ {
+ // Just kill off all of the transfers
+ (*iter).second->abortTransfer();
+ delete iter->second;
+ }
+ mTransferSources.mMap.clear();
+}
+
+void LLTransferSourceChannel::updatePriority(LLTransferSource *tsp, const F32 priority)
+{
+ mTransferSources.reprioritize(priority, tsp);
+}
+
+void LLTransferSourceChannel::updateTransfers()
+{
+ // Actually, this should do the following:
+ // Decide if we can actually send data.
+ // If so, update priorities so we know who gets to send it.
+ // Send data from the sources, while updating until we've sent our throttle allocation.
+
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(getHost());
+ if (!cdp)
+ {
+ return;
+ }
+
+ if (cdp->isBlocked())
+ {
+ // FIXME We need to make sure that the throttle bits available gets reset.
+
+ // We DON'T want to send any packets if they're blocked, they'll just end up
+ // piling up on the other end.
+ //llwarns << "Blocking transfers due to blocked circuit for " << getHost() << llendl;
+ return;
+ }
+
+ const S32 throttle_id = mThrottleID;
+
+ LLThrottleGroup &tg = cdp->getThrottleGroup();
+
+ if (tg.checkOverflow(throttle_id, 0.f))
+ {
+ return;
+ }
+
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+
+
+ BOOL done = FALSE;
+ for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;)
+ {
+ //llinfos << "LLTransferSourceChannel::updateTransfers()" << llendl;
+ // Do stuff.
+ LLTransferSource *tsp = iter->second;
+ U8 *datap = NULL;
+ S32 data_size = 0;
+ BOOL delete_data = FALSE;
+ S32 packet_id = 0;
+ S32 sent_bytes = 0;
+ LLTSCode status = LLTS_OK;
+
+ // Get the packetID for the next packet that we're transferring.
+ packet_id = tsp->getNextPacketID();
+ status = tsp->dataCallback(packet_id, DEFAULT_PACKET_SIZE, &datap, data_size, delete_data);
+
+ if (status == LLTS_SKIP)
+ {
+ // We don't have any data, but we're not done, just go on.
+ // This will presumably be used for streaming or async transfers that
+ // are stalled waiting for data from another source.
+ iter++;
+ continue;
+ }
+
+ LLUUID *cb_uuid = new LLUUID(tsp->getID());
+
+ // Send the data now, even if it's an error.
+ // The status code will tell the other end what to do.
+ gMessageSystem->newMessage("TransferPacket");
+ gMessageSystem->nextBlock("TransferData");
+ gMessageSystem->addUUID("TransferID", tsp->getID());
+ gMessageSystem->addS32("ChannelType", getChannelType());
+ gMessageSystem->addS32("Packet", packet_id); // HACK! Need to put in a REAL packet id
+ gMessageSystem->addS32("Status", status);
+ gMessageSystem->addBinaryData("Data", datap, data_size);
+ sent_bytes = gMessageSystem->getCurrentSendTotal();
+ gMessageSystem->sendReliable(getHost(), LL_DEFAULT_RELIABLE_RETRIES, TRUE, 0.f,
+ LLTransferManager::reliablePacketCallback, (void**)cb_uuid);
+
+ // Do bookkeeping for the throttle
+ done = tg.throttleOverflow(throttle_id, sent_bytes*8.f);
+ gTransferManager.addTransferBitsOut(mChannelType, sent_bytes*8);
+
+ // Clean up our temporary data.
+ if (delete_data)
+ {
+ delete[] datap;
+ datap = NULL;
+ }
+
+ // Update the packet counter
+ tsp->setLastPacketID(packet_id);
+
+ switch (status)
+ {
+ case LLTS_OK:
+ // We're OK, don't need to do anything. Keep sending data.
+ break;
+ case LLTS_ERROR:
+ llwarns << "Error in transfer dataCallback!" << llendl;
+ case LLTS_DONE:
+ // We need to clean up this transfer source.
+ //llinfos << "LLTransferSourceChannel::updateTransfers() " << tsp->getID() << " done" << llendl;
+ tsp->completionCallback(status);
+ delete tsp;
+
+ mTransferSources.mMap.erase(iter++);
+ break;
+ default:
+ llerrs << "Unknown transfer error code!" << llendl;
+ }
+
+ // At this point, we should do priority adjustment (since some transfers like
+ // streaming transfers will adjust priority based on how much they've sent and time,
+ // but I'm not going to bother yet. - djs.
+ }
+}
+
+
+void LLTransferSourceChannel::addTransferSource(LLTransferSource *sourcep)
+{
+ sourcep->mChannelp = this;
+ mTransferSources.push(sourcep->getPriority(), sourcep);
+}
+
+
+LLTransferSource *LLTransferSourceChannel::findTransferSource(const LLUUID &transfer_id)
+{
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+ for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
+ {
+ LLTransferSource *tsp = iter->second;
+ if (tsp->getID() == transfer_id)
+ {
+ return tsp;
+ }
+ }
+ return NULL;
+}
+
+
+BOOL LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp)
+{
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+ for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
+ {
+ if (iter->second == tsp)
+ {
+ break;
+ }
+ }
+
+ if (iter == mTransferSources.mMap.end())
+ {
+ llerrs << "Unable to find transfer source to delete!" << llendl;
+ return FALSE;
+ }
+ mTransferSources.mMap.erase(iter);
+ delete tsp;
+ return TRUE;
+}
+
+
+//
+// LLTransferTargetChannel implementation
+//
+
+LLTransferTargetChannel::LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host) :
+ mChannelType(channel_type),
+ mHost(host)
+{
+}
+
+LLTransferTargetChannel::~LLTransferTargetChannel()
+{
+ tt_iter iter;
+ for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
+ {
+ // Abort all of the current transfers
+ (*iter)->abortTransfer();
+ delete *iter;
+ }
+ mTransferTargets.clear();
+}
+
+
+void LLTransferTargetChannel::requestTransfer(const LLTransferSourceParams &source_params,
+ const LLTransferTargetParams &target_params,
+ const F32 priority)
+{
+ LLUUID id;
+ id.generate();
+ LLTransferTarget *ttp = LLTransferTarget::createTarget(target_params.getType(), id);
+ if (!ttp)
+ {
+ llwarns << "LLTransferManager::requestTransfer aborting due to target creation failure!" << llendl;
+ }
+
+ ttp->applyParams(target_params);
+ addTransferTarget(ttp);
+
+ sendTransferRequest(ttp, source_params, priority);
+}
+
+
+void LLTransferTargetChannel::sendTransferRequest(LLTransferTarget *targetp,
+ const LLTransferSourceParams &params,
+ const F32 priority)
+{
+ //
+ // Pack the message with data which explains how to get the source, and
+ // send it off to the source for this channel.
+ //
+ llassert(targetp);
+ llassert(targetp->getChannel() == this);
+
+ gMessageSystem->newMessage("TransferRequest");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", targetp->getID());
+ gMessageSystem->addS32("SourceType", params.getType());
+ gMessageSystem->addS32("ChannelType", getChannelType());
+ gMessageSystem->addF32("Priority", priority);
+
+ U8 tmp[MAX_PARAMS_SIZE];
+ LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
+ params.packParams(dp);
+ S32 len = dp.getCurrentSize();
+ gMessageSystem->addBinaryData("Params", tmp, len);
+
+ gMessageSystem->sendReliable(mHost);
+}
+
+
+void LLTransferTargetChannel::addTransferTarget(LLTransferTarget *targetp)
+{
+ targetp->mChannelp = this;
+ mTransferTargets.push_back(targetp);
+}
+
+
+LLTransferTarget *LLTransferTargetChannel::findTransferTarget(const LLUUID &transfer_id)
+{
+ tt_iter iter;
+ for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
+ {
+ LLTransferTarget *ttp = *iter;
+ if (ttp->getID() == transfer_id)
+ {
+ return ttp;
+ }
+ }
+ return NULL;
+}
+
+
+BOOL LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp)
+{
+ tt_iter iter;
+ for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
+ {
+ if (*iter == ttp)
+ {
+ break;
+ }
+ }
+
+ if (iter == mTransferTargets.end())
+ {
+ llerrs << "Unable to find transfer target to delete!" << llendl;
+ return FALSE;
+ }
+ mTransferTargets.erase(iter);
+ delete ttp;
+ return TRUE;
+}
+
+
+//
+// LLTransferSource implementation
+//
+
+LLTransferSource::LLTransferSource(const LLTransferSourceType type,
+ const LLUUID &transfer_id,
+ const F32 priority) :
+ mType(type),
+ mID(transfer_id),
+ mChannelp(NULL),
+ mPriority(priority),
+ mSize(0),
+ mLastPacketID(-1)
+{
+ setPriority(priority);
+}
+
+
+LLTransferSource::~LLTransferSource()
+{
+ // No actual cleanup of the transfer is done here, this is purely for
+ // memory cleanup. The completionCallback is guaranteed to get called
+ // before this happens.
+}
+
+
+void LLTransferSource::sendTransferStatus(LLTSCode status)
+{
+ gMessageSystem->newMessage("TransferInfo");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", getID());
+ gMessageSystem->addS32("TargetType", LLTTT_UNKNOWN);
+ gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
+ gMessageSystem->addS32("Status", status);
+ gMessageSystem->addS32("Size", mSize);
+ gMessageSystem->sendReliable(mChannelp->getHost());
+
+ // Abort if there was as asset system issue.
+ if (status != LLTS_OK)
+ {
+ completionCallback(status);
+ mChannelp->deleteTransfer(this);
+ }
+}
+
+
+// This should never be called directly, the transfer manager is responsible for
+// aborting the transfer from the channel. I might want to rethink this in the
+// future, though.
+void LLTransferSource::abortTransfer()
+{
+ // Send a message down, call the completion callback
+ llinfos << "Aborting transfer " << getID() << " to " << mChannelp->getHost() << llendl;
+ gMessageSystem->newMessage("TransferAbort");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", getID());
+ gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
+ gMessageSystem->sendReliable(mChannelp->getHost());
+
+ completionCallback(LLTS_ABORT);
+}
+
+
+//static
+void LLTransferSource::registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc func)
+{
+ if (sSourceCreateMap.count(stype))
+ {
+ // Disallow changing what class handles a source type
+ // Unclear when you would want to do this, and whether it would work.
+ llerrs << "Reregistering source type " << stype << llendl;
+ }
+ else
+ {
+ sSourceCreateMap[stype] = func;
+ }
+}
+
+//static
+LLTransferSource *LLTransferSource::createSource(const LLTransferSourceType stype,
+ const LLUUID &id,
+ const F32 priority)
+{
+ switch (stype)
+ {
+ // *NOTE: The source file transfer mechanism is highly insecure and could
+ // lead to easy exploitation of a server process.
+ // I have removed all uses of it from the codebase. Phoenix.
+ //
+ //case LLTST_FILE:
+ // return new LLTransferSourceFile(id, priority);
+ case LLTST_ASSET:
+ return new LLTransferSourceAsset(id, priority);
+ default:
+ {
+ if (!sSourceCreateMap.count(stype))
+ {
+ // Use the callback to create the source type if it's not there.
+ llwarns << "Unknown transfer source type: " << stype << llendl;
+ return NULL;
+ }
+ return (sSourceCreateMap[stype])(id, priority);
+ }
+ }
+}
+
+
+// static
+void LLTransferSource::sSetPriority(LLTransferSource *&tsp, const F32 priority)
+{
+ tsp->setPriority(priority);
+}
+
+
+// static
+F32 LLTransferSource::sGetPriority(LLTransferSource *&tsp)
+{
+ return tsp->getPriority();
+}
+
+
+//
+// LLTransferPacket implementation
+//
+
+LLTransferPacket::LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size) :
+ mPacketID(packet_id),
+ mStatus(status),
+ mDatap(NULL),
+ mSize(size)
+{
+ if (size == 0)
+ {
+ return;
+ }
+
+ mDatap = new U8[size];
+ if (mDatap != NULL)
+ {
+ memcpy(mDatap, datap, size);
+ }
+}
+
+LLTransferPacket::~LLTransferPacket()
+{
+ delete[] mDatap;
+}
+
+//
+// LLTransferTarget implementation
+//
+
+LLTransferTarget::LLTransferTarget(LLTransferTargetType type, const LLUUID &id) :
+ mType(type),
+ mID(id),
+ mGotInfo(FALSE),
+ mSize(0),
+ mLastPacketID(-1)
+{
+}
+
+LLTransferTarget::~LLTransferTarget()
+{
+ // No actual cleanup of the transfer is done here, this is purely for
+ // memory cleanup. The completionCallback is guaranteed to get called
+ // before this happens.
+ tpm_iter iter;
+ for (iter = mDelayedPacketMap.begin(); iter != mDelayedPacketMap.end(); iter++)
+ {
+ delete iter->second;
+ }
+ mDelayedPacketMap.clear();
+}
+
+// This should never be called directly, the transfer manager is responsible for
+// aborting the transfer from the channel. I might want to rethink this in the
+// future, though.
+void LLTransferTarget::abortTransfer()
+{
+ // Send a message up, call the completion callback
+ llinfos << "Aborting transfer " << getID() << " from " << mChannelp->getHost() << llendl;
+ gMessageSystem->newMessage("TransferAbort");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", getID());
+ gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
+ gMessageSystem->sendReliable(mChannelp->getHost());
+
+ completionCallback(LLTS_ABORT);
+}
+
+void LLTransferTarget::addDelayedPacket(const S32 packet_id, const LLTSCode status, U8 *datap, const S32 size)
+{
+ LLTransferPacket *tpp = new LLTransferPacket(packet_id, status, datap, size);
+#ifdef _DEBUG
+ if (mDelayedPacketMap.find(packet_id) != mDelayedPacketMap.end())
+ {
+ llerrs << "Packet ALREADY in delayed packet map!" << llendl;
+ }
+#endif
+ mDelayedPacketMap[packet_id] = tpp;
+}
+
+
+LLTransferTarget *LLTransferTarget::createTarget(const LLTransferTargetType type, const LLUUID &id)
+{
+ switch (type)
+ {
+ case LLTTT_FILE:
+ return new LLTransferTargetFile(id);
+ case LLTTT_VFILE:
+ return new LLTransferTargetVFile(id);
+ default:
+ llwarns << "Unknown transfer target type: " << type << llendl;
+ return NULL;
+ }
+}
+
+
+LLTransferSourceParamsInvItem::LLTransferSourceParamsInvItem() : LLTransferSourceParams(LLTST_SIM_INV_ITEM), mAssetType(LLAssetType::AT_NONE)
+{
+}
+
+
+void LLTransferSourceParamsInvItem::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
+{
+ mAgentID = agent_id;
+ mSessionID = session_id;
+}
+
+
+void LLTransferSourceParamsInvItem::setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id)
+{
+ mOwnerID = owner_id;
+ mTaskID = task_id;
+ mItemID = item_id;
+}
+
+
+void LLTransferSourceParamsInvItem::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+
+void LLTransferSourceParamsInvItem::packParams(LLDataPacker &dp) const
+{
+ dp.packUUID(mAgentID, "AgentID");
+ dp.packUUID(mSessionID, "SessionID");
+ dp.packUUID(mOwnerID, "OwnerID");
+ dp.packUUID(mTaskID, "TaskID");
+ dp.packUUID(mItemID, "ItemID");
+ dp.packUUID(mAssetID, "AssetID");
+ dp.packS32(mAssetType, "AssetType");
+}
+
+
+BOOL LLTransferSourceParamsInvItem::unpackParams(LLDataPacker &dp)
+{
+ S32 tmp_at;
+
+ dp.unpackUUID(mAgentID, "AgentID");
+ dp.unpackUUID(mSessionID, "SessionID");
+ dp.unpackUUID(mOwnerID, "OwnerID");
+ dp.unpackUUID(mTaskID, "TaskID");
+ dp.unpackUUID(mItemID, "ItemID");
+ dp.unpackUUID(mAssetID, "AssetID");
+ dp.unpackS32(tmp_at, "AssetType");
+
+ mAssetType = (LLAssetType::EType)tmp_at;
+
+ return TRUE;
+}
+
+LLTransferSourceParamsEstate::LLTransferSourceParamsEstate() :
+ LLTransferSourceParams(LLTST_SIM_ESTATE), mEstateAssetType(ET_NONE)
+{
+}
+
+void LLTransferSourceParamsEstate::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
+{
+ mAgentID = agent_id;
+ mSessionID = session_id;
+}
+
+void LLTransferSourceParamsEstate::setEstateAssetType(const EstateAssetType etype)
+{
+ mEstateAssetType = etype;
+}
+
+void LLTransferSourceParamsEstate::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+void LLTransferSourceParamsEstate::packParams(LLDataPacker &dp) const
+{
+ dp.packUUID(mAgentID, "AgentID");
+ dp.packUUID(mSessionID, "SessionID");
+ dp.packS32(mEstateAssetType, "EstateAssetType");
+}
+
+
+BOOL LLTransferSourceParamsEstate::unpackParams(LLDataPacker &dp)
+{
+ S32 tmp_et;
+
+ dp.unpackUUID(mAgentID, "AgentID");
+ dp.unpackUUID(mSessionID, "SessionID");
+ dp.unpackS32(tmp_et, "EstateAssetType");
+
+ mEstateAssetType = (EstateAssetType)tmp_et;
+
+ return TRUE;
+}
diff --git a/indra/llmessage/lltransfermanager.h b/indra/llmessage/lltransfermanager.h
new file mode 100644
index 0000000000..8c4c9f8ba7
--- /dev/null
+++ b/indra/llmessage/lltransfermanager.h
@@ -0,0 +1,466 @@
+/**
+ * @file lltransfermanager.h
+ * @brief Improved transfer mechanism for moving data through the
+ * message system.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERMANAGER_H
+#define LL_LLTRANSFERMANAGER_H
+
+#include <map>
+#include <list>
+
+#include "llhost.h"
+#include "lluuid.h"
+#include "llthrottle.h"
+#include "llpriqueuemap.h"
+#include "llassettype.h"
+
+//
+// Definition of the manager class for the new LLXfer replacement.
+// Provides prioritized, bandwidth-throttled transport of arbitrary
+// binary data between host/circuit combos
+//
+
+
+typedef enum e_transfer_channel_type
+{
+ LLTCT_UNKNOWN = 0,
+ LLTCT_MISC,
+ LLTCT_ASSET,
+ LLTCT_NUM_TYPES
+} LLTransferChannelType;
+
+
+typedef enum e_transfer_source_type
+{
+ LLTST_UNKNOWN = 0,
+ LLTST_FILE,
+ LLTST_ASSET,
+ LLTST_SIM_INV_ITEM, // Simulator specific, may not be handled
+ LLTST_SIM_ESTATE, // Simulator specific, may not be handled
+ LLTST_NUM_TYPES
+} LLTransferSourceType;
+
+
+typedef enum e_transfer_target_type
+{
+ LLTTT_UNKNOWN = 0,
+ LLTTT_FILE,
+ LLTTT_VFILE,
+ LLTTT_NUM_TYPES
+} LLTransferTargetType;
+
+
+// Errors are negative, expected values are positive.
+typedef enum e_status_codes
+{
+ LLTS_OK = 0,
+ LLTS_DONE = 1,
+ LLTS_SKIP = 2,
+ LLTS_ABORT = 3,
+ LLTS_ERROR = -1,
+ LLTS_UNKNOWN_SOURCE = -2, // Equivalent of a 404
+ LLTS_INSUFFICIENT_PERMISSIONS = -3 // Not enough permissions
+} LLTSCode;
+
+// Types of requests for estate wide information
+typedef enum e_estate_type
+{
+ ET_Covenant = 0,
+ ET_NONE = -1
+} EstateAssetType;
+
+class LLMessageSystem;
+class LLDataPacker;
+
+class LLTransferConnection;
+class LLTransferSourceChannel;
+class LLTransferTargetChannel;
+class LLTransferSourceParams;
+class LLTransferTargetParams;
+class LLTransferSource;
+class LLTransferTarget;
+
+class LLTransferManager
+{
+public:
+ LLTransferManager();
+ virtual ~LLTransferManager();
+
+ void init();
+ void cleanup();
+
+ void updateTransfers(); // Called per frame to push packets out on the various different channels.
+ void cleanupConnection(const LLHost &host);
+
+
+ LLTransferSourceChannel *getSourceChannel(const LLHost &host, const LLTransferChannelType stype);
+ LLTransferTargetChannel *getTargetChannel(const LLHost &host, const LLTransferChannelType stype);
+
+ LLTransferSource *findTransferSource(const LLUUID &transfer_id);
+
+ BOOL isValid() const { return mValid; }
+
+ static void processTransferRequest(LLMessageSystem *mesgsys, void **);
+ static void processTransferInfo(LLMessageSystem *mesgsys, void **);
+ static void processTransferPacket(LLMessageSystem *mesgsys, void **);
+ static void processTransferAbort(LLMessageSystem *mesgsys, void **);
+ static void processTransferPriority(LLMessageSystem *mesgsys, void **);
+
+ static void reliablePacketCallback(void **, S32 result);
+
+ S32 getTransferBitsIn(const LLTransferChannelType tctype) const { return mTransferBitsIn[tctype]; }
+ S32 getTransferBitsOut(const LLTransferChannelType tctype) const { return mTransferBitsOut[tctype]; }
+ void resetTransferBitsIn(const LLTransferChannelType tctype) { mTransferBitsIn[tctype] = 0; }
+ void resetTransferBitsOut(const LLTransferChannelType tctype) { mTransferBitsOut[tctype] = 0; }
+ void addTransferBitsIn(const LLTransferChannelType tctype, const S32 bits) { mTransferBitsIn[tctype] += bits; }
+ void addTransferBitsOut(const LLTransferChannelType tctype, const S32 bits) { mTransferBitsOut[tctype] += bits; }
+protected:
+ LLTransferConnection *getTransferConnection(const LLHost &host);
+ BOOL removeTransferConnection(const LLHost &host);
+
+protected:
+ // Convenient typedefs
+ typedef std::map<LLHost, LLTransferConnection *> host_tc_map;
+
+ BOOL mValid;
+ LLHost mHost;
+
+ S32 mTransferBitsIn[LLTTT_NUM_TYPES];
+ S32 mTransferBitsOut[LLTTT_NUM_TYPES];
+
+ // We keep a map between each host and LLTransferConnection.
+ host_tc_map mTransferConnections;
+};
+
+
+//
+// Keeps tracks of all channels to/from a particular host.
+//
+class LLTransferConnection
+{
+public:
+ LLTransferConnection(const LLHost &host);
+ virtual ~LLTransferConnection();
+
+ void updateTransfers();
+
+ LLTransferSourceChannel *getSourceChannel(const LLTransferChannelType type);
+ LLTransferTargetChannel *getTargetChannel(const LLTransferChannelType type);
+
+ // Convenient typedefs
+ typedef std::list<LLTransferSourceChannel *>::iterator tsc_iter;
+ typedef std::list<LLTransferTargetChannel *>::iterator ttc_iter;
+ friend class LLTransferManager;
+protected:
+
+ LLHost mHost;
+ std::list<LLTransferSourceChannel *> mTransferSourceChannels;
+ std::list<LLTransferTargetChannel *> mTransferTargetChannels;
+
+};
+
+
+//
+// A channel which is pushing data out.
+//
+
+class LLTransferSourceChannel
+{
+public:
+ LLTransferSourceChannel(const LLTransferChannelType channel_type,
+ const LLHost &host);
+ virtual ~LLTransferSourceChannel();
+
+ void updateTransfers();
+
+ void updatePriority(LLTransferSource *tsp, const F32 priority);
+
+ void addTransferSource(LLTransferSource *sourcep);
+ LLTransferSource *findTransferSource(const LLUUID &transfer_id);
+ BOOL deleteTransfer(LLTransferSource *tsp);
+
+ void setThrottleID(const S32 throttle_id) { mThrottleID = throttle_id; }
+
+ LLTransferChannelType getChannelType() const { return mChannelType; }
+ LLHost getHost() const { return mHost; }
+
+protected:
+ typedef std::list<LLTransferSource *>::iterator ts_iter;
+
+ LLTransferChannelType mChannelType;
+ LLHost mHost;
+ LLPriQueueMap<LLTransferSource*> mTransferSources;
+
+ // The throttle that this source channel should use
+ S32 mThrottleID;
+};
+
+
+//
+// A channel receiving data from a source.
+//
+class LLTransferTargetChannel
+{
+public:
+ LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host);
+ virtual ~LLTransferTargetChannel();
+
+ void requestTransfer(const LLTransferSourceParams &source_params,
+ const LLTransferTargetParams &target_params,
+ const F32 priority);
+
+ LLTransferTarget *findTransferTarget(const LLUUID &transfer_id);
+ BOOL deleteTransfer(LLTransferTarget *ttp);
+
+
+ LLTransferChannelType getChannelType() const { return mChannelType; }
+ LLHost getHost() const { return mHost; }
+
+protected:
+ void sendTransferRequest(LLTransferTarget *targetp,
+ const LLTransferSourceParams &params,
+ const F32 priority);
+
+ void addTransferTarget(LLTransferTarget *targetp);
+
+ friend class LLTransferTarget;
+ friend class LLTransferManager;
+protected:
+ typedef std::list<LLTransferTarget *>::iterator tt_iter;
+
+ LLTransferChannelType mChannelType;
+ LLHost mHost;
+ std::list<LLTransferTarget *> mTransferTargets;
+};
+
+
+class LLTransferSourceParams
+{
+public:
+ LLTransferSourceParams(const LLTransferSourceType type) : mType(type) { }
+ virtual ~LLTransferSourceParams();
+
+ virtual void packParams(LLDataPacker &dp) const = 0;
+ virtual BOOL unpackParams(LLDataPacker &dp) = 0;
+
+ LLTransferSourceType getType() const { return mType; }
+
+protected:
+ LLTransferSourceType mType;
+};
+
+
+//
+// LLTransferSource is an interface, all transfer sources should be derived from it.
+//
+typedef LLTransferSource *(*LLTransferSourceCreateFunc)(const LLUUID &id, const F32 priority);
+
+class LLTransferSource
+{
+public:
+
+ LLUUID getID() { return mID; }
+
+ friend class LLTransferManager;
+ friend class LLTransferSourceChannel;
+
+protected:
+ LLTransferSource(const LLTransferSourceType source_type,
+ const LLUUID &request_id,
+ const F32 priority);
+ virtual ~LLTransferSource();
+
+ void sendTransferStatus(LLTSCode status); // When you've figured out your transfer status, do this
+
+ virtual void initTransfer() = 0;
+ virtual F32 updatePriority() = 0;
+ virtual LLTSCode dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **datap,
+ S32 &returned_bytes,
+ BOOL &delete_returned) = 0;
+
+ // The completionCallback is GUARANTEED to be called before the destructor.
+ virtual void completionCallback(const LLTSCode status) = 0;
+
+ virtual BOOL unpackParams(LLDataPacker &dp) = 0;
+
+ virtual S32 getNextPacketID() { return mLastPacketID + 1; }
+ virtual void setLastPacketID(const S32 packet_id) { mLastPacketID = packet_id; }
+
+
+ // For now, no self-induced priority changes
+ F32 getPriority() { return mPriority; }
+ void setPriority(const F32 pri) { mPriority = pri; }
+
+ virtual void abortTransfer(); // DON'T USE THIS ONE, used internally by LLTransferManager
+
+ static LLTransferSource *createSource(const LLTransferSourceType stype,
+ const LLUUID &request_id,
+ const F32 priority);
+ static void registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc);
+
+ static void sSetPriority(LLTransferSource *&tsp, const F32 priority);
+ static F32 sGetPriority(LLTransferSource *&tsp);
+protected:
+ typedef std::map<LLTransferSourceType, LLTransferSourceCreateFunc> stype_scfunc_map;
+ static stype_scfunc_map sSourceCreateMap;
+
+ LLTransferSourceType mType;
+ LLUUID mID;
+ LLTransferSourceChannel *mChannelp;
+ F32 mPriority;
+ S32 mSize;
+ S32 mLastPacketID;
+};
+
+
+class LLTransferTargetParams
+{
+public:
+ LLTransferTargetParams(const LLTransferTargetType type) : mType(type) {}
+ LLTransferTargetType getType() const { return mType; }
+protected:
+ LLTransferTargetType mType;
+};
+
+
+class LLTransferPacket
+{
+ // Used for storing a packet that's being delivered later because it's out of order.
+ // ONLY should be accessed by the following two classes, for now.
+ friend class LLTransferTarget;
+ friend class LLTransferManager;
+
+protected:
+
+ LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size);
+ virtual ~LLTransferPacket();
+
+protected:
+ S32 mPacketID;
+ LLTSCode mStatus;
+ U8 *mDatap;
+ S32 mSize;
+};
+
+
+class LLTransferTarget
+{
+public:
+ LLTransferTarget(LLTransferTargetType target_type, const LLUUID &transfer_id);
+ virtual ~LLTransferTarget();
+
+
+ // Accessors
+ LLUUID getID() const { return mID; }
+ LLTransferTargetType getType() const { return mType; }
+ LLTransferTargetChannel *getChannel() const { return mChannelp; }
+
+ friend class LLTransferManager;
+ friend class LLTransferTargetChannel;
+
+ static LLTransferTarget *createTarget(const LLTransferTargetType type,
+ const LLUUID &request_id);
+protected:
+ virtual void applyParams(const LLTransferTargetParams &params) = 0;
+ virtual LLTSCode dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size) = 0;
+
+ // The completionCallback is GUARANTEED to be called before the destructor, so all handling
+ // of errors/aborts should be done here.
+ virtual void completionCallback(const LLTSCode status) = 0;
+
+ void abortTransfer();
+
+ virtual S32 getNextPacketID() { return mLastPacketID + 1; }
+ virtual void setLastPacketID(const S32 packet_id) { mLastPacketID = packet_id; }
+ void setSize(const S32 size) { mSize = size; }
+ void setGotInfo(const BOOL got_info) { mGotInfo = got_info; }
+ BOOL gotInfo() const { return mGotInfo; }
+
+ void addDelayedPacket(const S32 packet_id, const LLTSCode status, U8 *datap, const S32 size);
+
+protected:
+ typedef std::map<S32, LLTransferPacket *> transfer_packet_map;
+ typedef std::map<S32, LLTransferPacket *>::iterator tpm_iter;
+
+ LLTransferTargetType mType;
+ LLUUID mID;
+ LLTransferTargetChannel *mChannelp;
+ BOOL mGotInfo;
+ S32 mSize;
+ S32 mLastPacketID;
+
+ transfer_packet_map mDelayedPacketMap; // Packets that are waiting because of missing/out of order issues
+};
+
+
+// Hack, here so it's publicly available even though LLTransferSourceInvItem is only available on the simulator
+class LLTransferSourceParamsInvItem: public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsInvItem();
+ virtual ~LLTransferSourceParamsInvItem() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setAgentSession(const LLUUID &agent_id, const LLUUID &session_id);
+ void setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id);
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType at);
+
+ LLUUID getAgentID() const { return mAgentID; }
+ LLUUID getSessionID() const { return mSessionID; }
+ LLUUID getOwnerID() const { return mOwnerID; }
+ LLUUID getTaskID() const { return mTaskID; }
+ LLUUID getItemID() const { return mItemID; }
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+protected:
+ LLUUID mAgentID;
+ LLUUID mSessionID;
+ LLUUID mOwnerID;
+ LLUUID mTaskID;
+ LLUUID mItemID;
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+};
+
+
+// Hack, here so it's publicly available even though LLTransferSourceEstate is only available on the simulator
+class LLTransferSourceParamsEstate: public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsEstate();
+ virtual ~LLTransferSourceParamsEstate() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setAgentSession(const LLUUID &agent_id, const LLUUID &session_id);
+ void setEstateAssetType(const EstateAssetType etype);
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType at);
+
+ LLUUID getAgentID() const { return mAgentID; }
+ LLUUID getSessionID() const { return mSessionID; }
+ EstateAssetType getEstateAssetType() const { return mEstateAssetType; }
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+protected:
+ LLUUID mAgentID;
+ LLUUID mSessionID;
+ EstateAssetType mEstateAssetType;
+ // these are set on the sim based on estateinfotype
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+};
+
+
+extern LLTransferManager gTransferManager;
+
+#endif//LL_LLTRANSFERMANAGER_H
diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp
new file mode 100644
index 0000000000..f7c6711bd0
--- /dev/null
+++ b/indra/llmessage/lltransfersourceasset.cpp
@@ -0,0 +1,235 @@
+/**
+ * @file lltransfersourceasset.cpp
+ * @brief Transfer system for sending an asset.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfersourceasset.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+#include "llvfile.h"
+
+LLTransferSourceAsset::LLTransferSourceAsset(const LLUUID &request_id, const F32 priority) :
+ LLTransferSource(LLTST_ASSET, request_id, priority),
+ mGotResponse(FALSE),
+ mCurPos(0)
+{
+}
+
+LLTransferSourceAsset::~LLTransferSourceAsset()
+{
+}
+
+
+void LLTransferSourceAsset::initTransfer()
+{
+ if (gAssetStorage)
+ {
+ // *HACK: asset transfers will only be coming from the viewer
+ // to the simulator. This is subset of assets we allow to be
+ // simply pulled straight from the asset system.
+ // *FIX: Make this list smaller.
+ LLUUID* tidp;
+ switch(mParams.getAssetType())
+ {
+ case LLAssetType::AT_SOUND:
+ case LLAssetType::AT_LANDMARK:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_GESTURE:
+ case LLAssetType::AT_ANIMATION:
+ tidp = new LLUUID(getID());
+ gAssetStorage->getAssetData(
+ mParams.getAssetID(),
+ mParams.getAssetType(),
+ LLTransferSourceAsset::responderCallback,
+ tidp,
+ FALSE);
+ break;
+ default:
+ llwarns << "Attempted to request blocked asset "
+ << mParams.getAssetID() << ":"
+ << LLAssetType::lookupHumanReadable(mParams.getAssetType())
+ << llendl;
+ sendTransferStatus(LLTS_ERROR);
+ break;
+ }
+ }
+ else
+ {
+ llwarns << "Attempted to request asset "
+ << mParams.getAssetID() << ":" << LLAssetType::lookupHumanReadable(mParams.getAssetType())
+ << " without an asset system!" << llendl;
+ sendTransferStatus(LLTS_ERROR);
+ }
+}
+
+F32 LLTransferSourceAsset::updatePriority()
+{
+ return 0.f;
+}
+
+LLTSCode LLTransferSourceAsset::dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **data_handle,
+ S32 &returned_bytes,
+ BOOL &delete_returned)
+{
+ //llinfos << "LLTransferSourceAsset::dataCallback" << llendl;
+ if (!mGotResponse)
+ {
+ return LLTS_SKIP;
+ }
+
+ LLVFile vf(gAssetStorage->mVFS, mParams.getAssetID(), mParams.getAssetType(), LLVFile::READ);
+
+ if (!vf.getSize())
+ {
+ // Something bad happened with the asset request!
+ return LLTS_ERROR;
+ }
+
+ if (packet_id != mLastPacketID + 1)
+ {
+ llerrs << "Can't handle out of order file transfer yet!" << llendl;
+ }
+
+ // grab a buffer from the right place in the file
+ if (!vf.seek(mCurPos, 0))
+ {
+ llwarns << "LLTransferSourceAsset Can't seek to " << mCurPos << " length " << vf.getSize() << llendl;
+ llwarns << "While sending " << mParams.getAssetID() << llendl;
+ return LLTS_ERROR;
+ }
+
+ delete_returned = TRUE;
+ U8 *tmpp = new U8[max_bytes];
+ *data_handle = tmpp;
+ if (!vf.read(tmpp, max_bytes)) /* Flawfinder: Ignore */
+ {
+ // Crap, read failure, need to deal with it.
+ delete[] tmpp;
+ *data_handle = NULL;
+ returned_bytes = 0;
+ delete_returned = FALSE;
+ return LLTS_ERROR;
+ }
+
+ returned_bytes = vf.getLastBytesRead();
+ mCurPos += returned_bytes;
+
+
+ if (vf.eof())
+ {
+ if (!returned_bytes)
+ {
+ delete[] tmpp;
+ *data_handle = NULL;
+ returned_bytes = 0;
+ delete_returned = FALSE;
+ }
+ return LLTS_DONE;
+ }
+
+ return LLTS_OK;
+}
+
+void LLTransferSourceAsset::completionCallback(const LLTSCode status)
+{
+ // No matter what happens, all we want to do is close the vfile if
+ // we've got it open.
+}
+
+BOOL LLTransferSourceAsset::unpackParams(LLDataPacker &dp)
+{
+ //llinfos << "LLTransferSourceAsset::unpackParams" << llendl;
+
+ return mParams.unpackParams(dp);
+}
+
+
+void LLTransferSourceAsset::responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type,
+ void *user_data, S32 result)
+{
+ LLUUID *tidp = ((LLUUID*) user_data);
+ LLUUID transfer_id = *(tidp);
+ delete tidp;
+ tidp = NULL;
+
+ LLTransferSourceAsset *tsap = (LLTransferSourceAsset *) gTransferManager.findTransferSource(transfer_id);
+
+ if (!tsap)
+ {
+ llinfos << "Aborting transfer " << transfer_id << " callback, transfer source went away" << llendl;
+ return;
+ }
+
+ if (result)
+ {
+ llinfos << "AssetStorage: Error " << gAssetStorage->getErrorString(result) << " downloading uuid " << uuid << llendl;
+ }
+
+ LLTSCode status;
+
+ tsap->mGotResponse = TRUE;
+ if (LL_ERR_NOERR == result)
+ {
+ // Everything's OK.
+ LLVFile vf(gAssetStorage->mVFS, uuid, type, LLVFile::READ);
+ tsap->mSize = vf.getSize();
+ status = LLTS_OK;
+ }
+ else
+ {
+ // Uh oh, something bad happened when we tried to get this asset!
+ switch (result)
+ {
+ case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
+ status = LLTS_UNKNOWN_SOURCE;
+ break;
+ default:
+ status = LLTS_ERROR;
+ }
+ }
+
+ tsap->sendTransferStatus(status);
+}
+
+
+
+LLTransferSourceParamsAsset::LLTransferSourceParamsAsset() : LLTransferSourceParams(LLTST_ASSET)
+{
+}
+
+void LLTransferSourceParamsAsset::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+void LLTransferSourceParamsAsset::packParams(LLDataPacker &dp) const
+{
+ dp.packUUID(mAssetID, "AssetID");
+ dp.packS32(mAssetType, "AssetType");
+}
+
+
+BOOL LLTransferSourceParamsAsset::unpackParams(LLDataPacker &dp)
+{
+ S32 tmp_at;
+
+ dp.unpackUUID(mAssetID, "AssetID");
+ dp.unpackS32(tmp_at, "AssetType");
+
+ mAssetType = (LLAssetType::EType)tmp_at;
+
+ return TRUE;
+}
+
diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h
new file mode 100644
index 0000000000..931fc461f9
--- /dev/null
+++ b/indra/llmessage/lltransfersourceasset.h
@@ -0,0 +1,62 @@
+/**
+ * @file lltransfersourceasset.h
+ * @brief Transfer system for sending an asset.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERSOURCEASSET_H
+#define LL_LLTRANSFERSOURCEASSET_H
+
+#include "lltransfermanager.h"
+#include "llassetstorage.h"
+
+class LLVFile;
+
+class LLTransferSourceParamsAsset : public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsAsset();
+ virtual ~LLTransferSourceParamsAsset() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type);
+
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+protected:
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+};
+
+class LLTransferSourceAsset : public LLTransferSource
+{
+public:
+ LLTransferSourceAsset(const LLUUID &request_id, const F32 priority);
+ virtual ~LLTransferSourceAsset();
+
+ static void responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type,
+ void *user_data, S32 result);
+protected:
+ /*virtual*/ void initTransfer();
+ /*virtual*/ F32 updatePriority();
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **datap,
+ S32 &returned_bytes,
+ BOOL &delete_returned);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+protected:
+ LLTransferSourceParamsAsset mParams;
+ BOOL mGotResponse;
+
+ S32 mCurPos;
+};
+
+#endif // LL_LLTRANSFERSOURCEASSET_H
diff --git a/indra/llmessage/lltransfersourcefile.cpp b/indra/llmessage/lltransfersourcefile.cpp
new file mode 100644
index 0000000000..45b03d7653
--- /dev/null
+++ b/indra/llmessage/lltransfersourcefile.cpp
@@ -0,0 +1,151 @@
+/**
+ * @file lltransfersourcefile.cpp
+ * @brief Transfer system for sending a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfersourcefile.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+
+LLTransferSourceFile::LLTransferSourceFile(const LLUUID &request_id, const F32 priority) :
+ LLTransferSource(LLTST_FILE, request_id, priority),
+ mFP(NULL)
+{
+}
+
+LLTransferSourceFile::~LLTransferSourceFile()
+{
+ if (mFP)
+ {
+ llerrs << "Destructor called without the completion callback being called!" << llendl;
+ }
+}
+
+void LLTransferSourceFile::initTransfer()
+{
+ std::string filename = mParams.getFilename();
+ std::string delimiter = gDirUtilp->getDirDelimiter();
+
+ if((filename == ".")
+ || (filename == "..")
+ || (filename.find(delimiter[0]) != std::string::npos))
+ {
+ llwarns << "Attempting to transfer file " << filename << " with path delimiter, aborting!" << llendl;
+
+ sendTransferStatus(LLTS_ERROR);
+ return;
+ }
+ // Look for the file.
+ mFP = LLFile::fopen(mParams.getFilename().c_str(), "rb"); /* Flawfinder: ignore */
+ if (!mFP)
+ {
+ sendTransferStatus(LLTS_ERROR);
+ return;
+ }
+
+ // Get the size of the file using the hack from
+ fseek(mFP,0,SEEK_END);
+ mSize = ftell(mFP);
+ fseek(mFP,0,SEEK_SET);
+
+ sendTransferStatus(LLTS_OK);
+}
+
+F32 LLTransferSourceFile::updatePriority()
+{
+ return 0.f;
+}
+
+LLTSCode LLTransferSourceFile::dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **data_handle,
+ S32 &returned_bytes,
+ BOOL &delete_returned)
+{
+ //llinfos << "LLTransferSourceFile::dataCallback" << llendl;
+
+ if (!mFP)
+ {
+ llerrs << "Data callback without file set!" << llendl;
+ return LLTS_ERROR;
+ }
+
+ if (packet_id != mLastPacketID + 1)
+ {
+ llerrs << "Can't handle out of order file transfer yet!" << llendl;
+ }
+
+ // Grab up until the max number of bytes from the file.
+ delete_returned = TRUE;
+ U8 *tmpp = new U8[max_bytes];
+ *data_handle = tmpp;
+ returned_bytes = (S32)fread(tmpp, 1, max_bytes, mFP);
+ if (!returned_bytes)
+ {
+ delete[] tmpp;
+ *data_handle = NULL;
+ returned_bytes = 0;
+ delete_returned = FALSE;
+ return LLTS_DONE;
+ }
+
+ return LLTS_OK;
+}
+
+void LLTransferSourceFile::completionCallback(const LLTSCode status)
+{
+ // No matter what happens, all we want to do is close the file pointer if
+ // we've got it open.
+ if (mFP)
+ {
+ fclose(mFP);
+ mFP = NULL;
+
+ }
+ // Delete the file iff the filename begins with "TEMP"
+ if (mParams.getDeleteOnCompletion() && memcmp(mParams.getFilename().c_str(), "TEMP", 4) == 0)
+ {
+ LLFile::remove(mParams.getFilename().c_str());
+ }
+}
+
+BOOL LLTransferSourceFile::unpackParams(LLDataPacker &dp)
+{
+ //llinfos << "LLTransferSourceFile::unpackParams" << llendl;
+
+ return mParams.unpackParams(dp);
+}
+
+
+LLTransferSourceParamsFile::LLTransferSourceParamsFile() : LLTransferSourceParams(LLTST_FILE)
+{
+}
+
+
+void LLTransferSourceParamsFile::packParams(LLDataPacker &dp) const
+{
+ dp.packString(mFilename.c_str(), "Filename");
+ dp.packU8((U8)mDeleteOnCompletion, "Delete");
+}
+
+
+BOOL LLTransferSourceParamsFile::unpackParams(LLDataPacker &dp)
+{
+ char tmp_str[512]; /* Flawfinder: ignore */
+ dp.unpackString(tmp_str, "Filename");
+ mFilename = tmp_str;
+ U8 delete_flag;
+ dp.unpackU8(delete_flag, "Delete");
+ mDeleteOnCompletion = delete_flag;
+
+ llinfos << "Unpacked filename: " << mFilename << llendl;
+ return TRUE;
+}
diff --git a/indra/llmessage/lltransfersourcefile.h b/indra/llmessage/lltransfersourcefile.h
new file mode 100644
index 0000000000..ecaa6c908a
--- /dev/null
+++ b/indra/llmessage/lltransfersourcefile.h
@@ -0,0 +1,58 @@
+/**
+ * @file lltransfersourcefile.h
+ * @brief Transfer system for sending a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERSOURCEFILE_H
+#define LL_LLTRANSFERSOURCEFILE_H
+
+#include "lltransfermanager.h"
+
+#include <stdio.h>
+
+class LLTransferSourceParamsFile : public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsFile();
+ virtual ~LLTransferSourceParamsFile() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setFilename(const LLString &filename) { mFilename = filename; }
+ std::string getFilename() const { return mFilename; }
+
+ void setDeleteOnCompletion(BOOL enabled) { mDeleteOnCompletion = enabled; }
+ BOOL getDeleteOnCompletion() { return mDeleteOnCompletion; }
+protected:
+ std::string mFilename;
+ // ONLY DELETE THINGS OFF THE SIM IF THE FILENAME BEGINS IN 'TEMP'
+ BOOL mDeleteOnCompletion;
+};
+
+class LLTransferSourceFile : public LLTransferSource
+{
+public:
+ LLTransferSourceFile(const LLUUID &transfer_id, const F32 priority);
+ virtual ~LLTransferSourceFile();
+
+protected:
+ /*virtual*/ void initTransfer();
+ /*virtual*/ F32 updatePriority();
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **datap,
+ S32 &returned_bytes,
+ BOOL &delete_returned);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+protected:
+ LLTransferSourceParamsFile mParams;
+ FILE *mFP;
+};
+
+#endif // LL_LLTRANSFERSOURCEFILE_H
diff --git a/indra/llmessage/lltransfertargetfile.cpp b/indra/llmessage/lltransfertargetfile.cpp
new file mode 100644
index 0000000000..92776e081d
--- /dev/null
+++ b/indra/llmessage/lltransfertargetfile.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file lltransfertargetfile.cpp
+ * @brief Transfer system for receiving a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfertargetfile.h"
+#include "llerror.h"
+
+
+
+
+LLTransferTargetFile::LLTransferTargetFile(const LLUUID &uuid) :
+ LLTransferTarget(LLTTT_FILE, uuid),
+ mFP(NULL)
+{
+}
+
+LLTransferTargetFile::~LLTransferTargetFile()
+{
+ if (mFP)
+ {
+ llerrs << "LLTransferTargetFile::~LLTransferTargetFile - Should have been cleaned up in completion callback" << llendl;
+ fclose(mFP);
+ mFP = NULL;
+ }
+}
+
+void LLTransferTargetFile::applyParams(const LLTransferTargetParams &params)
+{
+ if (params.getType() != mType)
+ {
+ llwarns << "Target parameter type doesn't match!" << llendl;
+ return;
+ }
+
+ mParams = (LLTransferTargetParamsFile &)params;
+}
+
+LLTSCode LLTransferTargetFile::dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size)
+{
+ //llinfos << "LLTransferTargetFile::dataCallback" << llendl;
+ //llinfos << "Packet: " << packet_id << llendl;
+
+ if (!mFP)
+ {
+ mFP = LLFile::fopen(mParams.mFilename.c_str(), "wb"); /* Flawfinder: ignore */
+
+ if (!mFP)
+ {
+ llwarns << "Failure opening " << mParams.mFilename << " for write by LLTransferTargetFile" << llendl;
+ return LLTS_ERROR;
+ }
+ }
+ if (!in_size)
+ {
+ return LLTS_OK;
+ }
+
+ S32 count = (S32)fwrite(in_datap, 1, in_size, mFP);
+ if (count != in_size)
+ {
+ llwarns << "Failure in LLTransferTargetFile::dataCallback!" << llendl;
+ return LLTS_ERROR;
+ }
+ return LLTS_OK;
+}
+
+void LLTransferTargetFile::completionCallback(const LLTSCode status)
+{
+ llinfos << "LLTransferTargetFile::completionCallback" << llendl;
+ if (mFP)
+ {
+ fclose(mFP);
+ }
+
+ // Still need to gracefully handle error conditions.
+ switch (status)
+ {
+ case LLTS_DONE:
+ break;
+ case LLTS_ABORT:
+ case LLTS_ERROR:
+ // We're aborting this transfer, we don't want to keep this file.
+ llwarns << "Aborting file transfer for " << mParams.mFilename << llendl;
+ if (mFP)
+ {
+ // Only need to remove file if we successfully opened it.
+ LLFile::remove(mParams.mFilename.c_str());
+ }
+ default:
+ break;
+ }
+
+ mFP = NULL;
+ if (mParams.mCompleteCallback)
+ {
+ mParams.mCompleteCallback(status, mParams.mUserData);
+ }
+}
diff --git a/indra/llmessage/lltransfertargetfile.h b/indra/llmessage/lltransfertargetfile.h
new file mode 100644
index 0000000000..63cc21262b
--- /dev/null
+++ b/indra/llmessage/lltransfertargetfile.h
@@ -0,0 +1,53 @@
+/**
+ * @file lltransfertargetfile.h
+ * @brief Transfer system for receiving a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERTARGETFILE_H
+#define LL_LLTRANSFERTARGETFILE_H
+
+#include "lltransfermanager.h"
+
+#include <stdio.h>
+
+typedef void (*LLTTFCompleteCallback)(const LLTSCode status, void *user_data);
+
+class LLTransferTargetParamsFile : public LLTransferTargetParams
+{
+public:
+ LLTransferTargetParamsFile() : LLTransferTargetParams(LLTTT_FILE) {}
+ void setFilename(const char *filename) { mFilename = filename; }
+ void setCallback(LLTTFCompleteCallback cb, void *user_data) { mCompleteCallback = cb; mUserData = user_data; }
+
+ friend class LLTransferTargetFile;
+protected:
+ LLString mFilename;
+ LLTTFCompleteCallback mCompleteCallback;
+ void * mUserData;
+};
+
+
+class LLTransferTargetFile : public LLTransferTarget
+{
+public:
+ LLTransferTargetFile(const LLUUID &uuid);
+ virtual ~LLTransferTargetFile();
+
+ static void requestTransfer(LLTransferTargetChannel *channelp,
+ const char *local_filename,
+ const LLTransferSourceParams &source_params,
+ LLTTFCompleteCallback callback);
+protected:
+ /*virtual*/ void applyParams(const LLTransferTargetParams &params);
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ LLTransferTargetParamsFile mParams;
+
+ FILE *mFP;
+};
+
+#endif // LL_LLTRANSFERTARGETFILE_H
diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp
new file mode 100644
index 0000000000..9e323537d7
--- /dev/null
+++ b/indra/llmessage/lltransfertargetvfile.cpp
@@ -0,0 +1,187 @@
+/**
+ * @file lltransfertargetvfile.cpp
+ * @brief Transfer system for receiving a vfile.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfertargetvfile.h"
+#include "llerror.h"
+
+#include "llvfile.h"
+
+//static
+std::list<LLTransferTargetParamsVFile*> LLTransferTargetVFile::sCallbackQueue;
+
+//static
+void LLTransferTargetVFile::updateQueue(bool shutdown)
+{
+ for(std::list<LLTransferTargetParamsVFile*>::iterator iter = sCallbackQueue.begin();
+ iter != sCallbackQueue.end(); )
+ {
+ std::list<LLTransferTargetParamsVFile*>::iterator curiter = iter++;
+ LLTransferTargetParamsVFile* params = *curiter;
+ LLVFSThread::status_t s = LLVFile::getVFSThread()->getRequestStatus(params->mHandle);
+ if (s == LLVFSThread::STATUS_COMPLETE || s == LLVFSThread::STATUS_EXPIRED)
+ {
+ params->mCompleteCallback(params->mErrCode, params->mUserDatap);
+ delete params;
+ iter = sCallbackQueue.erase(curiter);
+ }
+ else if (shutdown)
+ {
+ delete params;
+ iter = sCallbackQueue.erase(curiter);
+ }
+ }
+}
+
+
+LLTransferTargetParamsVFile::LLTransferTargetParamsVFile() :
+ LLTransferTargetParams(LLTTT_VFILE),
+ mAssetType(LLAssetType::AT_NONE),
+ mCompleteCallback(NULL),
+ mUserDatap(NULL),
+ mErrCode(0),
+ mHandle(LLVFSThread::nullHandle())
+{
+}
+
+void LLTransferTargetParamsVFile::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+void LLTransferTargetParamsVFile::setCallback(LLTTVFCompleteCallback cb, void *user_data)
+{
+ mCompleteCallback = cb;
+ mUserDatap = user_data;
+}
+
+
+LLTransferTargetVFile::LLTransferTargetVFile(const LLUUID &uuid) :
+ LLTransferTarget(LLTTT_VFILE, uuid),
+ mNeedsCreate(TRUE)
+{
+ mTempID.generate();
+}
+
+
+LLTransferTargetVFile::~LLTransferTargetVFile()
+{
+}
+
+
+void LLTransferTargetVFile::applyParams(const LLTransferTargetParams &params)
+{
+ if (params.getType() != mType)
+ {
+ llwarns << "Target parameter type doesn't match!" << llendl;
+ return;
+ }
+
+ mParams = (LLTransferTargetParamsVFile &)params;
+}
+
+
+LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size)
+{
+ //llinfos << "LLTransferTargetFile::dataCallback" << llendl;
+ //llinfos << "Packet: " << packet_id << llendl;
+
+ LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND);
+ if (mNeedsCreate)
+ {
+ vf.setMaxSize(mSize);
+ mNeedsCreate = FALSE;
+ }
+
+ if (!in_size)
+ {
+ return LLTS_OK;
+ }
+
+ if (!vf.write(in_datap, in_size))
+ {
+ llwarns << "Failure in LLTransferTargetVFile::dataCallback!" << llendl;
+ return LLTS_ERROR;
+ }
+ return LLTS_OK;
+}
+
+
+void LLTransferTargetVFile::completionCallback(const LLTSCode status)
+{
+ //llinfos << "LLTransferTargetVFile::completionCallback" << llendl;
+
+ if (!gAssetStorage)
+ {
+ llwarns << "Aborting vfile transfer after asset storage shut down!" << llendl;
+ return;
+ }
+ LLVFSThread::handle_t handle = LLVFSThread::nullHandle();
+
+ // Still need to gracefully handle error conditions.
+ S32 err_code = 0;
+ switch (status)
+ {
+ case LLTS_DONE:
+ if (!mNeedsCreate)
+ {
+ handle = LLVFile::getVFSThread()->rename(gAssetStorage->mVFS,
+ mTempID, mParams.getAssetType(),
+ mParams.getAssetID(), mParams.getAssetType(),
+ LLVFSThread::AUTO_DELETE);
+ }
+ err_code = LL_ERR_NOERR;
+ // llinfos << "Successful vfile transfer for " << mParams.getAssetID() << llendl;
+ break;
+ case LLTS_ERROR:
+ case LLTS_ABORT:
+ case LLTS_UNKNOWN_SOURCE:
+ default:
+ {
+ // We're aborting this transfer, we don't want to keep this file.
+ llwarns << "Aborting vfile transfer for " << mParams.getAssetID() << llendl;
+ LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND);
+ vf.remove();
+ }
+ break;
+ }
+
+ switch (status)
+ {
+ case LLTS_DONE:
+ err_code = LL_ERR_NOERR;
+ break;
+ case LLTS_UNKNOWN_SOURCE:
+ err_code = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ break;
+ case LLTS_INSUFFICIENT_PERMISSIONS:
+ err_code = LL_ERR_INSUFFICIENT_PERMISSIONS;
+ break;
+ case LLTS_ERROR:
+ case LLTS_ABORT:
+ default:
+ err_code = LL_ERR_ASSET_REQUEST_FAILED;
+ break;
+ }
+ if (mParams.mCompleteCallback)
+ {
+ if (handle != LLVFSThread::nullHandle())
+ {
+ LLTransferTargetParamsVFile* params = new LLTransferTargetParamsVFile(mParams);
+ params->mErrCode = err_code;
+ params->mHandle = handle;
+ sCallbackQueue.push_back(params);
+ }
+ else
+ {
+ mParams.mCompleteCallback(err_code, mParams.mUserDatap);
+ }
+ }
+}
diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h
new file mode 100644
index 0000000000..7614021179
--- /dev/null
+++ b/indra/llmessage/lltransfertargetvfile.h
@@ -0,0 +1,70 @@
+/**
+ * @file lltransfertargetvfile.h
+ * @brief Transfer system for receiving a vfile.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERTARGETVFILE_H
+#define LL_LLTRANSFERTARGETVFILE_H
+
+#include "lltransfermanager.h"
+#include "llassetstorage.h"
+#include "llvfile.h"
+
+class LLVFile;
+
+// Lame, an S32 for now until I figure out the deal with how we want to do
+// error codes.
+typedef void (*LLTTVFCompleteCallback)(const S32 status, void *user_data);
+
+class LLTransferTargetParamsVFile : public LLTransferTargetParams
+{
+public:
+ LLTransferTargetParamsVFile();
+
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type);
+ void setCallback(LLTTVFCompleteCallback cb, void *user_data);
+
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+ friend class LLTransferTargetVFile;
+protected:
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+
+ LLTTVFCompleteCallback mCompleteCallback;
+ void * mUserDatap;
+ S32 mErrCode;
+ LLVFSThread::handle_t mHandle;
+};
+
+
+class LLTransferTargetVFile : public LLTransferTarget
+{
+public:
+ LLTransferTargetVFile(const LLUUID &uuid);
+ virtual ~LLTransferTargetVFile();
+
+ static void requestTransfer(LLTransferTargetChannel *channelp,
+ const char *local_filename,
+ const LLTransferSourceParams &source_params,
+ LLTTVFCompleteCallback callback);
+ static void updateQueue(bool shutdown = false);
+
+protected:
+ /*virtual*/ void applyParams(const LLTransferTargetParams &params);
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ LLTransferTargetParamsVFile mParams;
+
+ BOOL mNeedsCreate;
+ LLUUID mTempID;
+
+ static std::list<LLTransferTargetParamsVFile*> sCallbackQueue;
+};
+
+#endif // LL_LLTRANSFERTARGETFILE_H
diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp
new file mode 100644
index 0000000000..ea0b13f703
--- /dev/null
+++ b/indra/llmessage/llurlrequest.cpp
@@ -0,0 +1,650 @@
+/**
+ * @file llurlrequest.cpp
+ * @author Phoenix
+ * @date 2005-04-28
+ * @brief Implementation of the URLRequest class and related classes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llurlrequest.h"
+
+#include <curl/curl.h>
+#include <algorithm>
+
+#include "llioutil.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llstring.h"
+
+static const U32 HTTP_STATUS_PIPE_ERROR = 499;
+
+/**
+ * String constants
+ */
+const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
+
+
+static
+size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
+
+/**
+ * class LLURLRequestDetail
+ */
+class LLURLRequestDetail
+{
+public:
+ LLURLRequestDetail();
+ ~LLURLRequestDetail();
+ CURLM* mCurlMulti;
+ CURL* mCurl;
+ struct curl_slist* mHeaders;
+ char* mURL;
+ char mCurlErrorBuf[CURL_ERROR_SIZE + 1]; /* Flawfinder: ignore */
+ bool mNeedToRemoveEasyHandle;
+ LLBufferArray* mResponseBuffer;
+ LLChannelDescriptors mChannels;
+ U8* mLastRead;
+ U32 mBodyLimit;
+ bool mIsBodyLimitSet;
+};
+
+LLURLRequestDetail::LLURLRequestDetail() :
+ mCurlMulti(NULL),
+ mCurl(NULL),
+ mHeaders(NULL),
+ mURL(NULL),
+ mNeedToRemoveEasyHandle(false),
+ mResponseBuffer(NULL),
+ mLastRead(NULL),
+ mBodyLimit(0),
+ mIsBodyLimitSet(false)
+
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mCurlErrorBuf[0] = '\0';
+}
+
+LLURLRequestDetail::~LLURLRequestDetail()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ if(mCurl)
+ {
+ if(mNeedToRemoveEasyHandle && mCurlMulti)
+ {
+ curl_multi_remove_handle(mCurlMulti, mCurl);
+ mNeedToRemoveEasyHandle = false;
+ }
+ curl_easy_cleanup(mCurl);
+ mCurl = NULL;
+ }
+ if(mCurlMulti)
+ {
+ curl_multi_cleanup(mCurlMulti);
+ mCurlMulti = NULL;
+ }
+ if(mHeaders)
+ {
+ curl_slist_free_all(mHeaders);
+ mHeaders = NULL;
+ }
+ delete[] mURL;
+ mURL = NULL;
+ mResponseBuffer = NULL;
+ mLastRead = NULL;
+}
+
+
+/**
+ * class LLURLRequest
+ */
+
+static std::string sCAFile("");
+static std::string sCAPath("");
+
+LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
+ mAction(action)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ initialize();
+}
+
+LLURLRequest::LLURLRequest(
+ LLURLRequest::ERequestAction action,
+ const std::string& url) :
+ mAction(action)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ initialize();
+ setURL(url);
+}
+
+LLURLRequest::~LLURLRequest()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ delete mDetail;
+}
+
+void LLURLRequest::setURL(const std::string& url)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ if(mDetail->mURL)
+ {
+ // *NOTE: if any calls to set the url have been made to curl,
+ // this will probably lead to a crash.
+ delete[] mDetail->mURL;
+ mDetail->mURL = NULL;
+ }
+ if(!url.empty())
+ {
+ mDetail->mURL = new char[url.size() + 1];
+ url.copy(mDetail->mURL, url.size());
+ mDetail->mURL[url.size()] = '\0';
+ }
+}
+
+void LLURLRequest::addHeader(const char* header)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mDetail->mHeaders = curl_slist_append(mDetail->mHeaders, header);
+}
+
+void LLURLRequest::requestEncoding(const char* encoding)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_ENCODING, encoding);
+}
+
+void LLURLRequest::setBodyLimit(U32 size)
+{
+ mDetail->mBodyLimit = size;
+ mDetail->mIsBodyLimitSet = true;
+}
+
+void LLURLRequest::checkRootCertificate(bool check, const char* caBundle)
+{
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
+ if (caBundle)
+ {
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, caBundle);
+ }
+}
+
+void LLURLRequest::setCallback(LLURLRequestComplete* callback)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mCompletionCallback = callback;
+
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_HEADERFUNCTION, &headerCallback);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEHEADER, callback);
+}
+
+// virtual
+LLIOPipe::EStatus LLURLRequest::handleError(
+ LLIOPipe::EStatus status,
+ LLPumpIO* pump)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ if(mCompletionCallback && pump)
+ {
+ LLURLRequestComplete* complete = NULL;
+ complete = (LLURLRequestComplete*)mCompletionCallback.get();
+ complete->httpStatus(
+ HTTP_STATUS_PIPE_ERROR,
+ LLIOPipe::lookupStatusString(status));
+ complete->responseStatus(status);
+ pump->respond(complete);
+ mCompletionCallback = NULL;
+ }
+ return status;
+}
+
+// virtual
+LLIOPipe::EStatus LLURLRequest::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ //llinfos << "LLURLRequest::process_impl()" << llendl;
+ if(!buffer) return STATUS_ERROR;
+ switch(mState)
+ {
+ case STATE_INITIALIZED:
+ {
+ PUMP_DEBUG;
+ // We only need to wait for input if we are uploading
+ // something.
+ if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
+ {
+ // we're waiting to get all of the information
+ return STATUS_BREAK;
+ }
+
+ // *FIX: bit of a hack, but it should work. The configure and
+ // callback method expect this information to be ready.
+ mDetail->mResponseBuffer = buffer.get();
+ mDetail->mChannels = channels;
+ if(!configure())
+ {
+ return STATUS_ERROR;
+ }
+ mState = STATE_WAITING_FOR_RESPONSE;
+
+ // *FIX: Maybe we should just go to the next state now...
+ return STATUS_BREAK;
+ }
+ case STATE_WAITING_FOR_RESPONSE:
+ case STATE_PROCESSING_RESPONSE:
+ {
+ PUMP_DEBUG;
+ const S32 MAX_CALLS = 5;
+ S32 count = MAX_CALLS;
+ CURLMcode code;
+ LLIOPipe::EStatus status = STATUS_BREAK;
+ S32 queue;
+ do
+ {
+ code = curl_multi_perform(mDetail->mCurlMulti, &queue);
+ }while((CURLM_CALL_MULTI_PERFORM == code) && (queue > 0) && count--);
+ CURLMsg* curl_msg;
+ do
+ {
+ curl_msg = curl_multi_info_read(mDetail->mCurlMulti, &queue);
+ if(curl_msg && (curl_msg->msg == CURLMSG_DONE))
+ {
+ mState = STATE_HAVE_RESPONSE;
+
+ CURLcode result = curl_msg->data.result;
+ switch(result)
+ {
+ case CURLE_OK:
+ case CURLE_WRITE_ERROR:
+ // NB: The error indication means that we stopped the
+ // writing due the body limit being reached
+ if(mCompletionCallback && pump)
+ {
+ LLURLRequestComplete* complete = NULL;
+ complete = (LLURLRequestComplete*)
+ mCompletionCallback.get();
+ complete->responseStatus(
+ result == CURLE_OK
+ ? STATUS_OK : STATUS_STOP);
+ LLPumpIO::links_t chain;
+ LLPumpIO::LLLinkInfo link;
+ link.mPipe = mCompletionCallback;
+ link.mChannels = LLBufferArray::makeChannelConsumer(
+ channels);
+ chain.push_back(link);
+ pump->respond(chain, buffer, context);
+ mCompletionCallback = NULL;
+ }
+ break;
+ case CURLE_COULDNT_CONNECT:
+ status = STATUS_NO_CONNECTION;
+ break;
+ default:
+ llwarns << "URLRequest Error: " << curl_msg->data.result
+ << ", "
+#if LL_DARWIN
+ // curl_easy_strerror was added in libcurl 7.12.0. Unfortunately, the version in the Mac OS X 10.3.9 SDK is 7.10.2...
+ // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number
+ // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00). We'll fix the header problem soon, but for now
+ // just punt and print the numeric error code on the Mac.
+ << curl_msg->data.result
+#else // LL_DARWIN
+ << curl_easy_strerror(curl_msg->data.result)
+#endif // LL_DARWIN
+ << ", "
+ << (mDetail->mURL ? mDetail->mURL : "<EMPTY URL>")
+ << llendl;
+ status = STATUS_ERROR;
+ break;
+ }
+ curl_multi_remove_handle(mDetail->mCurlMulti, mDetail->mCurl);
+ mDetail->mNeedToRemoveEasyHandle = false;
+ }
+ }while(curl_msg && (queue > 0));
+ return status;
+ }
+ case STATE_HAVE_RESPONSE:
+ PUMP_DEBUG;
+ // we already stuffed everything into channel in in the curl
+ // callback, so we are done.
+ eos = true;
+ return STATUS_DONE;
+
+ default:
+ PUMP_DEBUG;
+ return STATUS_ERROR;
+ }
+}
+
+void LLURLRequest::initialize()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mState = STATE_INITIALIZED;
+ mDetail = new LLURLRequestDetail;
+ mDetail->mCurl = curl_easy_init();
+ mDetail->mCurlMulti = curl_multi_init();
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEFUNCTION, &downCallback);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_READFUNCTION, &upCallback);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_READDATA, this);
+ curl_easy_setopt(
+ mDetail->mCurl,
+ CURLOPT_ERRORBUFFER,
+ mDetail->mCurlErrorBuf);
+
+ if(sCAPath != std::string(""))
+ {
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CAPATH, sCAPath.c_str());
+ }
+ if(sCAFile != std::string(""))
+ {
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, sCAFile.c_str());
+ }
+}
+
+bool LLURLRequest::configure()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ bool rv = false;
+ S32 bytes = mDetail->mResponseBuffer->countAfter(
+ mDetail->mChannels.in(),
+ NULL);
+ switch(mAction)
+ {
+ case HTTP_GET:
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_HTTPGET, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_FOLLOWLOCATION, 1);
+ rv = true;
+ break;
+
+ case HTTP_PUT:
+ // Disable the expect http 1.1 extension. POST and PUT default
+ // to turning this on, and I am not too sure what it means.
+ addHeader("Expect:");
+
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_INFILESIZE, bytes);
+ rv = true;
+ break;
+
+ case HTTP_POST:
+ // Disable the expect http 1.1 extension. POST and PUT default
+ // to turning this on, and I am not too sure what it means.
+ addHeader("Expect:");
+
+ // Disable the content type http header.
+ // *FIX: what should it be?
+ addHeader("Content-Type:");
+
+ // Set the handle for an http post
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_POST, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDS, NULL);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDSIZE, bytes);
+ rv = true;
+ break;
+
+ case HTTP_DELETE:
+ // Set the handle for an http post
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ rv = true;
+ break;
+
+ default:
+ llwarns << "Unhandled URLRequest action: " << mAction << llendl;
+ break;
+ }
+ if(rv)
+ {
+ if(mDetail->mHeaders)
+ {
+ curl_easy_setopt(
+ mDetail->mCurl,
+ CURLOPT_HTTPHEADER,
+ mDetail->mHeaders);
+ }
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL);
+ curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl);
+ mDetail->mNeedToRemoveEasyHandle = true;
+ }
+ return rv;
+}
+
+// static
+size_t LLURLRequest::downCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ LLURLRequest* req = (LLURLRequest*)user;
+ if(STATE_WAITING_FOR_RESPONSE == req->mState)
+ {
+ req->mState = STATE_PROCESSING_RESPONSE;
+ }
+ U32 bytes = size * nmemb;
+ if (req->mDetail->mIsBodyLimitSet)
+ {
+ if (bytes > req->mDetail->mBodyLimit)
+ {
+ bytes = req->mDetail->mBodyLimit;
+ req->mDetail->mBodyLimit = 0;
+ }
+ else
+ {
+ req->mDetail->mBodyLimit -= bytes;
+ }
+ }
+
+ req->mDetail->mResponseBuffer->append(
+ req->mDetail->mChannels.out(),
+ (U8*)data,
+ bytes);
+ return bytes;
+}
+
+// static
+size_t LLURLRequest::upCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ LLURLRequest* req = (LLURLRequest*)user;
+ S32 bytes = llmin(
+ (S32)(size * nmemb),
+ req->mDetail->mResponseBuffer->countAfter(
+ req->mDetail->mChannels.in(),
+ req->mDetail->mLastRead));
+ req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter(
+ req->mDetail->mChannels.in(),
+ req->mDetail->mLastRead,
+ (U8*)data,
+ bytes);
+ return bytes;
+}
+
+static
+size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
+{
+ const char* headerLine = (const char*)data;
+ size_t headerLen = size * nmemb;
+ LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
+
+ // FIXME: This should be a utility in llstring.h: isascii()
+ for (size_t i = 0; i < headerLen; ++i)
+ {
+ if (headerLine[i] < 0)
+ {
+ return headerLen;
+ }
+ }
+
+ size_t sep;
+ for (sep = 0; sep < headerLen && headerLine[sep] != ':'; ++sep) { }
+
+ if (sep < headerLen && complete)
+ {
+ std::string key(headerLine, sep);
+ std::string value(headerLine + sep + 1, headerLen - sep - 1);
+
+ key = utf8str_tolower(utf8str_trim(key));
+ value = utf8str_trim(value);
+
+ complete->header(key, value);
+ }
+ else
+ {
+ std::string s(headerLine, headerLen);
+
+ std::string::iterator end = s.end();
+ std::string::iterator pos1 = std::find(s.begin(), end, ' ');
+ if (pos1 != end) ++pos1;
+ std::string::iterator pos2 = std::find(pos1, end, ' ');
+ if (pos2 != end) ++pos2;
+ std::string::iterator pos3 = std::find(pos2, end, '\r');
+
+ std::string version(s.begin(), pos1);
+ std::string status(pos1, pos2);
+ std::string reason(pos2, pos3);
+
+ int statusCode = atoi(status.c_str());
+ if (statusCode > 0)
+ {
+ complete->httpStatus((U32)statusCode, reason);
+ }
+ }
+
+ return headerLen;
+}
+
+//static
+void LLURLRequest::setCertificateAuthorityFile(const std::string& file_name)
+{
+ sCAFile = file_name;
+}
+
+//static
+void LLURLRequest::setCertificateAuthorityPath(const std::string& path)
+{
+ sCAPath = path;
+}
+
+/**
+ * LLContextURLExtractor
+ */
+// virtual
+LLIOPipe::EStatus LLContextURLExtractor::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ // The destination host is in the context.
+ if(context.isUndefined() || !mRequest)
+ {
+ return STATUS_PRECONDITION_NOT_MET;
+ }
+
+ // copy in to out, since this just extract the URL and does not
+ // actually change the data.
+ LLChangeChannel change(channels.in(), channels.out());
+ std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
+
+ // find the context url
+ if(context.has(CONTEXT_DEST_URI_SD_LABEL))
+ {
+ mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL]);
+ return STATUS_DONE;
+ }
+ return STATUS_ERROR;
+}
+
+
+/**
+ * LLURLRequestComplete
+ */
+LLURLRequestComplete::LLURLRequestComplete() :
+ mRequestStatus(LLIOPipe::STATUS_ERROR)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+}
+
+// virtual
+LLURLRequestComplete::~LLURLRequestComplete()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+}
+
+//virtual
+void LLURLRequestComplete::header(const std::string& header, const std::string& value)
+{
+}
+
+//virtual
+void LLURLRequestComplete::httpStatus(U32 status, const std::string& reason)
+{
+}
+
+//virtual
+void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer)
+{
+ if(STATUS_OK == mRequestStatus)
+ {
+ response(channels, buffer);
+ }
+ else
+ {
+ noResponse();
+ }
+}
+
+//virtual
+void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer)
+{
+ llwarns << "LLURLRequestComplete::response default implementation called"
+ << llendl;
+}
+
+//virtual
+void LLURLRequestComplete::noResponse()
+{
+ llwarns << "LLURLRequestComplete::noResponse default implementation called"
+ << llendl;
+}
+
+void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mRequestStatus = status;
+}
+
+// virtual
+LLIOPipe::EStatus LLURLRequestComplete::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ complete(channels, buffer);
+ return STATUS_OK;
+}
diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h
new file mode 100644
index 0000000000..38c801cb10
--- /dev/null
+++ b/indra/llmessage/llurlrequest.h
@@ -0,0 +1,395 @@
+/**
+ * @file llurlrequest.h
+ * @author Phoenix
+ * @date 2005-04-21
+ * @brief Declaration of url based requests on pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURLREQUEST_H
+#define LL_LLURLREQUEST_H
+
+/**
+ * This file holds the declaration of useful classes for dealing with
+ * url based client requests.
+ */
+
+#include <string>
+#include "lliopipe.h"
+#include "llchainio.h"
+
+class LLURLRequestDetail;
+
+class LLURLRequestComplete;
+
+/**
+ * @class LLURLRequest
+ * @brief Class to handle url based requests.
+ * @see LLIOPipe
+ *
+ * Currently, this class is implemented on top of curl. From the
+ * vantage of a programmer using this class, you do not care so much,
+ * but it's useful to know since in order to accomplish 'non-blocking'
+ * behavior, we have to use a more expensive curl interface which can
+ * still block if the server enters a half-accepted state. It would be
+ * worth the time and effort to eventually port this to a raw client
+ * socket.
+ */
+class LLURLRequest : public LLIOPipe
+{
+public:
+ /**
+ * @brief This enumeration is for specifying the type of request.
+ */
+ enum ERequestAction
+ {
+ INVALID,
+ HTTP_GET,
+ HTTP_PUT,
+ HTTP_POST,
+ HTTP_DELETE,
+ REQUEST_ACTION_COUNT
+ };
+
+ /**
+ * @brief Constructor.
+ *
+ * @param action One of the ERequestAction enumerations.
+ */
+ LLURLRequest(ERequestAction action);
+
+ /**
+ * @brief Constructor.
+ *
+ * @param action One of the ERequestAction enumerations.
+ * @param url The url of the request. It should already be encoded.
+ */
+ LLURLRequest(ERequestAction action, const std::string& url);
+
+ /**
+ * @brief Destructor.
+ */
+ virtual ~LLURLRequest();
+
+ /* @name Instance methods
+ */
+ //@{
+ /**
+ * @brief Set the url for the request
+ *
+ * This method assumes the url is encoded appropriately for the
+ * request.
+ * The url must be set somehow before the first call to process(),
+ * or the url will not be set correctly.
+ *
+ */
+ void setURL(const std::string& url);
+
+ /**
+ * @brief Add a header to the http post.
+ *
+ * The header must be correctly formatted for HTTP requests. This
+ * provides a raw interface if you know what kind of request you
+ * will be making during construction of this instance. All
+ * required headers will be automatically constructed, so this is
+ * usually useful for encoding parameters.
+ */
+ void addHeader(const char* header);
+
+ /**
+ * @brief Check remote server certificate signed by a known root CA.
+ *
+ * Set whether request will check that remote server
+ * certificates are signed by a known root CA when using HTTPS.
+ * Use the supplied root certificate bundle if supplied, else use
+ * the standard bundle as found by libcurl and openssl.
+ */
+ void checkRootCertificate(bool check, const char* caBundle = NULL);
+
+ /**
+ * @brief Request a particular response encoding if available.
+ *
+ * This call is a shortcut for requesting a particular encoding
+ * from the server, eg, 'gzip'.
+ */
+ void requestEncoding(const char* encoding);
+
+ /**
+ * @brief Return at most size bytes of body.
+ *
+ * If the body had more bytes than this limit, they will not be
+ * returned and the connection closed. In this case, STATUS_STOP
+ * will be passed to responseStatus();
+ */
+ void setBodyLimit(U32 size);
+
+ /**
+ * @brief Set a completion callback for this URLRequest.
+ *
+ * The callback is added to this URLRequet's pump when either the
+ * entire buffer is known or an error like timeout or connection
+ * refused has happened. In the case of a complete transfer, this
+ * object builds a response chain such that the callback and the
+ * next process consumer get to read the output.
+ *
+ * This setup is a little fragile since the url request consumer
+ * might not just read the data - it may do a channel change,
+ * which invalidates the input to the callback, but it works well
+ * in practice.
+ */
+ void setCallback(LLURLRequestComplete* callback);
+ //@}
+
+ /**
+ * @ brief Set certificate authority file used to verify HTTPS certs.
+ */
+ static void setCertificateAuthorityFile(const std::string& file_name);
+
+ /**
+ * @ brief Set certificate authority path used to verify HTTPS certs.
+ */
+ static void setCertificateAuthorityPath(const std::string& path);
+
+ /* @name LLIOPipe virtual implementations
+ */
+public:
+ /**
+ * @brief Give this pipe a chance to handle a generated error
+ */
+ virtual EStatus handleError(EStatus status, LLPumpIO* pump);
+
+protected:
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ enum EState
+ {
+ STATE_INITIALIZED,
+ STATE_WAITING_FOR_RESPONSE,
+ STATE_PROCESSING_RESPONSE,
+ STATE_HAVE_RESPONSE,
+ };
+ EState mState;
+ ERequestAction mAction;
+ LLURLRequestDetail* mDetail;
+ LLIOPipe::ptr_t mCompletionCallback;
+
+private:
+ /**
+ * @brief Initialize the object. Called during construction.
+ */
+ void initialize();
+
+ /**
+ * @brief Handle action specific url request configuration.
+ *
+ * @return Returns true if this is configured.
+ */
+ bool configure();
+
+ /**
+ * @brief Download callback method.
+ */
+ static size_t downCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user);
+
+ /**
+ * @brief Upload callback method.
+ */
+ static size_t upCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user);
+
+ /**
+ * @brief Declaration of unimplemented method to prevent copy
+ * construction.
+ */
+ LLURLRequest(const LLURLRequest&);
+};
+
+
+/**
+ * @class LLContextURLExtractor
+ * @brief This class unpacks the url out of a agent usher service so
+ * it can be packed into a LLURLRequest object.
+ * @see LLIOPipe
+ *
+ * This class assumes that the context is a map that contains an entry
+ * named CONTEXT_DEST_URI_SD_LABEL.
+ */
+class LLContextURLExtractor : public LLIOPipe
+{
+public:
+ LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {}
+ ~LLContextURLExtractor() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLURLRequest* mRequest;
+};
+
+
+/**
+ * @class LLURLRequestComplete
+ * @brief Class which can optionally be used with an LLURLRequest to
+ * get notification when the url request is complete.
+ */
+class LLURLRequestComplete : public LLIOPipe
+{
+public:
+
+ virtual void header(const std::string& header, const std::string& value);
+ ///< Called once for each header received, prior to httpStatus
+
+ virtual void httpStatus(U32 status, const std::string& reason);
+ ///< Always called on request completion, prior to complete
+
+ virtual void complete(
+ const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer);
+
+ /**
+ * @brief This method is called when we got a valid response.
+ *
+ * It is up to class implementers to do something useful here.
+ */
+ virtual void response(
+ const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer);
+
+ /**
+ * @brief This method is called if there was no response.
+ *
+ * It is up to class implementers to do something useful here.
+ */
+ virtual void noResponse();
+
+ /**
+ * @brief This method will be called by the LLURLRequest object.
+ *
+ * If this is set to STATUS_OK or STATUS_STOP, then the transfer
+ * is asssumed to have worked. This will lead to calling response()
+ * on the next call to process(). Otherwise, this object will call
+ * noResponse() on the next call to process.
+ * @param status The status of the URLRequest.
+ */
+ void responseStatus(EStatus status);
+
+ // constructor & destructor.
+ LLURLRequestComplete();
+ virtual ~LLURLRequestComplete();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+ // value to note if we actually got the response. This value
+ // depends on correct useage from the LLURLRequest instance.
+ EStatus mRequestStatus;
+};
+
+
+/**
+ * @class LLURLRequestClientFactory
+ * @brief Template class to build url request based client chains
+ *
+ * This class eases construction of a basic sd rpc client. Here is an
+ * example of it's use:
+ * <code>
+ * class LLUsefulService : public LLService { ... }<br>
+ * LLService::registerCreator(<br>
+ * "useful",<br>
+ * LLService::creator_t(new LLURLRequestClientFactory<LLUsefulService>))<br>
+ * </code>
+ *
+ * This class should work, but I never got around to using/testing it.
+ *
+ */
+#if 0
+template<class Client>
+class LLURLRequestClientFactory : public LLChainIOFactory
+{
+public:
+ LLURLRequestClientFactory(LLURLRequest::ERequestAction action) {}
+ LLURLRequestClientFactory(
+ LLURLRequest::ERequestAction action,
+ const std::string& fixed_url) :
+ mAction(action),
+ mURL(fixed_url)
+ {
+ }
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLURLRequestClientFactory::build" << llendl;
+ LLIOPipe::ptr_t service(new Client);
+ chain.push_back(service);
+ LLURLRequest* http(new LLURLRequest(mAction));
+ LLIOPipe::ptr_t http_pipe(http);
+ // *FIX: how do we know the content type?
+ //http->addHeader("Content-Type: text/llsd");
+ if(mURL.empty())
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
+ }
+ else
+ {
+ http->setURL(mURL);
+ }
+ chain.push_back(http_pipe);
+ chain.push_back(service);
+ return true;
+ }
+
+protected:
+ LLURLRequest::ERequestAction mAction;
+ std::string mURL;
+};
+#endif
+
+/**
+ * External constants
+ */
+extern const std::string CONTEXT_DEST_URI_SD_LABEL;
+
+#endif // LL_LLURLREQUEST_H
diff --git a/indra/llmessage/lluseroperation.cpp b/indra/llmessage/lluseroperation.cpp
new file mode 100644
index 0000000000..f7506c955c
--- /dev/null
+++ b/indra/llmessage/lluseroperation.cpp
@@ -0,0 +1,161 @@
+/**
+ * @file lluseroperation.cpp
+ * @brief LLUserOperation class definition.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lluseroperation.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLUserOperationMgr* gUserOperationMgr = NULL;
+
+///----------------------------------------------------------------------------
+/// Class LLUserOperation
+///----------------------------------------------------------------------------
+
+LLUserOperation::LLUserOperation(const LLUUID& agent_id)
+: mAgentID(agent_id),
+ mTimer()
+{
+ mTransactionID.generate();
+}
+
+LLUserOperation::LLUserOperation(const LLUUID& agent_id,
+ const LLUUID& transaction_id) :
+ mAgentID(agent_id),
+ mTransactionID(transaction_id),
+ mTimer()
+{
+}
+
+// protected constructor which is used by base classes that determine
+// transaction, agent, et. after construction.
+LLUserOperation::LLUserOperation() :
+ mTimer()
+{
+}
+
+LLUserOperation::~LLUserOperation()
+{
+}
+
+
+BOOL LLUserOperation::isExpired()
+{
+ const F32 EXPIRE_TIME_SECS = 10.f;
+ return mTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS;
+}
+
+void LLUserOperation::expire()
+{
+ // by default, do do anything.
+}
+
+///----------------------------------------------------------------------------
+/// Class LLUserOperationMgr
+///----------------------------------------------------------------------------
+
+LLUserOperationMgr::LLUserOperationMgr()
+{
+}
+
+
+LLUserOperationMgr::~LLUserOperationMgr()
+{
+ if (mUserOperationList.size() > 0)
+ {
+ llwarns << "Exiting with user operations pending." << llendl;
+ }
+}
+
+
+void LLUserOperationMgr::addOperation(LLUserOperation* op)
+{
+ if(!op)
+ {
+ llwarns << "Tried to add null op" << llendl;
+ return;
+ }
+ LLUUID id = op->getTransactionID();
+ llassert(mUserOperationList.count(id) == 0);
+ mUserOperationList[id] = op;
+}
+
+
+LLUserOperation* LLUserOperationMgr::findOperation(const LLUUID& tid)
+{
+ user_operation_list_t::iterator iter = mUserOperationList.find(tid);
+ if (iter != mUserOperationList.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+
+BOOL LLUserOperationMgr::deleteOperation(LLUserOperation* op)
+{
+ size_t rv = 0;
+ if(op)
+ {
+ LLUUID id = op->getTransactionID();
+ rv = mUserOperationList.erase(id);
+ delete op;
+ op = NULL;
+ }
+ return rv ? TRUE : FALSE;
+}
+
+void LLUserOperationMgr::deleteExpiredOperations()
+{
+ const S32 MAX_OPS_CONSIDERED = 2000;
+ S32 ops_left = MAX_OPS_CONSIDERED;
+ LLUserOperation* op = NULL;
+ user_operation_list_t::iterator it;
+ if(mLastOperationConsidered.isNull())
+ {
+ it = mUserOperationList.begin();
+ }
+ else
+ {
+ it = mUserOperationList.lower_bound(mLastOperationConsidered);
+ }
+ while((ops_left--) && (it != mUserOperationList.end()))
+ {
+ op = (*it).second;
+ if(op && op->isExpired())
+ {
+ lldebugs << "expiring: " << (*it).first << llendl;
+ op->expire();
+ mUserOperationList.erase(it++);
+ delete op;
+ }
+ else if(op)
+ {
+ ++it;
+ }
+ else
+ {
+ mUserOperationList.erase(it++);
+ }
+ }
+ if(it != mUserOperationList.end())
+ {
+ mLastOperationConsidered = (*it).first;
+ }
+ else
+ {
+ mLastOperationConsidered.setNull();
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/llmessage/lluseroperation.h b/indra/llmessage/lluseroperation.h
new file mode 100644
index 0000000000..ac6590abf9
--- /dev/null
+++ b/indra/llmessage/lluseroperation.h
@@ -0,0 +1,75 @@
+/**
+ * @file lluseroperation.h
+ * @brief LLUserOperation class header file - used for message based
+ * transaction. For example, money transactions.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUSEROPERATION_H
+#define LL_LLUSEROPERATION_H
+
+#include "lluuid.h"
+#include "llframetimer.h"
+
+#include <map>
+
+class LLUserOperation
+{
+public:
+ LLUserOperation(const LLUUID& agent_id);
+ LLUserOperation(const LLUUID& agent_id, const LLUUID& transaction_id);
+ virtual ~LLUserOperation();
+
+ const LLUUID& getTransactionID() const { return mTransactionID; }
+ const LLUUID& getAgentID() const { return mAgentID; }
+
+ // Operation never got necessary data, so expired
+ virtual BOOL isExpired();
+
+ // Send request to the dataserver
+ virtual void sendRequest() = 0;
+
+ // Run the operation. This will only be called in the case of an
+ // actual success or failure of the operation.
+ virtual BOOL execute(BOOL transaction_success) = 0;
+
+ // This method is called when the user op has expired, and is
+ // about to be deleted by the manager. This gives the user op the
+ // ability to nack someone when the user op is never evaluated
+ virtual void expire();
+
+protected:
+ LLUserOperation();
+
+protected:
+ LLUUID mAgentID;
+ LLUUID mTransactionID;
+ LLFrameTimer mTimer;
+};
+
+
+class LLUserOperationMgr
+{
+public:
+ LLUserOperationMgr();
+ ~LLUserOperationMgr();
+
+ void addOperation(LLUserOperation* op);
+ LLUserOperation* findOperation(const LLUUID& transaction_id);
+ BOOL deleteOperation(LLUserOperation* op);
+
+ // Call this method every once in a while to clean up old
+ // transactions.
+ void deleteExpiredOperations();
+
+private:
+ typedef std::map<LLUUID, LLUserOperation*> user_operation_list_t;
+ user_operation_list_t mUserOperationList;
+ LLUUID mLastOperationConsidered;
+};
+
+extern LLUserOperationMgr* gUserOperationMgr;
+
+#endif // LL_LLUSEROPERATION_H
diff --git a/indra/llmessage/llvehicleparams.h b/indra/llmessage/llvehicleparams.h
new file mode 100644
index 0000000000..bd49d3f6ae
--- /dev/null
+++ b/indra/llmessage/llvehicleparams.h
@@ -0,0 +1,105 @@
+/**
+ * @file llvehicleparams.h
+ * @brief For parameter names that must be shared between the
+ * scripting language and the LLVehicleAction class on the simulator.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VEHICLE_PARAMS_H
+#define LL_VEHICLE_PARAMS_H
+
+/**
+ * The idea is that the various parameters that control vehicle
+ * behavior can be tweeked by name using general-purpose script calls.
+ */
+
+typedef enum e_vehicle_param
+{
+ VEHICLE_TYPE_NONE, // TYPE_0
+ VEHICLE_TYPE_SLED,
+ VEHICLE_TYPE_CAR,
+ VEHICLE_TYPE_BOAT,
+ VEHICLE_TYPE_AIRPLANE,
+ VEHICLE_TYPE_BALLOON, // TYPE_5
+ VEHICLE_TYPE_6,
+ VEHICLE_TYPE_7,
+ VEHICLE_TYPE_8,
+ VEHICLE_TYPE_9,
+ VEHICLE_TYPE_10,
+ VEHICLE_TYPE_11,
+ VEHICLE_TYPE_12,
+ VEHICLE_TYPE_13,
+ VEHICLE_TYPE_14,
+ VEHICLE_TYPE_15,
+
+ // vector parameters
+ VEHICLE_LINEAR_FRICTION_TIMESCALE,
+ VEHICLE_ANGULAR_FRICTION_TIMESCALE,
+ VEHICLE_LINEAR_MOTOR_DIRECTION,
+ VEHICLE_ANGULAR_MOTOR_DIRECTION,
+ VEHICLE_LINEAR_MOTOR_OFFSET,
+ VEHICLE_VECTOR_PARAM_5,
+ VEHICLE_VECTOR_PARAM_6,
+ VEHICLE_VECTOR_PARAM_7,
+
+ // floating point parameters
+ VEHICLE_HOVER_HEIGHT,
+ VEHICLE_HOVER_EFFICIENCY,
+ VEHICLE_HOVER_TIMESCALE,
+ VEHICLE_BUOYANCY,
+
+ VEHICLE_LINEAR_DEFLECTION_EFFICIENCY,
+ VEHICLE_LINEAR_DEFLECTION_TIMESCALE,
+ VEHICLE_LINEAR_MOTOR_TIMESCALE,
+ VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE,
+
+ VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY,
+ VEHICLE_ANGULAR_DEFLECTION_TIMESCALE,
+ VEHICLE_ANGULAR_MOTOR_TIMESCALE,
+ VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,
+
+ VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY,
+ VEHICLE_VERTICAL_ATTRACTION_TIMESCALE,
+
+ VEHICLE_BANKING_EFFICIENCY,
+ VEHICLE_BANKING_MIX,
+ VEHICLE_BANKING_TIMESCALE,
+
+ VEHICLE_FLOAT_PARAM_17,
+ VEHICLE_FLOAT_PARAM_18,
+ VEHICLE_FLOAT_PARAM_19,
+
+ // rotation parameters
+ VEHICLE_REFERENCE_FRAME,
+ VEHICLE_ROTATION_PARAM_1,
+ VEHICLE_ROTATION_PARAM_2,
+ VEHICLE_ROTATION_PARAM_3,
+
+} EVehicleParam;
+
+
+// some flags that effect how the vehicle moves
+
+// zeros world-z component of linear deflection
+const U32 VEHICLE_FLAG_NO_DEFLECTION_UP = 1 << 0;
+
+// spring-loads roll only
+const U32 VEHICLE_FLAG_LIMIT_ROLL_ONLY = 1 << 1;
+
+// hover flags
+const U32 VEHICLE_FLAG_HOVER_WATER_ONLY = 1 << 2;
+const U32 VEHICLE_FLAG_HOVER_TERRAIN_ONLY = 1 << 3;
+const U32 VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT = 1 << 4;
+const U32 VEHICLE_FLAG_HOVER_UP_ONLY = 1 << 5;
+
+// caps world-z component of linear motor to prevent
+// climbing up into the sky
+const U32 VEHICLE_FLAG_LIMIT_MOTOR_UP = 1 << 6;
+
+const U32 VEHICLE_FLAG_MOUSELOOK_STEER = 1 << 7;
+const U32 VEHICLE_FLAG_MOUSELOOK_BANK = 1 << 8;
+const U32 VEHICLE_FLAG_CAMERA_DECOUPLED = 1 << 9;
+
+#endif
diff --git a/indra/llmessage/llxfer.cpp b/indra/llmessage/llxfer.cpp
new file mode 100644
index 0000000000..154696eb4e
--- /dev/null
+++ b/indra/llmessage/llxfer.cpp
@@ -0,0 +1,350 @@
+/**
+ * @file llxfer.cpp
+ * @brief implementation of LLXfer class for a single xfer.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfer.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "u64.h"
+
+//number of bytes sent in each message
+const U32 LL_XFER_CHUNK_SIZE = 1000;
+
+const U32 LLXfer::XFER_FILE = 1;
+const U32 LLXfer::XFER_VFILE = 2;
+const U32 LLXfer::XFER_MEM = 3;
+
+///////////////////////////////////////////////////////////
+
+LLXfer::LLXfer (S32 chunk_size)
+{
+ init(chunk_size);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer::~LLXfer ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::init (S32 chunk_size)
+{
+ mID = 0;
+
+ mPacketNum = -1; // there's a preincrement before sending the zeroth packet
+ mXferSize = 0;
+
+ mStatus = e_LL_XFER_UNINITIALIZED;
+ mNext = NULL;
+ mWaitingForACK = FALSE;
+
+ mCallback = NULL;
+ mCallbackDataHandle = NULL;
+
+ mBufferContainsEOF = FALSE;
+ mBuffer = NULL;
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+
+ mRetries = 0;
+
+ if (chunk_size < 1)
+ {
+ chunk_size = LL_XFER_CHUNK_SIZE;
+ }
+ mChunkSize = chunk_size;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::free ()
+{
+ if (mBuffer)
+ {
+ delete[] mBuffer;
+ mBuffer = NULL;
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ llwarns << "undifferentiated LLXfer::startSend for " << getName() << llendl;
+ return (-1);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::setXferSize (S32 xfer_size)
+{
+ mXferSize = xfer_size;
+// cout << "starting transfer of size: " << xfer_size << endl;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::startDownload()
+{
+ llwarns << "undifferentiated LLXfer::startDownload for " << getName()
+ << llendl;
+ return (-1);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::receiveData (char *datap, S32 data_size)
+{
+ S32 retval = 0;
+
+ if (((S32) mBufferLength + data_size) > getMaxBufferSize())
+ {
+ retval = flush();
+ }
+
+ if (!retval)
+ {
+ if (datap != NULL)
+ {
+ memcpy(&mBuffer[mBufferLength],datap,data_size);
+ mBufferLength += data_size;
+ }
+ else
+ {
+ llerrs << "NULL data passed in receiveData" << llendl;
+ }
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::flush()
+{
+ // only files have somewhere to flush to
+ // if we get called with a flush it means we've blown past our
+ // allocated buffer size
+
+ return (-1);
+}
+
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::suck(S32 start_position)
+{
+ llwarns << "Attempted to send a packet outside the buffer bounds in LLXfer::suck()" << llendl;
+ return (-1);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::sendPacket(S32 packet_num)
+{
+ char fdata_buf[LL_XFER_LARGE_PAYLOAD+4]; /* Flawfinder: ignore */
+ S32 fdata_size = mChunkSize;
+ BOOL last_packet = FALSE;
+ S32 num_copy = 0;
+
+ // if the desired packet is not in our current buffered excerpt from the file. . .
+ if (((U32)packet_num*fdata_size < mBufferStartOffset)
+ || ((U32)llmin((U32)mXferSize,(U32)((U32)(packet_num+1)*fdata_size)) > mBufferStartOffset + mBufferLength))
+
+ {
+ if (suck(packet_num*fdata_size)) // returns non-zero on failure
+ {
+ abort(LL_ERR_EOF);
+ return;
+ }
+ }
+
+ S32 desired_read_position = 0;
+
+ desired_read_position = packet_num * fdata_size - mBufferStartOffset;
+
+ fdata_size = llmin((S32)mBufferLength-desired_read_position, mChunkSize);
+
+ if (fdata_size < 0)
+ {
+ llwarns << "negative data size in xfer send, aborting" << llendl;
+ abort(LL_ERR_EOF);
+ return;
+ }
+
+ if (((U32)(desired_read_position + fdata_size) >= (U32)mBufferLength) && (mBufferContainsEOF))
+ {
+ last_packet = TRUE;
+ }
+
+ if (packet_num)
+ {
+ num_copy = llmin(fdata_size, (S32)sizeof(fdata_buf));
+ num_copy = llmin(num_copy, (S32)(sizeof(mBuffer) - desired_read_position));
+ if (num_copy > 0)
+ {
+ memcpy(fdata_buf,&mBuffer[desired_read_position],num_copy);
+ }
+ }
+ else // if we're the first packet, encode size as an additional S32 at start of data
+ {
+ num_copy = llmin(fdata_size, (S32)(sizeof(fdata_buf)-sizeof(S32)));
+ num_copy = llmin(num_copy, (S32)(sizeof(mBuffer) - desired_read_position));
+ if (num_copy > 0)
+ {
+ memcpy(fdata_buf+sizeof(S32),&mBuffer[desired_read_position],fdata_size);
+ }
+ fdata_size += sizeof(S32);
+ htonmemcpy(fdata_buf,&mXferSize, MVT_S32, sizeof(S32));
+ }
+
+ S32 encoded_packetnum = encodePacketNum(packet_num,last_packet);
+
+ if (fdata_size)
+ {
+ // send the packet
+ gMessageSystem->newMessageFast(_PREHASH_SendXferPacket);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addU32Fast(_PREHASH_Packet, encoded_packetnum);
+
+ gMessageSystem->nextBlockFast(_PREHASH_DataPacket);
+ gMessageSystem->addBinaryDataFast(_PREHASH_Data, &fdata_buf,fdata_size);
+
+ gMessageSystem->sendMessage(mRemoteHost);
+
+ ACKTimer.reset();
+ mWaitingForACK = TRUE;
+ }
+ if (last_packet)
+ {
+ mStatus = e_LL_XFER_COMPLETE;
+ }
+ else
+ {
+ mStatus = e_LL_XFER_IN_PROGRESS;
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::sendNextPacket()
+{
+ mRetries = 0;
+ sendPacket(++mPacketNum);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::resendLastPacket()
+{
+ mRetries++;
+ sendPacket(mPacketNum);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::processEOF()
+{
+ S32 retval = 0;
+
+ mStatus = e_LL_XFER_COMPLETE;
+
+ if (LL_ERR_NOERR == mCallbackResult)
+ {
+ llinfos << "xfer from " << mRemoteHost << " complete: " << getName()
+ << llendl;
+ }
+ else
+ {
+ llinfos << "xfer from " << mRemoteHost << " failed, code "
+ << mCallbackResult << ": " << getName() << llendl;
+ }
+
+ if (mCallback)
+ {
+ mCallback(mCallbackDataHandle,mCallbackResult);
+ }
+
+ return(retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::encodePacketNum(S32 packet_num, BOOL is_EOF)
+{
+ if (is_EOF)
+ {
+ packet_num |= 0x80000000;
+ }
+ return packet_num;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::abort (S32 result_code)
+{
+ mCallbackResult = result_code;
+
+ llinfos << "Aborting xfer from " << mRemoteHost << " named " << getName()
+ << " - error: " << result_code << llendl;
+
+ gMessageSystem->newMessageFast(_PREHASH_AbortXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addS32Fast(_PREHASH_Result, result_code);
+
+ gMessageSystem->sendMessage(mRemoteHost);
+
+ mStatus = e_LL_XFER_ABORTED;
+}
+
+
+///////////////////////////////////////////////////////////
+
+const char * LLXfer::getName()
+{
+ static char tmp_str[256]; /* Flawfinder: ignore */
+
+ return (U64_to_str(mID, tmp_str, sizeof(tmp_str)));
+}
+
+///////////////////////////////////////////////////////////
+
+U32 LLXfer::getXferTypeTag()
+{
+ return 0;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::getMaxBufferSize ()
+{
+ return(mXferSize);
+}
+
+
+std::ostream& operator<< (std::ostream& os, LLXfer &hh)
+{
+ os << hh.getName() ;
+ return os;
+}
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llxfer.h b/indra/llmessage/llxfer.h
new file mode 100644
index 0000000000..3fd12df7a5
--- /dev/null
+++ b/indra/llmessage/llxfer.h
@@ -0,0 +1,98 @@
+/**
+ * @file llxfer.h
+ * @brief definition of LLXfer class for a single xfer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_H
+#define LL_LLXFER_H
+
+#include "message.h"
+#include "lltimer.h"
+
+const S32 LL_XFER_LARGE_PAYLOAD = 7680;
+
+typedef enum ELLXferStatus {
+ e_LL_XFER_UNINITIALIZED,
+ e_LL_XFER_REGISTERED, // a buffer which has been registered as available for a request
+ e_LL_XFER_PENDING, // a transfer which has been requested but is waiting for a free slot
+ e_LL_XFER_IN_PROGRESS,
+ e_LL_XFER_COMPLETE,
+ e_LL_XFER_ABORTED,
+ e_LL_XFER_NONE
+} ELLXferStatus;
+
+class LLXfer
+{
+ private:
+ protected:
+ S32 mChunkSize;
+
+ public:
+ LLXfer *mNext;
+ U64 mID;
+ S32 mPacketNum;
+
+ LLHost mRemoteHost;
+ S32 mXferSize;
+
+ char *mBuffer;
+ U32 mBufferLength;
+ U32 mBufferStartOffset;
+ BOOL mBufferContainsEOF;
+
+ ELLXferStatus mStatus;
+
+ BOOL mWaitingForACK;
+
+ void (*mCallback)(void **,S32);
+ void **mCallbackDataHandle;
+ S32 mCallbackResult;
+
+ LLTimer ACKTimer;
+ S32 mRetries;
+
+ static const U32 XFER_FILE;
+ static const U32 XFER_VFILE;
+ static const U32 XFER_MEM;
+
+ private:
+ protected:
+ public:
+ LLXfer (S32 chunk_size);
+ virtual ~LLXfer();
+
+ void init(S32 chunk_size);
+ virtual void free();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+ virtual void sendPacket(S32 packet_num);
+ virtual void sendNextPacket();
+ virtual void resendLastPacket();
+ virtual S32 processEOF();
+ virtual S32 startDownload();
+ virtual S32 receiveData (char *datap, S32 data_size);
+ virtual void abort(S32);
+
+ virtual S32 suck(S32 start_position);
+ virtual S32 flush();
+
+ virtual S32 encodePacketNum(S32 packet_num, BOOL is_eof);
+ virtual void setXferSize (S32 data_size);
+ virtual S32 getMaxBufferSize();
+
+ virtual const char *getName();
+
+ virtual U32 getXferTypeTag();
+
+ friend std::ostream& operator<< (std::ostream& os, LLXfer &hh);
+
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfer_file.cpp b/indra/llmessage/llxfer_file.cpp
new file mode 100644
index 0000000000..da72467c76
--- /dev/null
+++ b/indra/llmessage/llxfer_file.cpp
@@ -0,0 +1,416 @@
+/**
+ * @file llxfer_file.cpp
+ * @brief implementation of LLXfer_File class for a single xfer (file)
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if !LL_WINDOWS
+#include <errno.h>
+#include <unistd.h>
+#endif
+
+#include "llxfer_file.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "llstring.h"
+#include "lldir.h"
+
+// size of chunks read from/written to disk
+const U32 LL_MAX_XFER_FILE_BUFFER = 65536;
+
+// local function to copy a file
+S32 copy_file(const char* from, const char* to);
+
+///////////////////////////////////////////////////////////
+
+LLXfer_File::LLXfer_File (S32 chunk_size)
+: LLXfer(chunk_size)
+{
+ init(LLString::null, FALSE, chunk_size);
+}
+
+LLXfer_File::LLXfer_File (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size)
+: LLXfer(chunk_size)
+{
+ init(local_filename, delete_local_on_completion, chunk_size);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer_File::~LLXfer_File ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_File::init (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size)
+{
+
+ mFp = NULL;
+ mLocalFilename[0] = 0;
+ mRemoteFilename[0] = 0;
+ mRemotePath = LL_PATH_NONE;
+ mTempFilename[0] = 0;
+ mDeleteLocalOnCompletion = FALSE;
+ mDeleteRemoteOnCompletion = FALSE;
+
+ if (!local_filename.empty())
+ {
+ strncpy(mLocalFilename, local_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+
+ // You can only automatically delete .tmp file as a safeguard against nasty messages.
+ mDeleteLocalOnCompletion = (delete_local_on_completion && (strstr(mLocalFilename,".tmp") == &mLocalFilename[strlen(mLocalFilename)-4])); /* Flawfinder : ignore */
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_File::free ()
+{
+ if (mFp)
+ {
+ fclose(mFp);
+ mFp = NULL;
+ }
+
+ LLFile::remove(mTempFilename);
+
+ if (mDeleteLocalOnCompletion)
+ {
+ lldebugs << "Removing file: " << mLocalFilename << llendl;
+ LLFile::remove(mLocalFilename);
+ }
+ else
+ {
+ lldebugs << "Keeping local file: " << mLocalFilename << llendl;
+ }
+
+ LLXfer::free();
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::initializeRequest(U64 xfer_id,
+ const LLString& local_filename,
+ const LLString& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32),
+ void** user_data)
+{
+ S32 retval = 0; // presume success
+
+ mID = xfer_id;
+ strncpy(mLocalFilename, local_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+ strncpy(mRemoteFilename,remote_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+ mRemotePath = remote_path;
+ mRemoteHost = remote_host;
+ mDeleteRemoteOnCompletion = delete_remote_on_completion;
+
+ snprintf(mTempFilename, sizeof(mTempFilename), "%s",gDirUtilp->getTempFilename().c_str()); /* Flawfinder : ignore */
+
+ mCallback = callback;
+ mCallbackDataHandle = user_data;
+ mCallbackResult = LL_ERR_NOERR;
+
+ llinfos << "Requesting xfer from " << remote_host << " for file: " << mLocalFilename << llendl;
+
+ if (mBuffer)
+ {
+ delete(mBuffer);
+ mBuffer = NULL;
+ }
+
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+ mBufferLength = 0;
+
+ mPacketNum = 0;
+
+ mStatus = e_LL_XFER_PENDING;
+ return retval;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::startDownload()
+{
+ S32 retval = 0; // presume success
+ mFp = LLFile::fopen(mTempFilename,"w+b"); /* Flawfinder : ignore */
+ if (mFp)
+ {
+ fclose(mFp);
+ mFp = NULL;
+
+ gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addStringFast(_PREHASH_Filename, mRemoteFilename);
+ gMessageSystem->addU8("FilePath", (U8) mRemotePath);
+ gMessageSystem->addBOOL("DeleteOnCompletion", mDeleteRemoteOnCompletion);
+ gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
+ gMessageSystem->addUUIDFast(_PREHASH_VFileID, LLUUID::null);
+ gMessageSystem->addS16Fast(_PREHASH_VFileType, -1);
+
+ gMessageSystem->sendReliable(mRemoteHost);
+ mStatus = e_LL_XFER_IN_PROGRESS;
+ }
+ else
+ {
+ llwarns << "Couldn't create file to be received!" << llendl;
+ retval = -1;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ S32 retval = LL_ERR_NOERR; // presume success
+
+ mRemoteHost = remote_host;
+ mID = xfer_id;
+ mPacketNum = -1;
+
+// cout << "Sending file: " << mLocalFilename << endl;
+
+ delete [] mBuffer;
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+
+ mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */
+ if (mFp)
+ {
+ fseek(mFp,0,SEEK_END);
+
+ S32 file_size = ftell(mFp);
+ if (file_size <= 0)
+ {
+ return LL_ERR_FILE_EMPTY;
+ }
+ setXferSize(file_size);
+
+ fseek(mFp,0,SEEK_SET);
+ }
+ else
+ {
+ llinfos << "Warning: " << mLocalFilename << " not found." << llendl;
+ return (LL_ERR_FILE_NOT_FOUND);
+ }
+
+ mStatus = e_LL_XFER_PENDING;
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::getMaxBufferSize ()
+{
+ return(LL_MAX_XFER_FILE_BUFFER);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::suck(S32 start_position)
+{
+ S32 retval = 0;
+
+ if (mFp)
+ {
+ // grab a buffer from the right place in the file
+ fseek (mFp,start_position,SEEK_SET);
+
+ mBufferLength = (U32)fread(mBuffer,1,LL_MAX_XFER_FILE_BUFFER,mFp);
+ mBufferStartOffset = start_position;
+
+ if (feof(mFp))
+ {
+ mBufferContainsEOF = TRUE;
+ }
+ else
+ {
+ mBufferContainsEOF = FALSE;
+ }
+ }
+ else
+ {
+ retval = -1;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::flush()
+{
+ S32 retval = 0;
+ if (mBufferLength)
+ {
+ if (mFp)
+ {
+ llerrs << "Overwriting open file pointer!" << llendl;
+ }
+ mFp = LLFile::fopen(mTempFilename,"a+b"); /* Flawfinder : ignore */
+
+ if (mFp)
+ {
+ fwrite(mBuffer,1,mBufferLength,mFp);
+// llinfos << "******* wrote " << mBufferLength << " bytes of file xfer" << llendl;
+ fclose(mFp);
+ mFp = NULL;
+
+ mBufferLength = 0;
+ }
+ else
+ {
+ llwarns << "LLXfer_File::flush() unable to open " << mTempFilename << " for writing!" << llendl;
+ retval = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::processEOF()
+{
+ S32 retval = 0;
+ mStatus = e_LL_XFER_COMPLETE;
+
+ S32 flushval = flush();
+
+ // If we have no other errors, our error becomes the error generated by
+ // flush.
+ if (!mCallbackResult)
+ {
+ mCallbackResult = flushval;
+ }
+
+ LLFile::remove(mLocalFilename);
+
+ if (!mCallbackResult)
+ {
+ if (LLFile::rename(mTempFilename,mLocalFilename))
+ {
+#if !LL_WINDOWS
+ S32 error_number = errno;
+ llinfos << "Rename failure (" << error_number << ") - "
+ << mTempFilename << " to " << mLocalFilename << llendl;
+ if(EXDEV == error_number)
+ {
+ if(copy_file(mTempFilename, mLocalFilename) == 0)
+ {
+ llinfos << "Rename across mounts; copying+unlinking the file instead." << llendl;
+ unlink(mTempFilename);
+ }
+ else
+ {
+ llwarns << "Copy failure - " << mTempFilename << " to "
+ << mLocalFilename << llendl;
+ }
+ }
+ else
+ {
+ //FILE* fp = LLFile::fopen(mTempFilename, "r");
+ //llwarns << "File " << mTempFilename << " does "
+ // << (!fp ? "not" : "" ) << " exit." << llendl;
+ //if(fp) fclose(fp);
+ //fp = LLFile::fopen(mLocalFilename, "r");
+ //llwarns << "File " << mLocalFilename << " does "
+ // << (!fp ? "not" : "" ) << " exit." << llendl;
+ //if(fp) fclose(fp);
+ llwarns << "Rename fatally failed, can only handle EXDEV ("
+ << EXDEV << ")" << llendl;
+ }
+#else
+ llwarns << "Rename failure - " << mTempFilename << " to "
+ << mLocalFilename << llendl;
+#endif
+ }
+ }
+
+ if (mFp)
+ {
+ fclose(mFp);
+ mFp = NULL;
+ }
+
+ retval = LLXfer::processEOF();
+
+ return(retval);
+}
+
+///////////////////////////////////////////////////////////
+
+BOOL LLXfer_File::matchesLocalFilename(const LLString& filename)
+{
+ return (filename == mLocalFilename);
+}
+
+///////////////////////////////////////////////////////////
+
+BOOL LLXfer_File::matchesRemoteFilename(const LLString& filename, ELLPath remote_path)
+{
+ return ((filename == mRemoteFilename) && (remote_path == mRemotePath));
+}
+
+
+///////////////////////////////////////////////////////////
+
+const char * LLXfer_File::getName()
+{
+ return (mLocalFilename);
+}
+
+///////////////////////////////////////////////////////////
+
+// hacky - doesn't matter what this is
+// as long as it's different from the other classes
+U32 LLXfer_File::getXferTypeTag()
+{
+ return LLXfer::XFER_FILE;
+}
+
+///////////////////////////////////////////////////////////
+
+#if !LL_WINDOWS
+
+// This is really close to, but not quite a general purpose copy
+// function. It does not really spam enough information, but is useful
+// for this cpp file, because this should never be called in a
+// production environment.
+S32 copy_file(const char* from, const char* to)
+{
+ S32 rv = 0;
+ FILE* in = LLFile::fopen(from, "rb");
+ FILE* out = LLFile::fopen(to, "wb");
+ if(in && out)
+ {
+ S32 read = 0;
+ const S32 COPY_BUFFER_SIZE = 16384;
+ U8 buffer[COPY_BUFFER_SIZE];
+ while(((read = fread(buffer, 1, sizeof(buffer), in)) > 0)
+ && (fwrite(buffer, 1, read, out) == (U32)read)); /* Flawfinder : ignore */
+ if(ferror(in) || ferror(out)) rv = -2;
+ }
+ else
+ {
+ rv = -1;
+ }
+ if(in) fclose(in);
+ if(out) fclose(out);
+ return rv;
+}
+#endif
diff --git a/indra/llmessage/llxfer_file.h b/indra/llmessage/llxfer_file.h
new file mode 100644
index 0000000000..b3d1ccbfbe
--- /dev/null
+++ b/indra/llmessage/llxfer_file.h
@@ -0,0 +1,68 @@
+/**
+ * @file llxfer_file.h
+ * @brief definition of LLXfer_File class for a single xfer_file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_FILE_H
+#define LL_LLXFER_FILE_H
+
+#include <stdio.h>
+
+#include "llxfer.h"
+#include "lldir.h"
+
+class LLXfer_File : public LLXfer
+{
+ protected:
+ FILE *mFp;
+ char mLocalFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+ char mRemoteFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+ ELLPath mRemotePath;
+ char mTempFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+
+ BOOL mDeleteLocalOnCompletion;
+ BOOL mDeleteRemoteOnCompletion;
+
+ public:
+ LLXfer_File (S32 chunk_size);
+ LLXfer_File (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size);
+ virtual ~LLXfer_File();
+
+ virtual void init(const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size);
+ virtual void free();
+
+ virtual S32 initializeRequest(U64 xfer_id,
+ const LLString& local_filename,
+ const LLString& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32),
+ void** user_data);
+ virtual S32 startDownload();
+
+ virtual S32 processEOF();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+
+ virtual S32 suck(S32 start_position);
+ virtual S32 flush();
+
+ virtual BOOL matchesLocalFilename(const LLString& filename);
+ virtual BOOL matchesRemoteFilename(const LLString& filename, ELLPath remote_path);
+
+ virtual S32 getMaxBufferSize();
+
+ virtual U32 getXferTypeTag();
+
+ virtual const char *getName();
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfer_mem.cpp b/indra/llmessage/llxfer_mem.cpp
new file mode 100644
index 0000000000..8f48247e20
--- /dev/null
+++ b/indra/llmessage/llxfer_mem.cpp
@@ -0,0 +1,199 @@
+/**
+ * @file llxfer_mem.cpp
+ * @brief implementation of LLXfer_Mem class for a single xfer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfer_mem.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+
+///////////////////////////////////////////////////////////
+
+LLXfer_Mem::LLXfer_Mem ()
+: LLXfer(-1)
+{
+ init();
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer_Mem::~LLXfer_Mem ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_Mem::init ()
+{
+ mRemoteFilename[0] = '\0';
+ mRemotePath = LL_PATH_NONE;
+ mDeleteRemoteOnCompletion = FALSE;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_Mem::free ()
+{
+ LLXfer::free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_Mem::setXferSize (S32 xfer_size)
+{
+ mXferSize = xfer_size;
+
+ delete[] mBuffer;
+ mBuffer = new char[xfer_size];
+
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+ mBufferContainsEOF = TRUE;
+
+// cout << "starting transfer of size: " << xfer_size << endl;
+}
+
+///////////////////////////////////////////////////////////
+
+U64 LLXfer_Mem::registerXfer(U64 xfer_id, const void *datap, const S32 length)
+{
+ mID = xfer_id;
+
+ if (datap)
+ {
+ setXferSize(length);
+ if (mBuffer)
+ {
+ memcpy(mBuffer,datap,length); /* Flawfinder : ignore */
+ mBufferLength = length;
+ }
+ else
+ {
+ xfer_id = 0;
+ }
+ }
+
+ mStatus = e_LL_XFER_REGISTERED;
+ return (xfer_id);
+}
+
+S32 LLXfer_Mem::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ S32 retval = LL_ERR_NOERR; // presume success
+
+ if (mXferSize <= 0)
+ {
+ return LL_ERR_FILE_EMPTY;
+ }
+
+ mRemoteHost = remote_host;
+ mID = xfer_id;
+ mPacketNum = -1;
+
+// cout << "Sending file: " << getName() << endl;
+
+ mStatus = e_LL_XFER_PENDING;
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_Mem::processEOF()
+{
+ S32 retval = 0;
+
+ mStatus = e_LL_XFER_COMPLETE;
+
+ llinfos << "xfer complete: " << getName() << llendl;
+
+ if (mCallback)
+ {
+ mCallback((void *)mBuffer,mBufferLength,mCallbackDataHandle,mCallbackResult);
+ }
+
+ return(retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_Mem::initializeRequest(U64 xfer_id,
+ const std::string& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*,S32,void**,S32),
+ void** user_data)
+{
+ S32 retval = 0; // presume success
+
+ mRemoteHost = remote_host;
+
+ // create a temp filename string using a GUID
+ mID = xfer_id;
+ mCallback = callback;
+ mCallbackDataHandle = user_data;
+ mCallbackResult = LL_ERR_NOERR;
+
+ strncpy(mRemoteFilename, remote_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+ mRemotePath = remote_path;
+ mDeleteRemoteOnCompletion = delete_remote_on_completion;
+
+ llinfos << "Requesting file: " << remote_filename << llendl;
+
+ delete [] mBuffer;
+ mBuffer = NULL;
+
+ mBufferLength = 0;
+ mPacketNum = 0;
+ mStatus = e_LL_XFER_PENDING;
+ return retval;
+}
+
+//////////////////////////////////////////////////////////
+
+S32 LLXfer_Mem::startDownload()
+{
+ S32 retval = 0; // presume success
+ gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addStringFast(_PREHASH_Filename, mRemoteFilename);
+ gMessageSystem->addU8("FilePath", (U8) mRemotePath);
+ gMessageSystem->addBOOL("DeleteOnCompletion", mDeleteRemoteOnCompletion);
+ gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
+ gMessageSystem->addUUIDFast(_PREHASH_VFileID, LLUUID::null);
+ gMessageSystem->addS16Fast(_PREHASH_VFileType, -1);
+
+ gMessageSystem->sendReliable(mRemoteHost);
+ mStatus = e_LL_XFER_IN_PROGRESS;
+
+ return (retval);
+}
+
+//////////////////////////////////////////////////////////
+
+U32 LLXfer_Mem::getXferTypeTag()
+{
+ return LLXfer::XFER_MEM;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llxfer_mem.h b/indra/llmessage/llxfer_mem.h
new file mode 100644
index 0000000000..d7cbdc4f85
--- /dev/null
+++ b/indra/llmessage/llxfer_mem.h
@@ -0,0 +1,61 @@
+/**
+ * @file llxfer_mem.h
+ * @brief definition of LLXfer_Mem class for a single xfer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_MEM_H
+#define LL_LLXFER_MEM_H
+
+#include <stdio.h>
+
+#include "message.h"
+#include "lltimer.h"
+#include "llxfer.h"
+#include "lldir.h"
+
+class LLXfer_Mem : public LLXfer
+{
+ private:
+ protected:
+ void (*mCallback)(void *, S32, void **,S32);
+ char mRemoteFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+ ELLPath mRemotePath;
+ BOOL mDeleteRemoteOnCompletion;
+
+ public:
+
+ private:
+ protected:
+ public:
+ LLXfer_Mem ();
+ virtual ~LLXfer_Mem();
+
+ virtual void init();
+ virtual void free();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+ virtual U64 registerXfer(U64 xfer_id, const void *datap, const S32 length);
+ virtual void setXferSize (S32 data_size);
+
+ virtual S32 initializeRequest(U64 xfer_id,
+ const std::string& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*,S32,void**,S32),
+ void** user_data);
+ virtual S32 startDownload();
+
+ virtual S32 processEOF();
+
+ virtual U32 getXferTypeTag();
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp
new file mode 100644
index 0000000000..5030556eb0
--- /dev/null
+++ b/indra/llmessage/llxfer_vfile.cpp
@@ -0,0 +1,320 @@
+/**
+ * @file llxfer_vfile.cpp
+ * @brief implementation of LLXfer_VFile class for a single xfer (vfile).
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfer_vfile.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "lldir.h"
+
+// size of chunks read from/written to disk
+const U32 LL_MAX_XFER_FILE_BUFFER = 65536;
+
+///////////////////////////////////////////////////////////
+
+LLXfer_VFile::LLXfer_VFile ()
+: LLXfer(-1)
+{
+ init(NULL, LLUUID::null, LLAssetType::AT_NONE);
+}
+
+LLXfer_VFile::LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type)
+: LLXfer(-1)
+{
+ init(vfs, local_id, type);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer_VFile::~LLXfer_VFile ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_VFile::init (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type)
+{
+
+ mVFS = vfs;
+ mLocalID = local_id;
+ mType = type;
+
+ mVFile = NULL;
+
+ char id_string[UUID_STR_LENGTH]; /* Flawfinder : ignore */
+ mLocalID.toString(id_string);
+
+ snprintf(mName, sizeof(mName), "VFile %s:%s", id_string, LLAssetType::lookup(mType)); /* Flawfinder : ignore */
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_VFile::free ()
+{
+ LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE);
+ file.remove();
+
+ delete mVFile;
+ mVFile = NULL;
+
+ LLXfer::free();
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::initializeRequest(U64 xfer_id,
+ LLVFS* vfs,
+ const LLUUID& local_id,
+ const LLUUID& remote_id,
+ LLAssetType::EType type,
+ const LLHost& remote_host,
+ void (*callback)(void**,S32),
+ void** user_data)
+{
+ S32 retval = 0; // presume success
+
+ mRemoteHost = remote_host;
+
+ mVFS = vfs;
+ mLocalID = local_id;
+ mRemoteID = remote_id;
+ mType = type;
+
+ mID = xfer_id;
+ mCallback = callback;
+ mCallbackDataHandle = user_data;
+ mCallbackResult = LL_ERR_NOERR;
+
+ char id_string[UUID_STR_LENGTH]; /* Flawfinder : ignore */
+ mLocalID.toString(id_string);
+
+ snprintf(mName, sizeof(mName), "VFile %s:%s", id_string, LLAssetType::lookup(mType)); /* Flawfinder : ignore */
+
+ llinfos << "Requesting " << mName << llendl;
+
+ if (mBuffer)
+ {
+ delete[] mBuffer;
+ mBuffer = NULL;
+ }
+
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+
+ mBufferLength = 0;
+ mPacketNum = 0;
+ mTempID.generate();
+ mStatus = e_LL_XFER_PENDING;
+ return retval;
+}
+
+//////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::startDownload()
+{
+ S32 retval = 0; // presume success
+ LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND);
+
+ gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addStringFast(_PREHASH_Filename, "");
+ gMessageSystem->addU8("FilePath", (U8) LL_PATH_NONE);
+ gMessageSystem->addBOOL("DeleteOnCompletion", FALSE);
+ gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
+ gMessageSystem->addUUIDFast(_PREHASH_VFileID, mRemoteID);
+ gMessageSystem->addS16Fast(_PREHASH_VFileType, (S16)mType);
+
+ gMessageSystem->sendReliable(mRemoteHost);
+ mStatus = e_LL_XFER_IN_PROGRESS;
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ S32 retval = LL_ERR_NOERR; // presume success
+
+ mRemoteHost = remote_host;
+ mID = xfer_id;
+ mPacketNum = -1;
+
+// cout << "Sending file: " << mLocalFilename << endl;
+
+ delete [] mBuffer;
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+
+ delete mVFile;
+ mVFile = NULL;
+ if(mVFS->getExists(mLocalID, mType))
+ {
+ mVFile = new LLVFile(mVFS, mLocalID, mType, LLVFile::READ);
+
+ if (mVFile->getSize() <= 0)
+ {
+ delete mVFile;
+ mVFile = NULL;
+
+ return LL_ERR_FILE_EMPTY;
+ }
+ }
+
+ if(mVFile)
+ {
+ setXferSize(mVFile->getSize());
+ mStatus = e_LL_XFER_PENDING;
+ }
+ else
+ {
+ retval = LL_ERR_FILE_NOT_FOUND;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+void LLXfer_VFile::setXferSize (S32 xfer_size)
+{
+ LLXfer::setXferSize(xfer_size);
+
+ // Don't do this on the server side, where we have a persistent mVFile
+ // It would be nice if LLXFers could tell which end of the pipe they were
+ if (! mVFile)
+ {
+ LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND);
+ file.setMaxSize(xfer_size);
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::getMaxBufferSize ()
+{
+ return(LL_MAX_XFER_FILE_BUFFER);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::suck(S32 start_position)
+{
+ S32 retval = 0;
+
+ if (mVFile)
+ {
+ // grab a buffer from the right place in the file
+ if (! mVFile->seek(start_position, 0))
+ {
+ llwarns << "VFile Xfer Can't seek to position " << start_position << ", file length " << mVFile->getSize() << llendl;
+ llwarns << "While sending file " << mLocalID << llendl;
+ return -1;
+ }
+
+ if (mVFile->read((U8*)mBuffer, LL_MAX_XFER_FILE_BUFFER)) /* Flawfinder : ignore */
+ {
+ mBufferLength = mVFile->getLastBytesRead();
+ mBufferStartOffset = start_position;
+
+ mBufferContainsEOF = mVFile->eof();
+ }
+ else
+ {
+ retval = -1;
+ }
+ }
+ else
+ {
+ retval = -1;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::flush()
+{
+ S32 retval = 0;
+ if (mBufferLength)
+ {
+ LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND);
+
+ file.write((U8*)mBuffer, mBufferLength);
+
+ mBufferLength = 0;
+ }
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::processEOF()
+{
+ S32 retval = 0;
+ mStatus = e_LL_XFER_COMPLETE;
+
+ flush();
+
+ if (!mCallbackResult)
+ {
+ LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE);
+ if (! file.rename(mLocalID, mType))
+ {
+ llinfos << "copy from temp file failed: unable to rename to " << mLocalID << llendl;
+ }
+
+ }
+
+ if (mVFile)
+ {
+ delete mVFile;
+ mVFile = NULL;
+ }
+
+ retval = LLXfer::processEOF();
+
+ return(retval);
+}
+
+////////////////////////////////////////////////////////////
+
+BOOL LLXfer_VFile::matchesLocalFile(const LLUUID &id, LLAssetType::EType type)
+{
+ return (id == mLocalID && type == mType);
+}
+
+//////////////////////////////////////////////////////////
+
+BOOL LLXfer_VFile::matchesRemoteFile(const LLUUID &id, LLAssetType::EType type)
+{
+ return (id == mRemoteID && type == mType);
+}
+
+//////////////////////////////////////////////////////////
+
+const char * LLXfer_VFile::getName()
+{
+ return mName;
+}
+
+//////////////////////////////////////////////////////////
+
+// hacky - doesn't matter what this is
+// as long as it's different from the other classes
+U32 LLXfer_VFile::getXferTypeTag()
+{
+ return LLXfer::XFER_VFILE;
+}
diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h
new file mode 100644
index 0000000000..3d9d8de7a7
--- /dev/null
+++ b/indra/llmessage/llxfer_vfile.h
@@ -0,0 +1,74 @@
+/**
+ * @file llxfer_vfile.h
+ * @brief definition of LLXfer_VFile class for a single xfer_vfile.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_VFILE_H
+#define LL_LLXFER_VFILE_H
+
+#include <stdio.h>
+
+#include "llxfer.h"
+#include "llassetstorage.h"
+
+class LLVFS;
+class LLVFile;
+
+class LLXfer_VFile : public LLXfer
+{
+ protected:
+ LLUUID mLocalID;
+ LLUUID mRemoteID;
+ LLUUID mTempID;
+ LLAssetType::EType mType;
+
+ LLVFile *mVFile;
+
+ LLVFS *mVFS;
+
+ char mName[MAX_STRING]; /* Flawfinder : ignore */
+
+ public:
+ LLXfer_VFile ();
+ LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type);
+ virtual ~LLXfer_VFile();
+
+ virtual void init(LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type);
+ virtual void free();
+
+ virtual S32 initializeRequest(U64 xfer_id,
+ LLVFS *vfs,
+ const LLUUID &local_id,
+ const LLUUID &remote_id,
+ const LLAssetType::EType type,
+ const LLHost &remote_host,
+ void (*callback)(void **,S32),
+ void **user_data);
+ virtual S32 startDownload();
+
+ virtual S32 processEOF();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+
+ virtual S32 suck(S32 start_position);
+ virtual S32 flush();
+
+ virtual BOOL matchesLocalFile(const LLUUID &id, LLAssetType::EType type);
+ virtual BOOL matchesRemoteFile(const LLUUID &id, LLAssetType::EType type);
+
+ virtual void setXferSize(S32 xfer_size);
+ virtual S32 getMaxBufferSize();
+
+ virtual U32 getXferTypeTag();
+
+ virtual const char *getName();
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp
new file mode 100644
index 0000000000..e2d8cd30b3
--- /dev/null
+++ b/indra/llmessage/llxfermanager.cpp
@@ -0,0 +1,1133 @@
+/**
+ * @file llxfermanager.cpp
+ * @brief implementation of LLXferManager class for a collection of xfers
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfermanager.h"
+
+#include "llxfer.h"
+#include "llxfer_file.h"
+#include "llxfer_mem.h"
+#include "llxfer_vfile.h"
+
+#include "llerror.h"
+#include "lluuid.h"
+#include "u64.h"
+
+const F32 LL_XFER_REGISTRATION_TIMEOUT = 60.0f; // timeout if a registered transfer hasn't been requested in 60 seconds
+const F32 LL_PACKET_TIMEOUT = 3.0f; // packet timeout at 3 s
+const S32 LL_PACKET_RETRY_LIMIT = 10; // packet retransmission limit
+
+const S32 LL_DEFAULT_MAX_SIMULTANEOUS_XFERS = 10;
+const S32 LL_DEFAULT_MAX_REQUEST_FIFO_XFERS = 1000;
+
+#define LL_XFER_PROGRESS_MESSAGES 0
+#define LL_XFER_TEST_REXMIT 0
+
+
+///////////////////////////////////////////////////////////
+
+LLXferManager::LLXferManager (LLVFS *vfs)
+{
+ init(vfs);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXferManager::~LLXferManager ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::init (LLVFS *vfs)
+{
+ mSendList = NULL;
+ mReceiveList = NULL;
+
+ setMaxOutgoingXfersPerCircuit(LL_DEFAULT_MAX_SIMULTANEOUS_XFERS);
+ setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS);
+
+ mVFS = vfs;
+
+ // Turn on or off ack throttling
+ mUseAckThrottling = FALSE;
+ setAckThrottleBPS(100000);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::free ()
+{
+ LLXfer *xferp;
+ LLXfer *delp;
+
+ mOutgoingHosts.deleteAllData();
+
+ delp = mSendList;
+ while (delp)
+ {
+ xferp = delp->mNext;
+ delete delp;
+ delp = xferp;
+ }
+ mSendList = NULL;
+
+ delp = mReceiveList;
+ while (delp)
+ {
+ xferp = delp->mNext;
+ delete delp;
+ delp = xferp;
+ }
+ mReceiveList = NULL;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::setMaxIncomingXfers(S32 max_num)
+{
+ mMaxIncomingXfers = max_num;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::setMaxOutgoingXfersPerCircuit(S32 max_num)
+{
+ mMaxOutgoingXfersPerCircuit = max_num;
+}
+
+void LLXferManager::setUseAckThrottling(const BOOL use)
+{
+ mUseAckThrottling = use;
+}
+
+void LLXferManager::setAckThrottleBPS(const F32 bps)
+{
+ // Let's figure out the min we can set based on the ack retry rate
+ // and number of simultaneous.
+
+ // Assuming we're running as slow as possible, this is the lowest ack
+ // rate we can use.
+ F32 min_bps = (1000.f * 8.f* mMaxIncomingXfers) / LL_PACKET_TIMEOUT;
+
+ // Set
+ F32 actual_rate = llmax(min_bps*1.1f, bps);
+ llinfos << "LLXferManager ack throttle min rate: " << min_bps << llendl;
+ llinfos << "LLXferManager ack throttle actual rate: " << actual_rate << llendl;
+ mAckThrottle.setRate(actual_rate);
+}
+
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::updateHostStatus()
+{
+ LLXfer *xferp;
+ LLHostStatus *host_statusp = NULL;
+
+ mOutgoingHosts.deleteAllData();
+
+ for (xferp = mSendList; xferp; xferp = xferp->mNext)
+ {
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == xferp->mRemoteHost)
+ {
+ break;
+ }
+ }
+ if (!host_statusp)
+ {
+ host_statusp = new LLHostStatus();
+ if (host_statusp)
+ {
+ host_statusp->mHost = xferp->mRemoteHost;
+ mOutgoingHosts.addData(host_statusp);
+ }
+ }
+ if (host_statusp)
+ {
+ if (xferp->mStatus == e_LL_XFER_PENDING)
+ {
+ host_statusp->mNumPending++;
+ }
+ else if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ host_statusp->mNumActive++;
+ }
+ }
+
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::printHostStatus()
+{
+ LLHostStatus *host_statusp = NULL;
+ if (mOutgoingHosts.getFirstData())
+ {
+ llinfos << "Outgoing Xfers:" << llendl;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ llinfos << " " << host_statusp->mHost << " active: " << host_statusp->mNumActive << " pending: " << host_statusp->mNumPending << llendl;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer *LLXferManager::findXfer (U64 id, LLXfer *list_head)
+{
+ LLXfer *xferp;
+ for (xferp = list_head; xferp; xferp = xferp->mNext)
+ {
+ if (xferp->mID == id)
+ {
+ return(xferp);
+ }
+ }
+ return(NULL);
+}
+
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::removeXfer (LLXfer *delp, LLXfer **list_head)
+{
+ LLXfer *xferp;
+
+ if (delp)
+ {
+ if (*list_head == delp)
+ {
+ *list_head = delp->mNext;
+ delete (delp);
+ }
+ else
+ {
+ xferp = *list_head;
+ while (xferp->mNext)
+ {
+ if (xferp->mNext == delp)
+ {
+ xferp->mNext = delp->mNext;
+ delete (delp);
+ continue;
+ }
+ xferp = xferp->mNext;
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+U32 LLXferManager::numActiveListEntries(LLXfer *list_head)
+{
+ U32 num_entries = 0;
+
+ while (list_head)
+ {
+ if ((list_head->mStatus == e_LL_XFER_IN_PROGRESS))
+ {
+ num_entries++;
+ }
+ list_head = list_head->mNext;
+ }
+ return(num_entries);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::numPendingXfers(const LLHost &host)
+{
+ LLHostStatus *host_statusp = NULL;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == host)
+ {
+ return (host_statusp->mNumPending);
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::numActiveXfers(const LLHost &host)
+{
+ LLHostStatus *host_statusp = NULL;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == host)
+ {
+ return (host_statusp->mNumActive);
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::changeNumActiveXfers(const LLHost &host, S32 delta)
+{
+ LLHostStatus *host_statusp = NULL;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == host)
+ {
+ host_statusp->mNumActive += delta;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::registerCallbacks(LLMessageSystem *msgsystem)
+{
+ msgsystem->setHandlerFuncFast(_PREHASH_ConfirmXferPacket, process_confirm_packet, NULL);
+ msgsystem->setHandlerFuncFast(_PREHASH_RequestXfer, process_request_xfer, NULL);
+ msgsystem->setHandlerFuncFast(_PREHASH_SendXferPacket, continue_file_receive, NULL);
+ msgsystem->setHandlerFuncFast(_PREHASH_AbortXfer, process_abort_xfer, NULL);
+}
+
+///////////////////////////////////////////////////////////
+
+U64 LLXferManager::getNextID ()
+{
+ LLUUID a_guid;
+
+ a_guid.generate();
+
+
+ return(*((U64*)(a_guid.mData)));
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::encodePacketNum(S32 packet_num, BOOL is_EOF)
+{
+ if (is_EOF)
+ {
+ packet_num |= 0x80000000;
+ }
+ return packet_num;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::decodePacketNum(S32 packet_num)
+{
+ return(packet_num & 0x0FFFFFFF);
+}
+
+///////////////////////////////////////////////////////////
+
+BOOL LLXferManager::isLastPacket(S32 packet_num)
+{
+ return(packet_num & 0x80000000);
+}
+
+///////////////////////////////////////////////////////////
+
+U64 LLXferManager::registerXfer(const void *datap, const S32 length)
+{
+ LLXfer *xferp;
+ U64 xfer_id = getNextID();
+
+ xferp = (LLXfer *) new LLXfer_Mem();
+ if (xferp)
+ {
+ xferp->mNext = mSendList;
+ mSendList = xferp;
+
+ xfer_id = ((LLXfer_Mem *)xferp)->registerXfer(xfer_id, datap,length);
+
+ if (!xfer_id)
+ {
+ removeXfer(xferp,&mSendList);
+ }
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ xfer_id = 0;
+ }
+
+ return(xfer_id);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::requestFile(const char* local_filename,
+ const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32),
+ void** user_data,
+ BOOL is_priority,
+ BOOL use_big_packets)
+{
+ LLXfer *xferp;
+
+ for (xferp = mReceiveList; xferp ; xferp = xferp->mNext)
+ {
+ if (xferp->getXferTypeTag() == LLXfer::XFER_FILE
+ && (((LLXfer_File*)xferp)->matchesLocalFilename(local_filename))
+ && (((LLXfer_File*)xferp)->matchesRemoteFilename(remote_filename, remote_path))
+ && (remote_host == xferp->mRemoteHost)
+ && (callback == xferp->mCallback)
+ && (user_data == xferp->mCallbackDataHandle))
+
+ {
+ // cout << "requested a xfer already in progress" << endl;
+ return;
+ }
+ }
+
+ S32 chunk_size = use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1;
+ xferp = (LLXfer *) new LLXfer_File(chunk_size);
+ if (xferp)
+ {
+ addToList(xferp, mReceiveList, is_priority);
+
+ // Remove any file by the same name that happens to be lying
+ // around.
+ // Note: according to AaronB, this is here to deal with locks on files that were
+ // in transit during a crash,
+ if(delete_remote_on_completion &&
+ (strstr(remote_filename,".tmp") == &remote_filename[strlen(remote_filename)-4])) /* Flawfinder : ignore */
+ {
+ LLFile::remove(local_filename);
+ }
+ ((LLXfer_File *)xferp)->initializeRequest(
+ getNextID(),
+ local_filename,
+ remote_filename,
+ remote_path,
+ remote_host,
+ delete_remote_on_completion,
+ callback,user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ }
+}
+
+void LLXferManager::requestFile(const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*,S32,void**,S32),
+ void** user_data,
+ BOOL is_priority)
+{
+ LLXfer *xferp;
+
+ xferp = (LLXfer *) new LLXfer_Mem();
+ if (xferp)
+ {
+ addToList(xferp, mReceiveList, is_priority);
+ ((LLXfer_Mem *)xferp)->initializeRequest(getNextID(),
+ remote_filename,
+ remote_path,
+ remote_host,
+ delete_remote_on_completion,
+ callback, user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ }
+}
+
+void LLXferManager::requestVFile(const LLUUID& local_id,
+ const LLUUID& remote_id,
+ LLAssetType::EType type, LLVFS* vfs,
+ const LLHost& remote_host,
+ void (*callback)(void**, S32),
+ void** user_data,
+ BOOL is_priority)
+{
+ LLXfer *xferp;
+
+ for (xferp = mReceiveList; xferp ; xferp = xferp->mNext)
+ {
+ if (xferp->getXferTypeTag() == LLXfer::XFER_VFILE
+ && (((LLXfer_VFile*)xferp)->matchesLocalFile(local_id, type))
+ && (((LLXfer_VFile*)xferp)->matchesRemoteFile(remote_id, type))
+ && (remote_host == xferp->mRemoteHost)
+ && (callback == xferp->mCallback)
+ && (user_data == xferp->mCallbackDataHandle))
+
+ {
+ // cout << "requested a xfer already in progress" << endl;
+ return;
+ }
+ }
+
+ xferp = (LLXfer *) new LLXfer_VFile();
+ if (xferp)
+ {
+ addToList(xferp, mReceiveList, is_priority);
+ ((LLXfer_VFile *)xferp)->initializeRequest(getNextID(),
+ vfs,
+ local_id,
+ remote_id,
+ type,
+ remote_host,
+ callback,
+ user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ }
+
+}
+
+/*
+void LLXferManager::requestXfer(
+ const char *local_filename,
+ BOOL delete_remote_on_completion,
+ U64 xfer_id,
+ const LLHost &remote_host,
+ void (*callback)(void **,S32),
+ void **user_data)
+{
+ LLXfer *xferp;
+
+ for (xferp = mReceiveList; xferp ; xferp = xferp->mNext)
+ {
+ if (xferp->getXferTypeTag() == LLXfer::XFER_FILE
+ && (((LLXfer_File*)xferp)->matchesLocalFilename(local_filename))
+ && (xfer_id == xferp->mID)
+ && (remote_host == xferp->mRemoteHost)
+ && (callback == xferp->mCallback)
+ && (user_data == xferp->mCallbackDataHandle))
+
+ {
+ // cout << "requested a xfer already in progress" << endl;
+ return;
+ }
+ }
+
+ xferp = (LLXfer *) new LLXfer_File();
+ if (xferp)
+ {
+ xferp->mNext = mReceiveList;
+ mReceiveList = xferp;
+
+ ((LLXfer_File *)xferp)->initializeRequest(xfer_id,local_filename,"",LL_PATH_NONE,remote_host,delete_remote_on_completion,callback,user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+}
+
+void LLXferManager::requestXfer(U64 xfer_id, const LLHost &remote_host, BOOL delete_remote_on_completion, void (*callback)(void *,S32,void **,S32),void **user_data)
+{
+ LLXfer *xferp;
+
+ xferp = (LLXfer *) new LLXfer_Mem();
+ if (xferp)
+ {
+ xferp->mNext = mReceiveList;
+ mReceiveList = xferp;
+
+ ((LLXfer_Mem *)xferp)->initializeRequest(xfer_id,"",LL_PATH_NONE,remote_host,delete_remote_on_completion,callback,user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+}
+*/
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processReceiveData (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+ // there's sometimes an extra 4 bytes added to an xfer payload
+ const S32 BUF_SIZE = LL_XFER_LARGE_PAYLOAD + 4;
+ char fdata_buf[LL_XFER_LARGE_PAYLOAD + 4]; /* Flawfinder : ignore */
+ S32 fdata_size;
+ U64 id;
+ S32 packetnum;
+ LLXfer * xferp;
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ mesgsys->getS32Fast(_PREHASH_XferID, _PREHASH_Packet, packetnum);
+
+ fdata_size = mesgsys->getSizeFast(_PREHASH_DataPacket,_PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_DataPacket, _PREHASH_Data, fdata_buf, 0, 0, BUF_SIZE);
+
+ xferp = findXfer(id, mReceiveList);
+
+ if (!xferp)
+ {
+ char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
+ llwarns << "received xfer data from " << mesgsys->getSender()
+ << " for non-existent xfer id: "
+ << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << llendl;
+ return;
+ }
+
+ S32 xfer_size;
+
+ if (decodePacketNum(packetnum) != xferp->mPacketNum) // is the packet different from what we were expecting?
+ {
+ // confirm it if it was a resend of the last one, since the confirmation might have gotten dropped
+ if (decodePacketNum(packetnum) == (xferp->mPacketNum - 1))
+ {
+ llinfos << "Reconfirming xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " packet " << packetnum << llendl; sendConfirmPacket(mesgsys, id, decodePacketNum(packetnum), mesgsys->getSender());
+ }
+ else
+ {
+ llinfos << "Ignoring xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " recv'd packet " << packetnum << "; expecting " << xferp->mPacketNum << llendl;
+ }
+ return;
+ }
+
+ S32 result = 0;
+
+ if (xferp->mPacketNum == 0) // first packet has size encoded as additional S32 at beginning of data
+ {
+ ntohmemcpy(&xfer_size,fdata_buf,MVT_S32,sizeof(S32));
+
+// do any necessary things on first packet ie. allocate memory
+ xferp->setXferSize(xfer_size);
+
+ // adjust buffer start and size
+ result = xferp->receiveData(&(fdata_buf[sizeof(S32)]),fdata_size-(sizeof(S32)));
+ }
+ else
+ {
+ result = xferp->receiveData(fdata_buf,fdata_size);
+ }
+
+ if (result == LL_ERR_CANNOT_OPEN_FILE)
+ {
+ xferp->abort(LL_ERR_CANNOT_OPEN_FILE);
+ removeXfer(xferp,&mReceiveList);
+ startPendingDownloads();
+ return;
+ }
+
+ xferp->mPacketNum++; // expect next packet
+
+ if (!mUseAckThrottling)
+ {
+ // No throttling, confirm right away
+ sendConfirmPacket(mesgsys, id, decodePacketNum(packetnum), mesgsys->getSender());
+ }
+ else
+ {
+ // Throttling, put on queue to be confirmed later.
+ LLXferAckInfo ack_info;
+ ack_info.mID = id;
+ ack_info.mPacketNum = decodePacketNum(packetnum);
+ ack_info.mRemoteHost = mesgsys->getSender();
+ mXferAckQueue.push(ack_info);
+ }
+
+ if (isLastPacket(packetnum))
+ {
+ xferp->processEOF();
+ removeXfer(xferp,&mReceiveList);
+ startPendingDownloads();
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 packetnum, const LLHost &remote_host)
+{
+#if LL_XFER_PROGRESS_MESSAGES
+ if (!(packetnum % 50))
+ {
+ cout << "confirming xfer packet #" << packetnum << endl;
+ }
+#endif
+ mesgsys->newMessageFast(_PREHASH_ConfirmXferPacket);
+ mesgsys->nextBlockFast(_PREHASH_XferID);
+ mesgsys->addU64Fast(_PREHASH_ID, id);
+ mesgsys->addU32Fast(_PREHASH_Packet, packetnum);
+
+ mesgsys->sendMessage(remote_host);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+
+ U64 id;
+ char local_filename[MAX_STRING]; /* Flawfinder : ignore */
+ ELLPath local_path = LL_PATH_NONE;
+ S32 result = LL_ERR_NOERR;
+ LLUUID uuid;
+ LLAssetType::EType type;
+ S16 type_s16;
+ BOOL b_use_big_packets;
+
+ mesgsys->getBOOL("XferID", "UseBigPackets", b_use_big_packets);
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
+ llinfos << "xfer request id: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF))
+ << " to " << mesgsys->getSender() << llendl;
+
+ mesgsys->getStringFast(_PREHASH_XferID, _PREHASH_Filename, MAX_STRING, local_filename);
+
+ U8 local_path_u8;
+ mesgsys->getU8("XferID", "FilePath", local_path_u8);
+ if( local_path_u8 < (U8)LL_PATH_COUNT )
+ {
+ local_path = (ELLPath)local_path_u8;
+ }
+ else
+ {
+ llwarns << "Invalid file path in LLXferManager::processFileRequest() " << (U32)local_path_u8 << llendl;
+ }
+
+ mesgsys->getUUIDFast(_PREHASH_XferID, _PREHASH_VFileID, uuid);
+ mesgsys->getS16Fast(_PREHASH_XferID, _PREHASH_VFileType, type_s16);
+ type = (LLAssetType::EType)type_s16;
+
+ LLXfer *xferp;
+
+ if (uuid != LLUUID::null)
+ {
+ if(NULL == LLAssetType::lookup(type))
+ {
+ llwarns << "Invalid type for xfer request: " << uuid << ":"
+ << type_s16 << " to " << mesgsys->getSender() << llendl;
+ return;
+ }
+
+ llinfos << "starting vfile transfer: " << uuid << "," << LLAssetType::lookup(type) << " to " << mesgsys->getSender() << llendl;
+
+ if (! mVFS)
+ {
+ llwarns << "Attempt to send VFile w/o available VFS" << llendl;
+ return;
+ }
+
+ xferp = (LLXfer *)new LLXfer_VFile(mVFS, uuid, type);
+ if (xferp)
+ {
+ xferp->mNext = mSendList;
+ mSendList = xferp;
+ result = xferp->startSend(id,mesgsys->getSender());
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+ }
+ else if (strlen(local_filename)) /* Flawfinder : ignore */
+ {
+ std::string expanded_filename = gDirUtilp->getExpandedFilename( local_path, local_filename );
+ llinfos << "starting file transfer: " << expanded_filename << " to " << mesgsys->getSender() << llendl;
+
+ BOOL delete_local_on_completion = FALSE;
+ mesgsys->getBOOL("XferID", "DeleteOnCompletion", delete_local_on_completion);
+
+ // -1 chunk_size causes it to use the default
+ xferp = (LLXfer *)new LLXfer_File(expanded_filename, delete_local_on_completion, b_use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1);
+
+ if (xferp)
+ {
+ xferp->mNext = mSendList;
+ mSendList = xferp;
+ result = xferp->startSend(id,mesgsys->getSender());
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+ }
+ else
+ {
+ char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
+ llinfos << "starting memory transfer: "
+ << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << " to "
+ << mesgsys->getSender() << llendl;
+
+ xferp = findXfer(id, mSendList);
+
+ if (xferp)
+ {
+ result = xferp->startSend(id,mesgsys->getSender());
+ }
+ else
+ {
+ llinfos << "Warning: " << U64_BUF << " not found." << llendl;
+ result = LL_ERR_FILE_NOT_FOUND;
+ }
+ }
+
+ if (result)
+ {
+ if (xferp)
+ {
+ xferp->abort(result);
+ removeXfer(xferp,&mSendList);
+ }
+ else // can happen with a memory transfer not found
+ {
+ llinfos << "Aborting xfer to " << mesgsys->getSender() << " with error: " << result << llendl;
+
+ mesgsys->newMessageFast(_PREHASH_AbortXfer);
+ mesgsys->nextBlockFast(_PREHASH_XferID);
+ mesgsys->addU64Fast(_PREHASH_ID, id);
+ mesgsys->addS32Fast(_PREHASH_Result, result);
+
+ mesgsys->sendMessage(mesgsys->getSender());
+ }
+ }
+ else if(xferp && (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit))
+ {
+ xferp->sendNextPacket();
+ changeNumActiveXfers(xferp->mRemoteHost,1);
+// llinfos << "***STARTING XFER IMMEDIATELY***" << llendl;
+ }
+ else
+ {
+ if(xferp)
+ {
+ llinfos << " queueing xfer request, " << numPendingXfers(xferp->mRemoteHost) << " ahead of this one" << llendl;
+ }
+ else
+ {
+ llwarns << "LLXferManager::processFileRequest() - no xfer found!"
+ << llendl;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processConfirmation (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+ U64 id = 0;
+ S32 packetNum = 0;
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ mesgsys->getS32Fast(_PREHASH_XferID, _PREHASH_Packet, packetNum);
+
+ LLXfer* xferp = findXfer(id, mSendList);
+ if (xferp)
+ {
+// cout << "confirmed packet #" << packetNum << " ping: "<< xferp->ACKTimer.getElapsedTimeF32() << endl;
+ xferp->mWaitingForACK = FALSE;
+ if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ xferp->sendNextPacket();
+ }
+ else
+ {
+ removeXfer(xferp, &mSendList);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::retransmitUnackedPackets ()
+{
+ LLXfer *xferp;
+ LLXfer *delp;
+ xferp = mReceiveList;
+ while(xferp)
+ {
+ if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ // if the circuit dies, abort
+ if (! gMessageSystem->mCircuitInfo.isCircuitAlive( xferp->mRemoteHost ))
+ {
+ llinfos << "Xfer found in progress on dead circuit, aborting" << llendl;
+ xferp->mCallbackResult = LL_ERR_CIRCUIT_GONE;
+ xferp->processEOF();
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mReceiveList);
+ continue;
+ }
+
+ }
+ xferp = xferp->mNext;
+ }
+
+ xferp = mSendList;
+ updateHostStatus();
+ F32 et;
+ while (xferp)
+ {
+ if (xferp->mWaitingForACK && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_PACKET_TIMEOUT))
+ {
+ if (xferp->mRetries > LL_PACKET_RETRY_LIMIT)
+ {
+ llinfos << "dropping xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " packet retransmit limit exceeded, xfer dropped" << llendl;
+ xferp->abort(LL_ERR_TCP_TIMEOUT);
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mSendList);
+ }
+ else
+ {
+ llinfos << "resending xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " packet unconfirmed after: "<< et << " sec, packet " << xferp->mPacketNum << llendl;
+ xferp->resendLastPacket();
+ xferp = xferp->mNext;
+ }
+ }
+ else if ((xferp->mStatus == e_LL_XFER_REGISTERED) && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_XFER_REGISTRATION_TIMEOUT))
+ {
+ llinfos << "registered xfer never requested, xfer dropped" << llendl;
+ xferp->abort(LL_ERR_TCP_TIMEOUT);
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mSendList);
+ }
+ else if (xferp->mStatus == e_LL_XFER_ABORTED)
+ {
+ llwarns << "Removing aborted xfer " << xferp->mRemoteHost << ":" << xferp->getName() << llendl;
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mSendList);
+ }
+ else if (xferp->mStatus == e_LL_XFER_PENDING)
+ {
+// llinfos << "*** numActiveXfers = " << numActiveXfers(xferp->mRemoteHost) << " mMaxOutgoingXfersPerCircuit = " << mMaxOutgoingXfersPerCircuit << llendl;
+ if (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit)
+ {
+// llinfos << "bumping pending xfer to active" << llendl;
+ xferp->sendNextPacket();
+ changeNumActiveXfers(xferp->mRemoteHost,1);
+ }
+ xferp = xferp->mNext;
+ }
+ else
+ {
+ xferp = xferp->mNext;
+ }
+ }
+
+ //
+ // HACK - if we're using xfer confirm throttling, throttle our xfer confirms here
+ // so we don't blow through bandwidth.
+ //
+
+ while (mXferAckQueue.getLength())
+ {
+ if (mAckThrottle.checkOverflow(1000.0f*8.0f))
+ {
+ break;
+ }
+ //llinfos << "Confirm packet queue length:" << mXferAckQueue.getLength() << llendl;
+ LLXferAckInfo ack_info;
+ mXferAckQueue.pop(ack_info);
+ //llinfos << "Sending confirm packet" << llendl;
+ sendConfirmPacket(gMessageSystem, ack_info.mID, ack_info.mPacketNum, ack_info.mRemoteHost);
+ mAckThrottle.throttleOverflow(1000.f*8.f); // Assume 1000 bytes/packet
+ }
+}
+
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processAbort (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+ U64 id = 0;
+ S32 result_code = 0;
+ LLXfer * xferp;
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ mesgsys->getS32Fast(_PREHASH_XferID, _PREHASH_Result, result_code);
+
+ xferp = findXfer(id, mReceiveList);
+ if (xferp)
+ {
+ xferp->mCallbackResult = result_code;
+ xferp->processEOF();
+ removeXfer(xferp, &mReceiveList);
+ startPendingDownloads();
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::startPendingDownloads()
+{
+ // This method goes through the list, and starts pending
+ // operations until active downloads == mMaxIncomingXfers. I copy
+ // the pending xfers into a temporary data structure because the
+ // xfers are stored as an intrusive linked list where older
+ // requests get pushed toward the back. Thus, if we didn't do a
+ // stateful iteration, it would be possible for old requests to
+ // never start.
+ LLXfer* xferp = mReceiveList;
+ LLLinkedList<LLXfer> pending_downloads;
+ S32 download_count = 0;
+ S32 pending_count = 0;
+ while(xferp)
+ {
+ if(xferp->mStatus == e_LL_XFER_PENDING)
+ {
+ ++pending_count; // getLength() is O(N), so track it here.
+ pending_downloads.addData(xferp);
+ }
+ else if(xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ ++download_count;
+ }
+ xferp = xferp->mNext;
+ }
+
+ S32 start_count = mMaxIncomingXfers - download_count;
+
+ lldebugs << "LLXferManager::startPendingDownloads() - XFER_IN_PROGRESS: "
+ << download_count << " XFER_PENDING: " << pending_count
+ << " startring " << llmin(start_count, pending_count) << llendl;
+
+ if((start_count > 0) && (pending_count > 0))
+ {
+ S32 result;
+ xferp = pending_downloads.getFirstData();
+ while(start_count-- && xferp)
+ {
+ result = xferp->startDownload();
+ if(result)
+ {
+ xferp->abort(result);
+ ++start_count;
+ }
+ xferp = pending_downloads.getNextData();
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::addToList(LLXfer* xferp, LLXfer*& head, BOOL is_priority)
+{
+ if(is_priority)
+ {
+ xferp->mNext = NULL;
+ LLXfer* next = head;
+ if(next)
+ {
+ while(next->mNext)
+ {
+ next = next->mNext;
+ }
+ next->mNext = xferp;
+ }
+ else
+ {
+ head = xferp;
+ }
+ }
+ else
+ {
+ xferp->mNext = head;
+ head = xferp;
+ }
+}
+
+///////////////////////////////////////////////////////////
+// Globals and C routines
+///////////////////////////////////////////////////////////
+
+LLXferManager *gXferManager = NULL;
+
+
+void start_xfer_manager(LLVFS *vfs)
+{
+ gXferManager = new LLXferManager(vfs);
+}
+
+void cleanup_xfer_manager()
+{
+ if (gXferManager)
+ {
+ delete(gXferManager);
+ gXferManager = NULL;
+ }
+}
+
+void process_confirm_packet (LLMessageSystem *mesgsys, void **user_data)
+{
+ gXferManager->processConfirmation(mesgsys,user_data);
+}
+
+void process_request_xfer(LLMessageSystem *mesgsys, void **user_data)
+{
+ gXferManager->processFileRequest(mesgsys,user_data);
+}
+
+void continue_file_receive(LLMessageSystem *mesgsys, void **user_data)
+{
+#if LL_TEST_XFER_REXMIT
+ if (frand(1.f) > 0.05f)
+ {
+#endif
+ gXferManager->processReceiveData(mesgsys,user_data);
+#if LL_TEST_XFER_REXMIT
+ }
+ else
+ {
+ cout << "oops! dropped a xfer packet" << endl;
+ }
+#endif
+}
+
+void process_abort_xfer(LLMessageSystem *mesgsys, void **user_data)
+{
+ gXferManager->processAbort(mesgsys,user_data);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llxfermanager.h b/indra/llmessage/llxfermanager.h
new file mode 100644
index 0000000000..eca3684df5
--- /dev/null
+++ b/indra/llmessage/llxfermanager.h
@@ -0,0 +1,187 @@
+/**
+ * @file llxfermanager.h
+ * @brief definition of LLXferManager class for a keeping track of
+ * multiple xfers
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFERMANAGER_H
+#define LL_LLXFERMANAGER_H
+
+/**
+ * this manager keeps both a send list and a receive list; anything with a
+ * LLXferManager can send and receive files via messages
+ */
+
+//Forward declaration to avoid circular dependencies
+class LLXfer;
+class LLVFS;
+
+#include "llxfer.h"
+#include "message.h"
+#include "llassetstorage.h"
+#include "linked_lists.h"
+#include "lldir.h"
+#include "lllinkedqueue.h"
+#include "llthrottle.h"
+
+class LLHostStatus
+{
+ public:
+ LLHost mHost;
+ S32 mNumActive;
+ S32 mNumPending;
+
+ LLHostStatus() {mNumActive = 0; mNumPending = 0;};
+ virtual ~LLHostStatus(){};
+};
+
+// Class stores ack information, to be put on list so we can throttle xfer rate.
+class LLXferAckInfo
+{
+public:
+ LLXferAckInfo(U32 dummy = 0)
+ {
+ mID = 0;
+ mPacketNum = -1;
+ }
+
+ U64 mID;
+ S32 mPacketNum;
+ LLHost mRemoteHost;
+};
+
+class LLXferManager
+{
+ private:
+ LLVFS *mVFS;
+
+ protected:
+ S32 mMaxOutgoingXfersPerCircuit;
+ S32 mMaxIncomingXfers;
+
+ BOOL mUseAckThrottling; // Use ack throttling to cap file xfer bandwidth
+ LLLinkedQueue<LLXferAckInfo> mXferAckQueue;
+ LLThrottle mAckThrottle;
+ public:
+
+ // This enumeration is useful in the requestFile() to specify if
+ // an xfer must happen asap.
+ enum
+ {
+ LOW_PRIORITY = FALSE,
+ HIGH_PRIORITY = TRUE,
+ };
+
+ LLXfer *mSendList;
+ LLXfer *mReceiveList;
+
+ LLLinkedList <LLHostStatus> mOutgoingHosts;
+
+ private:
+ protected:
+ // implementation methods
+ virtual void startPendingDownloads();
+ virtual void addToList(LLXfer* xferp, LLXfer*& head, BOOL is_priority);
+
+ public:
+ LLXferManager(LLVFS *vfs);
+ virtual ~LLXferManager();
+
+ virtual void init(LLVFS *vfs);
+ virtual void free();
+
+ void setUseAckThrottling(const BOOL use);
+ void setAckThrottleBPS(const F32 bps);
+
+// list management routines
+ virtual LLXfer *findXfer(U64 id, LLXfer *list_head);
+ virtual void removeXfer (LLXfer *delp, LLXfer **list_head);
+ virtual U32 numActiveListEntries(LLXfer *list_head);
+ virtual S32 numActiveXfers(const LLHost &host);
+ virtual S32 numPendingXfers(const LLHost &host);
+ virtual void changeNumActiveXfers(const LLHost &host, S32 delta);
+
+ virtual void setMaxOutgoingXfersPerCircuit (S32 max_num);
+ virtual void setMaxIncomingXfers(S32 max_num);
+ virtual void updateHostStatus();
+ virtual void printHostStatus();
+
+// general utility routines
+ virtual void registerCallbacks(LLMessageSystem *mesgsys);
+ virtual U64 getNextID ();
+ virtual S32 encodePacketNum(S32 packet_num, BOOL is_eof);
+ virtual S32 decodePacketNum(S32 packet_num);
+ virtual BOOL isLastPacket(S32 packet_num);
+
+ virtual U64 registerXfer(const void *datap, const S32 length);
+
+// file requesting routines
+// .. to file
+ virtual void requestFile(const char* local_filename,
+ const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32), void** user_data,
+ BOOL is_priority = FALSE,
+ BOOL use_big_packets = FALSE);
+
+// .. to memory
+ virtual void requestFile(const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost &remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*, S32, void**, S32),
+ void** user_data,
+ BOOL is_priority = FALSE);
+
+// vfile requesting
+// .. to vfile
+ virtual void requestVFile(const LLUUID &local_id, const LLUUID& remote_id,
+ LLAssetType::EType type, LLVFS* vfs,
+ const LLHost& remote_host,
+ void (*callback)(void**, S32), void** user_data,
+ BOOL is_priority = FALSE);
+
+/*
+// xfer request (may be memory or file)
+// .. to file
+ virtual void requestXfer(const char *local_filename, U64 xfer_id,
+ BOOL delete_remote_on_completion,
+ const LLHost &remote_host, void (*callback)(void **,S32),void **user_data);
+// .. to memory
+ virtual void requestXfer(U64 xfer_id,
+ const LLHost &remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void *, S32, void **, S32),void **user_data);
+*/
+
+ virtual void processReceiveData (LLMessageSystem *mesgsys, void **user_data);
+ virtual void sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 packetnum, const LLHost &remote_host);
+
+// file sending routines
+ virtual void processFileRequest (LLMessageSystem *mesgsys, void **user_data);
+ virtual void processConfirmation (LLMessageSystem *mesgsys, void **user_data);
+ virtual void retransmitUnackedPackets ();
+
+// error handling
+ virtual void processAbort (LLMessageSystem *mesgsys, void **user_data);
+};
+
+extern LLXferManager* gXferManager;
+
+// initialization and garbage collection
+void start_xfer_manager(LLVFS *vfs);
+void cleanup_xfer_manager();
+
+// message system callbacks
+void process_confirm_packet (LLMessageSystem *mesgsys, void **user_data);
+void process_request_xfer (LLMessageSystem *mesgsys, void **user_data);
+void continue_file_receive(LLMessageSystem *mesgsys, void **user_data);
+void process_abort_xfer (LLMessageSystem *mesgsys, void **user_data);
+#endif
+
+
diff --git a/indra/llmessage/llxorcipher.cpp b/indra/llmessage/llxorcipher.cpp
new file mode 100644
index 0000000000..1fbbfec9e0
--- /dev/null
+++ b/indra/llmessage/llxorcipher.cpp
@@ -0,0 +1,108 @@
+/**
+ * @file llxorcipher.cpp
+ * @brief Implementation of LLXORCipher
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcrypto.h"
+#include "llerror.h"
+
+///----------------------------------------------------------------------------
+/// Class LLXORCipher
+///----------------------------------------------------------------------------
+
+LLXORCipher::LLXORCipher(const U8* pad, U32 pad_len) :
+ mPad(NULL),
+ mHead(NULL),
+ mPadLen(0)
+{
+ init(pad, pad_len);
+}
+
+// Destroys the object
+LLXORCipher::~LLXORCipher()
+{
+ init(NULL, 0);
+}
+
+LLXORCipher::LLXORCipher(const LLXORCipher& cipher) :
+ mPad(NULL),
+ mHead(NULL),
+ mPadLen(0)
+{
+ init(cipher.mPad, cipher.mPadLen);
+}
+
+LLXORCipher& LLXORCipher::operator=(const LLXORCipher& cipher)
+{
+ if(this == &cipher) return *this;
+ init(cipher.mPad, cipher.mPadLen);
+ return *this;
+}
+
+BOOL LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ if(!src || !src_len || !dst || !dst_len || !mPad) return FALSE;
+ U8* pad_end = mPad + mPadLen;
+ while(src_len--)
+ {
+ *dst++ = *src++ ^ *mHead++;
+ if(mHead >= pad_end) mHead = mPad;
+ }
+ return TRUE;
+}
+
+BOOL LLXORCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ // xor is a symetric cipher, thus, just call the other function.
+ return encrypt(src, src_len, dst, dst_len);
+}
+
+U32 LLXORCipher::requiredEncryptionSpace(U32 len)
+{
+ return len;
+}
+
+void LLXORCipher::init(const U8* pad, U32 pad_len)
+{
+ if(mPad)
+ {
+ delete [] mPad;
+ mPad = NULL;
+ mPadLen = 0;
+ }
+ if(pad && pad_len)
+ {
+ mPadLen = pad_len;
+ mPad = new U8[mPadLen];
+ if (mPad != NULL)
+ {
+ memcpy(mPad, pad, mPadLen); /* Flawfinder : ignore */
+ }
+ }
+ mHead = mPad;
+}
+
+#ifdef _DEBUG
+// static
+BOOL LLXORCipher::testHarness()
+{
+ const U32 PAD_LEN = 3;
+ const U8 PAD[] = "abc";
+ const S32 MSG_LENGTH = 12;
+ const char MESSAGE[MSG_LENGTH+1] = "gesundheight"; /* Flawfinder : ignore */
+ U8 encrypted[MSG_LENGTH];
+ U8 decrypted[MSG_LENGTH];
+
+ LLXORCipher cipher(PAD, PAD_LEN);
+ cipher.encrypt((U8*)MESSAGE, MSG_LENGTH, encrypted, MSG_LENGTH);
+ cipher.decrypt(encrypted, MSG_LENGTH, decrypted, MSG_LENGTH);
+
+ if(0 != memcmp((void*)MESSAGE, decrypted, MSG_LENGTH)) return FALSE;
+ return TRUE;
+}
+#endif
diff --git a/indra/llmessage/machine.h b/indra/llmessage/machine.h
new file mode 100644
index 0000000000..b5efe717d8
--- /dev/null
+++ b/indra/llmessage/machine.h
@@ -0,0 +1,101 @@
+/**
+ * @file machine.h
+ * @brief LLMachine class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MACHINE_H
+#define LL_MACHINE_H
+
+#include "llerror.h"
+#include "net.h"
+#include "llhost.h"
+
+typedef enum e_machine_type
+{
+ MT_NULL,
+ MT_SIMULATOR,
+ MT_VIEWER,
+ MT_SPACE_SERVER,
+ MT_OBJECT_REPOSITORY,
+ MT_PROXY,
+ MT_EOF
+} EMachineType;
+
+const U32 ADDRESS_STRING_SIZE = 12;
+
+class LLMachine
+{
+public:
+ LLMachine()
+ : mMachineType(MT_NULL), mControlPort(0) {}
+
+ LLMachine(EMachineType machine_type, U32 ip, S32 port)
+ : mMachineType(machine_type), mControlPort(0), mHost(ip,port) {}
+
+ LLMachine(EMachineType machine_type, const LLHost &host)
+ : mMachineType(machine_type) {mHost = host; mControlPort = 0;}
+
+ ~LLMachine() {}
+
+ // get functions
+ EMachineType getMachineType() const { return mMachineType; }
+ const U32 getMachineIP() const { return mHost.getAddress(); }
+ const S32 getMachinePort() const { return mHost.getPort(); }
+ const LLHost &getMachineHost() const { return mHost; }
+ // The control port is the listen port of the parent process that
+ // launched this machine. 0 means none or not known.
+ const S32 &getControlPort() const { return mControlPort; }
+ BOOL isValid() const { return (mHost.getPort() != 0); } // TRUE if corresponds to functioning machine
+
+ // set functions
+ void setMachineType(EMachineType machine_type) { mMachineType = machine_type; }
+ void setMachineIP(U32 ip) { mHost.setAddress(ip); }
+ void setMachineHost(const LLHost &host) { mHost = host; }
+
+ void setMachinePort(S32 port)
+ {
+ if (port < 0)
+ {
+ llinfos << "Can't assign a negative number to LLMachine::mPort" << llendl;
+ mHost.setPort(0);
+ }
+ else
+ {
+ mHost.setPort(port);
+ }
+ }
+
+ void setControlPort( S32 port )
+ {
+ if (port < 0)
+ {
+ llinfos << "Can't assign a negative number to LLMachine::mControlPort" << llendl;
+ mControlPort = 0;
+ }
+ else
+ {
+ mControlPort = port;
+ }
+ }
+
+
+ // member variables
+
+// Someday these should be made private.
+// When they are, some of the code that breaks should
+// become member functions of LLMachine -- Leviathan
+//private:
+
+ // I fixed the others, somebody should fix these! - djs
+ EMachineType mMachineType;
+
+protected:
+
+ S32 mControlPort;
+ LLHost mHost;
+};
+
+#endif
diff --git a/indra/llmessage/mean_collision_data.h b/indra/llmessage/mean_collision_data.h
new file mode 100644
index 0000000000..7d3f90cde6
--- /dev/null
+++ b/indra/llmessage/mean_collision_data.h
@@ -0,0 +1,81 @@
+/**
+ * @file mean_collision_data.h
+ * @brief data type to log interactions between stuff and agents that
+ * might be community standards violations
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MEAN_COLLISIONS_DATA_H
+#define LL_MEAN_COLLISIONS_DATA_H
+
+#include <time.h>
+#include "lldbstrings.h"
+
+const F32 MEAN_COLLISION_TIMEOUT = 5.f;
+const S32 MAX_MEAN_COLLISIONS = 5;
+
+typedef enum e_mean_collision_types
+{
+ MEAN_INVALID,
+ MEAN_BUMP,
+ MEAN_LLPUSHOBJECT,
+ MEAN_SELECTED_OBJECT_COLLIDE,
+ MEAN_SCRIPTED_OBJECT_COLLIDE,
+ MEAN_PHYSICAL_OBJECT_COLLIDE,
+ MEAN_EOF
+} EMeanCollisionType;
+
+class LLMeanCollisionData
+{
+public:
+ LLMeanCollisionData(const LLUUID &victim, const LLUUID &perp, time_t time, EMeanCollisionType type, F32 mag)
+ : mVictim(victim), mPerp(perp), mTime(time), mType(type), mMag(mag)
+ { mFirstName[0] = 0; mLastName[0] = 0; }
+
+ LLMeanCollisionData(LLMeanCollisionData *mcd)
+ : mVictim(mcd->mVictim), mPerp(mcd->mPerp), mTime(mcd->mTime), mType(mcd->mType), mMag(mcd->mMag)
+ {
+ strncpy(mFirstName, mcd->mFirstName, sizeof(mFirstName) -1); /* Flawfinder: Ignore */
+ mFirstName[sizeof(mFirstName) -1] = '\0';
+ strncpy(mLastName, mcd->mLastName, sizeof(mLastName) -1); /* Flawfinder: Ignore */
+ mLastName[sizeof(mLastName) -1] = '\0';
+ }
+
+ friend std::ostream& operator<<(std::ostream& s, const LLMeanCollisionData &a)
+ {
+ switch(a.mType)
+ {
+ case MEAN_BUMP:
+ s << "Mean Collision: " << a.mPerp << " bumped " << a.mVictim << " with a velocity of " << a.mMag << " at " << ctime(&a.mTime);
+ break;
+ case MEAN_LLPUSHOBJECT:
+ s << "Mean Collision: " << a.mPerp << " llPushObject-ed " << a.mVictim << " with a total force of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ case MEAN_SELECTED_OBJECT_COLLIDE:
+ s << "Mean Collision: " << a.mPerp << " dragged an object into " << a.mVictim << " with a velocity of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ case MEAN_SCRIPTED_OBJECT_COLLIDE:
+ s << "Mean Collision: " << a.mPerp << " smacked " << a.mVictim << " with a scripted object with velocity of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ case MEAN_PHYSICAL_OBJECT_COLLIDE:
+ s << "Mean Collision: " << a.mPerp << " smacked " << a.mVictim << " with a physical object with velocity of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ default:
+ break;
+ }
+ return s;
+ }
+
+ LLUUID mVictim;
+ LLUUID mPerp;
+ time_t mTime;
+ EMeanCollisionType mType;
+ F32 mMag;
+ char mFirstName[DB_FIRST_NAME_BUF_SIZE]; /* Flawfinder: Ignore */
+ char mLastName[DB_LAST_NAME_BUF_SIZE]; /* Flawfinder: Ignore */
+};
+
+
+#endif
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
new file mode 100644
index 0000000000..cdafafc8db
--- /dev/null
+++ b/indra/llmessage/message.cpp
@@ -0,0 +1,5876 @@
+/**
+ * @file message.cpp
+ * @brief LLMessageSystem class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "message.h"
+
+// system library includes
+#if !LL_WINDOWS
+// following header files required for inet_addr()
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstring>
+#include <time.h>
+#include <iomanip>
+#include <iterator>
+#include <sstream>
+
+#include "llapr.h"
+#include "apr-1/apr_portable.h"
+#include "apr-1/apr_network_io.h"
+#include "apr-1/apr_poll.h"
+
+// linden library headers
+#include "indra_constants.h"
+#include "lldir.h"
+#include "llerror.h"
+#include "llfasttimer.h"
+#include "llmd5.h"
+#include "llsd.h"
+#include "lltransfermanager.h"
+#include "lluuid.h"
+#include "llxfermanager.h"
+#include "timing.h"
+#include "llquaternion.h"
+#include "u64.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "lltransfertargetvfile.h"
+
+// Constants
+//const char* MESSAGE_LOG_FILENAME = "message.log";
+static const F32 CIRCUIT_DUMP_TIMEOUT = 30.f;
+static const S32 TRUST_TIME_WINDOW = 3;
+
+class LLMsgVarData
+{
+public:
+ LLMsgVarData() : mName(NULL), mSize(-1), mDataSize(-1), mData(NULL), mType(MVT_U8)
+ {
+ }
+
+ LLMsgVarData(const char *name, EMsgVariableType type) : mSize(-1), mDataSize(-1), mData(NULL), mType(type)
+ {
+ mName = (char *)name;
+ }
+
+ ~LLMsgVarData()
+ {
+ // copy constructor just copies the mData pointer, so only delete mData explicitly
+ }
+
+ void deleteData()
+ {
+ delete[] mData;
+ mData = NULL;
+ }
+
+ void addData(const void *indata, S32 size, EMsgVariableType type, S32 data_size = -1);
+
+ char *getName() const { return mName; }
+ S32 getSize() const { return mSize; }
+ void *getData() { return (void*)mData; }
+ S32 getDataSize() const { return mDataSize; }
+ EMsgVariableType getType() const { return mType; }
+
+protected:
+ char *mName;
+ S32 mSize;
+ S32 mDataSize;
+
+ U8 *mData;
+ EMsgVariableType mType;
+};
+
+
+class LLMsgBlkData
+{
+public:
+ LLMsgBlkData(const char *name, S32 blocknum) : mOffset(-1), mBlockNumber(blocknum), mTotalSize(-1)
+ {
+ mName = (char *)name;
+ }
+
+ ~LLMsgBlkData()
+ {
+ for (msg_var_data_map_t::iterator iter = mMemberVarData.begin();
+ iter != mMemberVarData.end(); iter++)
+ {
+ iter->deleteData();
+ }
+ }
+
+ void addVariable(const char *name, EMsgVariableType type)
+ {
+ LLMsgVarData tmp(name,type);
+ mMemberVarData[name] = tmp;
+ }
+
+ void addData(char *name, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1)
+ {
+ LLMsgVarData* temp = &mMemberVarData[name]; // creates a new entry if one doesn't exist
+ temp->addData(data, size, type, data_size);
+ }
+
+ S32 mOffset;
+ S32 mBlockNumber;
+ typedef LLDynamicArrayIndexed<LLMsgVarData, const char *, 8> msg_var_data_map_t;
+ msg_var_data_map_t mMemberVarData;
+ char *mName;
+ S32 mTotalSize;
+};
+
+
+class LLMsgData
+{
+public:
+ LLMsgData(const char *name) : mTotalSize(-1)
+ {
+ mName = (char *)name;
+ }
+ ~LLMsgData()
+ {
+ for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePairedPointer());
+ }
+
+ void addBlock(LLMsgBlkData *blockp)
+ {
+ mMemberBlocks[blockp->mName] = blockp;
+ }
+
+ void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1);
+
+public:
+ S32 mOffset;
+ typedef std::map<char*, LLMsgBlkData*> msg_blk_data_map_t;
+ msg_blk_data_map_t mMemberBlocks;
+ char *mName;
+ S32 mTotalSize;
+};
+
+inline void LLMsgVarData::addData(const void *data, S32 size, EMsgVariableType type, S32 data_size)
+{
+ mSize = size;
+ mDataSize = data_size;
+ if ( (type != MVT_VARIABLE) && (type != MVT_FIXED)
+ && (mType != MVT_VARIABLE) && (mType != MVT_FIXED))
+ {
+ if (mType != type)
+ {
+ llwarns << "Type mismatch in addData for " << mName
+ << " message: " << gMessageSystem->getCurrentSMessageName()
+ << " block: " << gMessageSystem->getCurrentSBlockName()
+ << llendl;
+ }
+ }
+ if(size)
+ {
+ delete mData; // Delete it if it already exists
+ mData = new U8[size];
+ htonmemcpy(mData, data, mType, size);
+ }
+}
+
+
+
+inline void LLMsgData::addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size)
+{
+ // remember that if the blocknumber is > 0 then the number is appended to the name
+ char *namep = (char *)blockname;
+ LLMsgBlkData* block_data = mMemberBlocks[namep];
+ if (block_data->mBlockNumber)
+ {
+ namep += block_data->mBlockNumber;
+ block_data->addData(varname, data, size, type, data_size);
+ }
+ else
+ {
+ block_data->addData(varname, data, size, type, data_size);
+ }
+}
+
+// LLMessage* classes store the template of messages
+
+
+class LLMessageVariable
+{
+public:
+ LLMessageVariable() : mName(NULL), mType(MVT_NULL), mSize(-1)
+ {
+ }
+
+ LLMessageVariable(char *name) : mType(MVT_NULL), mSize(-1)
+ {
+ mName = name;
+ }
+
+ LLMessageVariable(char *name, const EMsgVariableType type, const S32 size) : mType(type), mSize(size)
+ {
+ mName = gMessageStringTable.getString(name);
+ }
+
+ ~LLMessageVariable() {}
+
+ friend std::ostream& operator<<(std::ostream& s, LLMessageVariable &msg);
+
+ EMsgVariableType getType() const { return mType; }
+ S32 getSize() const { return mSize; }
+ char *getName() const { return mName; }
+protected:
+ char *mName;
+ EMsgVariableType mType;
+ S32 mSize;
+};
+
+
+typedef enum e_message_block_type
+{
+ MBT_NULL,
+ MBT_SINGLE,
+ MBT_MULTIPLE,
+ MBT_VARIABLE,
+ MBT_EOF
+} EMsgBlockType;
+
+class LLMessageBlock
+{
+public:
+ LLMessageBlock(char *name, EMsgBlockType type, S32 number = 1) : mType(type), mNumber(number), mTotalSize(0)
+ {
+ mName = gMessageStringTable.getString(name);
+ }
+
+ ~LLMessageBlock()
+ {
+ for_each(mMemberVariables.begin(), mMemberVariables.end(), DeletePairedPointer());
+ }
+
+ void addVariable(char *name, const EMsgVariableType type, const S32 size)
+ {
+ LLMessageVariable** varp = &mMemberVariables[name];
+ if (*varp != NULL)
+ {
+ llerrs << name << " has already been used as a variable name!" << llendl;
+ }
+ *varp = new LLMessageVariable(name, type, size);
+ if (((*varp)->getType() != MVT_VARIABLE)
+ &&(mTotalSize != -1))
+ {
+ mTotalSize += (*varp)->getSize();
+ }
+ else
+ {
+ mTotalSize = -1;
+ }
+ }
+
+ EMsgVariableType getVariableType(char *name)
+ {
+ return (mMemberVariables[name])->getType();
+ }
+
+ S32 getVariableSize(char *name)
+ {
+ return (mMemberVariables[name])->getSize();
+ }
+
+ friend std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg);
+
+ typedef std::map<const char *, LLMessageVariable*> message_variable_map_t;
+ message_variable_map_t mMemberVariables;
+ char *mName;
+ EMsgBlockType mType;
+ S32 mNumber;
+ S32 mTotalSize;
+};
+
+
+enum EMsgFrequency
+{
+ MFT_NULL = 0, // value is size of message number in bytes
+ MFT_HIGH = 1,
+ MFT_MEDIUM = 2,
+ MFT_LOW = 4
+};
+
+typedef enum e_message_trust
+{
+ MT_TRUST,
+ MT_NOTRUST
+} EMsgTrust;
+
+enum EMsgEncoding
+{
+ ME_UNENCODED,
+ ME_ZEROCODED
+};
+
+class LLMessageTemplate
+{
+public:
+ LLMessageTemplate(const char *name, U32 message_number, EMsgFrequency freq)
+ :
+ //mMemberBlocks(),
+ mName(NULL),
+ mFrequency(freq),
+ mTrust(MT_NOTRUST),
+ mEncoding(ME_ZEROCODED),
+ mMessageNumber(message_number),
+ mTotalSize(0),
+ mReceiveCount(0),
+ mReceiveBytes(0),
+ mReceiveInvalid(0),
+ mDecodeTimeThisFrame(0.f),
+ mTotalDecoded(0),
+ mTotalDecodeTime(0.f),
+ mMaxDecodeTimePerMsg(0.f),
+ mBanFromTrusted(false),
+ mBanFromUntrusted(false),
+ mHandlerFunc(NULL),
+ mUserData(NULL)
+ {
+ mName = gMessageStringTable.getString(name);
+ }
+
+ ~LLMessageTemplate()
+ {
+ for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePairedPointer());
+ }
+
+ void addBlock(LLMessageBlock *blockp)
+ {
+ LLMessageBlock** member_blockp = &mMemberBlocks[blockp->mName];
+ if (*member_blockp != NULL)
+ {
+ llerrs << "Block " << blockp->mName
+ << "has already been used as a block name!" << llendl;
+ }
+ *member_blockp = blockp;
+ if ( (mTotalSize != -1)
+ &&(blockp->mTotalSize != -1)
+ &&( (blockp->mType == MBT_SINGLE)
+ ||(blockp->mType == MBT_MULTIPLE)))
+ {
+ mTotalSize += blockp->mNumber*blockp->mTotalSize;
+ }
+ else
+ {
+ mTotalSize = -1;
+ }
+ }
+
+ LLMessageBlock *getBlock(char *name)
+ {
+ return mMemberBlocks[name];
+ }
+
+ // Trusted messages can only be recieved on trusted circuits.
+ void setTrust(EMsgTrust t)
+ {
+ mTrust = t;
+ }
+
+ EMsgTrust getTrust(void)
+ {
+ return mTrust;
+ }
+
+ // controls for how the message should be encoded
+ void setEncoding(EMsgEncoding e)
+ {
+ mEncoding = e;
+ }
+ EMsgEncoding getEncoding()
+ {
+ return mEncoding;
+ }
+
+ void setHandlerFunc(void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
+ {
+ mHandlerFunc = handler_func;
+ mUserData = user_data;
+ }
+
+ BOOL callHandlerFunc(LLMessageSystem *msgsystem)
+ {
+ if (mHandlerFunc)
+ {
+ mHandlerFunc(msgsystem, mUserData);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ bool isBanned(bool trustedSource)
+ {
+ return trustedSource ? mBanFromTrusted : mBanFromUntrusted;
+ }
+
+ friend std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg);
+
+public:
+ typedef std::map<char*, LLMessageBlock*> message_block_map_t;
+ message_block_map_t mMemberBlocks;
+ char *mName;
+ EMsgFrequency mFrequency;
+ EMsgTrust mTrust;
+ EMsgEncoding mEncoding;
+ U32 mMessageNumber;
+ S32 mTotalSize;
+ U32 mReceiveCount; // how many of this template have been received since last reset
+ U32 mReceiveBytes; // How many bytes received
+ U32 mReceiveInvalid; // How many "invalid" packets
+ F32 mDecodeTimeThisFrame; // Total seconds spent decoding this frame
+ U32 mTotalDecoded; // Total messages successfully decoded
+ F32 mTotalDecodeTime; // Total time successfully decoding messages
+ F32 mMaxDecodeTimePerMsg;
+
+ bool mBanFromTrusted;
+ bool mBanFromUntrusted;
+
+private:
+ // message handler function (this is set by each application)
+ void (*mHandlerFunc)(LLMessageSystem *msgsystem, void **user_data);
+ void **mUserData;
+};
+
+
+
+// static
+BOOL LLMessageSystem::mTimeDecodes = FALSE;
+
+// static, 50ms per message decode
+F32 LLMessageSystem::mTimeDecodesSpamThreshold = 0.05f;
+
+// FIXME: This needs to be moved into a seperate file so that it never gets
+// included in the viewer. 30 Sep 2002 mark
+// NOTE: I don't think it's important that the messgage system tracks
+// this since it must get set externally. 2004.08.25 Phoenix.
+static std::string g_shared_secret;
+std::string get_shared_secret();
+
+class LLMessagePollInfo
+{
+public:
+ apr_socket_t *mAPRSocketp;
+ apr_pollfd_t mPollFD;
+};
+
+
+// LLMessageVariable functions and friends
+
+std::ostream& operator<<(std::ostream& s, LLMessageVariable &msg)
+{
+ s << "\t\t" << msg.mName << " (";
+ switch (msg.mType)
+ {
+ case MVT_FIXED:
+ s << "Fixed, " << msg.mSize << " bytes total)\n";
+ break;
+ case MVT_VARIABLE:
+ s << "Variable, " << msg.mSize << " bytes of size info)\n";
+ break;
+ default:
+ s << "Unknown\n";
+ break;
+ }
+ return s;
+}
+
+// LLMessageBlock functions and friends
+
+std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg)
+{
+ s << "\t" << msg.mName << " (";
+ switch (msg.mType)
+ {
+ case MBT_SINGLE:
+ s << "Fixed";
+ break;
+ case MBT_MULTIPLE:
+ s << "Multiple - " << msg.mNumber << " copies";
+ break;
+ case MBT_VARIABLE:
+ s << "Variable";
+ break;
+ default:
+ s << "Unknown";
+ break;
+ }
+ if (msg.mTotalSize != -1)
+ {
+ s << ", " << msg.mTotalSize << " bytes each, " << msg.mNumber*msg.mTotalSize << " bytes total)\n";
+ }
+ else
+ {
+ s << ")\n";
+ }
+
+
+ for (LLMessageBlock::message_variable_map_t::iterator iter = msg.mMemberVariables.begin();
+ iter != msg.mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ s << ci;
+ }
+
+ return s;
+}
+
+// LLMessageTemplate functions and friends
+
+std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg)
+{
+ switch (msg.mFrequency)
+ {
+ case MFT_HIGH:
+ s << "========================================\n" << "Message #" << msg.mMessageNumber << "\n" << msg.mName << " (";
+ s << "High";
+ break;
+ case MFT_MEDIUM:
+ s << "========================================\n" << "Message #";
+ s << (msg.mMessageNumber & 0xFF) << "\n" << msg.mName << " (";
+ s << "Medium";
+ break;
+ case MFT_LOW:
+ s << "========================================\n" << "Message #";
+ s << (msg.mMessageNumber & 0xFFFF) << "\n" << msg.mName << " (";
+ s << "Low";
+ break;
+ default:
+ s << "Unknown";
+ break;
+ }
+
+ if (msg.mTotalSize != -1)
+ {
+ s << ", " << msg.mTotalSize << " bytes total)\n";
+ }
+ else
+ {
+ s << ")\n";
+ }
+
+ for (LLMessageTemplate::message_block_map_t::iterator iter = msg.mMemberBlocks.begin();
+ iter != msg.mMemberBlocks.end(); iter++)
+ {
+ LLMessageBlock* ci = iter->second;
+ s << *ci;
+ }
+
+ return s;
+}
+
+// LLMessageList functions and friends
+
+// Lets support a small subset of regular expressions here
+// Syntax is a string made up of:
+// a - checks against alphanumeric ([A-Za-z0-9])
+// c - checks against character ([A-Za-z])
+// f - checks against first variable character ([A-Za-z_])
+// v - checks against variable ([A-Za-z0-9_])
+// s - checks against sign of integer ([-0-9])
+// d - checks against integer digit ([0-9])
+// * - repeat last check
+
+// checks 'a'
+BOOL b_return_alphanumeric_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z'))
+ &&( (c < '0')
+ ||(c > '9')))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'c'
+BOOL b_return_character_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z')))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'f'
+BOOL b_return_first_variable_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z'))
+ &&(c != '_'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'v'
+BOOL b_return_variable_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z'))
+ &&( (c < '0')
+ ||(c > '9'))
+ &&(c != '_'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 's'
+BOOL b_return_signed_integer_ok(char c)
+{
+ if ( ( (c < '0')
+ ||(c > '9'))
+ &&(c != '-'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'd'
+BOOL b_return_integer_ok(char c)
+{
+ if ( (c < '0')
+ ||(c > '9'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL (*gParseCheckCharacters[])(char c) =
+{
+ b_return_alphanumeric_ok,
+ b_return_character_ok,
+ b_return_first_variable_ok,
+ b_return_variable_ok,
+ b_return_signed_integer_ok,
+ b_return_integer_ok
+};
+
+S32 get_checker_number(char checker)
+{
+ switch(checker)
+ {
+ case 'a':
+ return 0;
+ case 'c':
+ return 1;
+ case 'f':
+ return 2;
+ case 'v':
+ return 3;
+ case 's':
+ return 4;
+ case 'd':
+ return 5;
+ case '*':
+ return 9999;
+ default:
+ return -1;
+ }
+}
+
+// check token based on passed simplified regular expression
+BOOL b_check_token(char *token, char *regexp)
+{
+ S32 tptr, rptr = 0;
+ S32 current_checker, next_checker = 0;
+
+ current_checker = get_checker_number(regexp[rptr++]);
+
+ if (current_checker == -1)
+ {
+ llerrs << "Invalid regular expression value!" << llendl;
+ return FALSE;
+ }
+
+ if (current_checker == 9999)
+ {
+ llerrs << "Regular expression can't start with *!" << llendl;
+ return FALSE;
+ }
+
+ for (tptr = 0; token[tptr]; tptr++)
+ {
+ if (current_checker == -1)
+ {
+ llerrs << "Input exceeds regular expression!\nDid you forget a *?" << llendl;
+ return FALSE;
+ }
+
+ if (!gParseCheckCharacters[current_checker](token[tptr]))
+ {
+ return FALSE;
+ }
+ if (next_checker != 9999)
+ {
+ next_checker = get_checker_number(regexp[rptr++]);
+ if (next_checker != 9999)
+ {
+ current_checker = next_checker;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// C variable can be made up of upper or lower case letters, underscores, or numbers, but can't start with a number
+BOOL b_variable_ok(char *token)
+{
+ if (!b_check_token(token, "fv*"))
+ {
+ llerrs << "Token '" << token << "' isn't a variable!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// An integer is made up of the digits 0-9 and may be preceded by a '-'
+BOOL b_integer_ok(char *token)
+{
+ if (!b_check_token(token, "sd*"))
+ {
+ llerrs << "Token isn't an integer!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// An integer is made up of the digits 0-9
+BOOL b_positive_integer_ok(char *token)
+{
+ if (!b_check_token(token, "d*"))
+ {
+ llerrs << "Token isn't an integer!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void LLMessageSystem::init()
+{
+ // initialize member variables
+ mVerboseLog = FALSE;
+
+ mbError = FALSE;
+ mErrorCode = 0;
+ mIncomingCompressedSize = 0;
+ mSendReliable = FALSE;
+
+ mbSBuilt = FALSE;
+ mbSClear = TRUE;
+
+ mUnackedListDepth = 0;
+ mUnackedListSize = 0;
+ mDSMaxListDepth = 0;
+
+ mCurrentRMessageData = NULL;
+ mCurrentRMessageTemplate = NULL;
+
+ mCurrentSMessageData = NULL;
+ mCurrentSMessageTemplate = NULL;
+ mCurrentSMessageName = NULL;
+
+ mCurrentRecvPacketID = 0;
+
+ mNumberHighFreqMessages = 0;
+ mNumberMediumFreqMessages = 0;
+ mNumberLowFreqMessages = 0;
+ mPacketsIn = mPacketsOut = 0;
+ mBytesIn = mBytesOut = 0;
+ mCompressedPacketsIn = mCompressedPacketsOut = 0;
+ mReliablePacketsIn = mReliablePacketsOut = 0;
+
+ mCompressedBytesIn = 0;
+ mCompressedBytesOut = 0;
+ mUncompressedBytesIn = 0;
+ mUncompressedBytesOut = 0;
+ mTotalBytesIn = 0;
+ mTotalBytesOut = 0;
+
+ mDroppedPackets = 0; // total dropped packets in
+ mResentPackets = 0; // total resent packets out
+ mFailedResendPackets = 0; // total resend failure packets out
+ mOffCircuitPackets = 0; // total # of off-circuit packets rejected
+ mInvalidOnCircuitPackets = 0; // total # of on-circuit packets rejected
+
+ mOurCircuitCode = 0;
+
+ mMessageFileChecksum = 0;
+ mMessageFileVersionNumber = 0.f;
+}
+
+LLMessageSystem::LLMessageSystem()
+{
+ init();
+
+ mSystemVersionMajor = 0;
+ mSystemVersionMinor = 0;
+ mSystemVersionPatch = 0;
+ mSystemVersionServer = 0;
+ mVersionFlags = 0x0;
+
+ // default to not accepting packets from not alive circuits
+ mbProtected = TRUE;
+
+ mSendPacketFailureCount = 0;
+ mCircuitPrintFreq = 0.f; // seconds
+
+ // initialize various bits of net info
+ mSocket = 0;
+ mPort = 0;
+
+ mPollInfop = NULL;
+
+ mResendDumpTime = 0;
+ mMessageCountTime = 0;
+ mCircuitPrintTime = 0;
+ mCurrentMessageTimeSeconds = 0;
+
+ // Constants for dumping output based on message processing time/count
+ mNumMessageCounts = 0;
+ mMaxMessageCounts = 0; // >= 0 means dump warnings
+ mMaxMessageTime = 0.f;
+
+ mTrueReceiveSize = 0;
+
+ // Error if checking this state, subclass methods which aren't implemented are delegated
+ // to properly constructed message system.
+ mbError = TRUE;
+}
+
+// Read file and build message templates
+LLMessageSystem::LLMessageSystem(const char *filename, U32 port,
+ S32 version_major,
+ S32 version_minor,
+ S32 version_patch)
+{
+ init();
+
+ mSystemVersionMajor = version_major;
+ mSystemVersionMinor = version_minor;
+ mSystemVersionPatch = version_patch;
+ mSystemVersionServer = 0;
+ mVersionFlags = 0x0;
+
+ // default to not accepting packets from not alive circuits
+ mbProtected = TRUE;
+
+ mSendPacketFailureCount = 0;
+
+ mCircuitPrintFreq = 60.f; // seconds
+
+ loadTemplateFile(filename);
+
+ // initialize various bits of net info
+ mSocket = 0;
+ mPort = port;
+
+ S32 error = start_net(mSocket, mPort);
+ if (error != 0)
+ {
+ mbError = TRUE;
+ mErrorCode = error;
+ }
+ //llinfos << << "*** port: " << mPort << llendl;
+
+ //
+ // Create the data structure that we can poll on
+ //
+ if (!gAPRPoolp)
+ {
+ llerrs << "No APR pool before message system initialization!" << llendl;
+ ll_init_apr();
+ }
+ apr_socket_t *aprSocketp = NULL;
+ apr_os_sock_put(&aprSocketp, (apr_os_sock_t*)&mSocket, gAPRPoolp);
+
+ mPollInfop = new LLMessagePollInfo;
+ mPollInfop->mAPRSocketp = aprSocketp;
+ mPollInfop->mPollFD.p = gAPRPoolp;
+ mPollInfop->mPollFD.desc_type = APR_POLL_SOCKET;
+ mPollInfop->mPollFD.reqevents = APR_POLLIN;
+ mPollInfop->mPollFD.rtnevents = 0;
+ mPollInfop->mPollFD.desc.s = aprSocketp;
+ mPollInfop->mPollFD.client_data = NULL;
+
+ F64 mt_sec = getMessageTimeSeconds();
+ mResendDumpTime = mt_sec;
+ mMessageCountTime = mt_sec;
+ mCircuitPrintTime = mt_sec;
+ mCurrentMessageTimeSeconds = mt_sec;
+
+ // Constants for dumping output based on message processing time/count
+ mNumMessageCounts = 0;
+ mMaxMessageCounts = 200; // >= 0 means dump warnings
+ mMaxMessageTime = 1.f;
+
+ mTrueReceiveSize = 0;
+}
+
+// Read file and build message templates
+void LLMessageSystem::loadTemplateFile(const char* filename)
+{
+ if(!filename)
+ {
+ llerrs << "No template filename specified" << llendl;
+ }
+
+ char token[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+
+ // state variables
+ BOOL b_template_start = TRUE;
+ BOOL b_template_end = FALSE;
+ BOOL b_template = FALSE;
+ BOOL b_block_start = FALSE;
+ BOOL b_block_end = FALSE;
+ BOOL b_block = FALSE;
+ BOOL b_variable_start = FALSE;
+ BOOL b_variable_end = FALSE;
+ BOOL b_variable = FALSE;
+ //BOOL b_in_comment_block = FALSE; // not yet used
+
+ // working temp variables
+ LLMessageTemplate *templatep = NULL;
+ char template_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+
+ LLMessageBlock *blockp = NULL;
+ char block_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+
+ LLMessageVariable var;
+ char var_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+ char formatString[MAX_MESSAGE_INTERNAL_NAME_SIZE];
+
+ FILE* messagefilep = NULL;
+ mMessageFileChecksum = 0;
+ mMessageFileVersionNumber = 0.f;
+ S32 checksum_offset = 0;
+ char* checkp = NULL;
+
+ snprintf(formatString, sizeof(formatString), "%%%ds", MAX_MESSAGE_INTERNAL_NAME_SIZE);
+ messagefilep = LLFile::fopen(filename, "r");
+ if (messagefilep)
+ {
+// mName = gMessageStringTable.getString(filename);
+
+ fseek(messagefilep, 0L, SEEK_SET );
+ while(fscanf(messagefilep, formatString, token) != EOF)
+ {
+ // skip comments
+ if (token[0] == '/')
+ {
+ // skip to end of line
+ while (token[0] != 10)
+ fscanf(messagefilep, "%c", token);
+ continue;
+ }
+
+ checkp = token;
+
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // what are we looking for
+ if (!strcmp(token, "{"))
+ {
+ // is that a legit option?
+ if (b_template_start)
+ {
+ // yup!
+ b_template_start = FALSE;
+
+ // remember that it could be only a signal message, so name is all that it contains
+ b_template_end = TRUE;
+
+ // start working on it!
+ b_template = TRUE;
+ }
+ else if (b_block_start)
+ {
+ // yup!
+ b_block_start = FALSE;
+ b_template_end = FALSE;
+
+ // start working on it!
+ b_block = TRUE;
+ }
+ else if (b_variable_start)
+ {
+ // yup!
+ b_variable_start = FALSE;
+ b_block_end = FALSE;
+
+ // start working on it!
+ b_variable = TRUE;
+ }
+ else
+ {
+ llerrs << "Detcted unexpected token '" << token
+ << "' while parsing template." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ }
+
+ if (!strcmp(token, "}"))
+ {
+ // is that a legit option?
+ if (b_template_end)
+ {
+ // yup!
+ b_template_end = FALSE;
+ b_template = FALSE;
+ b_block_start = FALSE;
+
+ // add data!
+ // we've gotten a complete variable! hooray!
+ // add it!
+ addTemplate(templatep);
+
+ //llinfos << "Read template: "templatep->mNametemp_str
+ // << llendl;
+
+ // look for next one!
+ b_template_start = TRUE;
+ }
+ else if (b_block_end)
+ {
+ // yup!
+ b_block_end = FALSE;
+ b_variable_start = FALSE;
+
+ // add data!
+ // we've gotten a complete variable! hooray!
+ // add it to template
+
+ templatep->addBlock(blockp);
+
+ // start working on it!
+ b_template_end = TRUE;
+ b_block_start = TRUE;
+ }
+ else if (b_variable_end)
+ {
+ // yup!
+ b_variable_end = FALSE;
+
+ // add data!
+ // we've gotten a complete variable! hooray!
+ // add it to block
+ blockp->addVariable(var.getName(), var.getType(), var.getSize());
+
+ // start working on it!
+ b_variable_start = TRUE;
+ b_block_end = TRUE;
+ }
+ else
+ {
+ llerrs << "Detcted unexpected token '" << token
+ << "' while parsing template." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ }
+
+ // now, are we looking to start a template?
+ if (b_template)
+ {
+
+ b_template = FALSE;
+
+ // name first
+ if (fscanf(messagefilep, formatString, template_name) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected message template name, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // debugging to help figure out busted templates
+ //llinfos << template_name << llendl;
+
+ // is name a legit C variable name
+ if (!b_variable_ok(template_name))
+ {
+ // nope!
+ llerrs << "Not legal message template name: "
+ << template_name << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = template_name;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // ok, now get Frequency ("High", "Medium", or "Low")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected message template frequency, found EOF."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // which one is it?
+ if (!strcmp(token, "High"))
+ {
+ if (++mNumberHighFreqMessages == 255)
+ {
+ // oops, too many High Frequency messages!!
+ llerrs << "Message " << template_name
+ << " exceeded 254 High frequency messages!"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a template!
+ // message number is just mNumberHighFreqMessages
+ templatep = new LLMessageTemplate(template_name, mNumberHighFreqMessages, MFT_HIGH);
+ //lldebugs << "Template " << template_name << " # "
+ // << std::hex << mNumberHighFreqMessages
+ // << std::dec << " high"
+ // << llendl;
+ }
+ else if (!strcmp(token, "Medium"))
+ {
+ if (++mNumberMediumFreqMessages == 255)
+ {
+ // oops, too many Medium Frequency messages!!
+ llerrs << "Message " << template_name
+ << " exceeded 254 Medium frequency messages!"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a template!
+ // message number is ((255 << 8) | mNumberMediumFreqMessages)
+ templatep = new LLMessageTemplate(template_name, (255 << 8) | mNumberMediumFreqMessages, MFT_MEDIUM);
+ //lldebugs << "Template " << template_name << " # "
+ // << std::hex << mNumberMediumFreqMessages
+ // << std::dec << " medium"
+ // << llendl;
+ }
+ else if (!strcmp(token, "Low"))
+ {
+ if (++mNumberLowFreqMessages == 65535)
+ {
+ // oops, too many High Frequency messages!!
+ llerrs << "Message " << template_name
+ << " exceeded 65534 Low frequency messages!"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a template!
+ // message number is ((255 << 24) | (255 << 16) | mNumberLowFreqMessages)
+ templatep = new LLMessageTemplate(template_name, (255 << 24) | (255 << 16) | mNumberLowFreqMessages, MFT_LOW);
+ //lldebugs << "Template " << template_name << " # "
+ // << std::hex << mNumberLowFreqMessages
+ // << std::dec << " low"
+ // << llendl;
+ }
+ else if (!strcmp(token, "Fixed"))
+ {
+ U32 message_num = 0;
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected message template number (fixed),"
+ << " found EOF." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ message_num = strtoul(token,NULL,0);
+
+ // ok, we can create a template!
+ // message number is ((255 << 24) | (255 << 16) | mNumberLowFreqMessages)
+ templatep = new LLMessageTemplate(template_name, message_num, MFT_LOW);
+ }
+ else
+ {
+ // oops, bad frequency line
+ llerrs << "Bad frequency! " << token
+ << " isn't High, Medium, or Low" << llendl
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // Now get trust ("Trusted", "NotTrusted")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // File ended
+ llerrs << "Expected message template "
+ "trust, but file ended."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32) *checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ if (strcmp(token, "Trusted") == 0)
+ {
+ templatep->setTrust(MT_TRUST);
+ }
+ else if (strcmp(token, "NotTrusted") == 0)
+ {
+ templatep->setTrust(MT_NOTRUST);
+ }
+ else
+ {
+ // bad trust token
+ llerrs << "bad trust: " << token
+ << " isn't Trusted or NotTrusted"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // get encoding
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // File ended
+ llerrs << "Expected message encoding, but file ended."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ checkp = token;
+ while(*checkp)
+ {
+ mMessageFileChecksum += ((U32) *checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ if(0 == strcmp(token, "Unencoded"))
+ {
+ templatep->setEncoding(ME_UNENCODED);
+ }
+ else if(0 == strcmp(token, "Zerocoded"))
+ {
+ templatep->setEncoding(ME_ZEROCODED);
+ }
+ else
+ {
+ // bad trust token
+ llerrs << "bad encoding: " << token
+ << " isn't Unencoded or Zerocoded" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // ok, now we need to look for a block
+ b_block_start = TRUE;
+ continue;
+ }
+
+ // now, are we looking to start a template?
+ if (b_block)
+ {
+ b_block = FALSE;
+ // ok, need to pull header info
+
+ // name first
+ if (fscanf(messagefilep, formatString, block_name) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected block name, but file ended" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = block_name;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is name a legit C variable name
+ if (!b_variable_ok(block_name))
+ {
+ // nope!
+ llerrs << block_name << "is not a legal block name"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // now, block type ("Single", "Multiple", or "Variable")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected block type, but file ended." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // which one is it?
+ if (!strcmp(token, "Single"))
+ {
+ // ok, we can create a block
+ blockp = new LLMessageBlock(block_name, MBT_SINGLE);
+ }
+ else if (!strcmp(token, "Multiple"))
+ {
+ // need to get the number of repeats
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected block multiple count,"
+ " but file ended." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is it a legal integer
+ if (!b_positive_integer_ok(token))
+ {
+ // nope!
+ llerrs << token << "is not a legal integer for"
+ " block multiple count" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a block
+ blockp = new LLMessageBlock(block_name, MBT_MULTIPLE, atoi(token));
+ }
+ else if (!strcmp(token, "Variable"))
+ {
+ // ok, we can create a block
+ blockp = new LLMessageBlock(block_name, MBT_VARIABLE);
+ }
+ else
+ {
+ // oops, bad block type
+ llerrs << "Bad block type! " << token
+ << " isn't Single, Multiple, or Variable" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, now we need to look for a variable
+ b_variable_start = TRUE;
+ continue;
+ }
+
+ // now, are we looking to start a template?
+ if (b_variable)
+ {
+ b_variable = FALSE;
+ // ok, need to pull header info
+
+ // name first
+ if (fscanf(messagefilep, formatString, var_name) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable name, but file ended."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = var_name;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is name a legit C variable name
+ if (!b_variable_ok(var_name))
+ {
+ // nope!
+ llerrs << var_name << " is not a legal variable name"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // now, variable type ("Fixed" or "Variable")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable type, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+
+ // which one is it?
+ if (!strcmp(token, "U8"))
+ {
+ var = LLMessageVariable(var_name, MVT_U8, 1);
+ }
+ else if (!strcmp(token, "U16"))
+ {
+ var = LLMessageVariable(var_name, MVT_U16, 2);
+ }
+ else if (!strcmp(token, "U32"))
+ {
+ var = LLMessageVariable(var_name, MVT_U32, 4);
+ }
+ else if (!strcmp(token, "U64"))
+ {
+ var = LLMessageVariable(var_name, MVT_U64, 8);
+ }
+ else if (!strcmp(token, "S8"))
+ {
+ var = LLMessageVariable(var_name, MVT_S8, 1);
+ }
+ else if (!strcmp(token, "S16"))
+ {
+ var = LLMessageVariable(var_name, MVT_S16, 2);
+ }
+ else if (!strcmp(token, "S32"))
+ {
+ var = LLMessageVariable(var_name, MVT_S32, 4);
+ }
+ else if (!strcmp(token, "S64"))
+ {
+ var = LLMessageVariable(var_name, MVT_S64, 8);
+ }
+ else if (!strcmp(token, "F32"))
+ {
+ var = LLMessageVariable(var_name, MVT_F32, 4);
+ }
+ else if (!strcmp(token, "F64"))
+ {
+ var = LLMessageVariable(var_name, MVT_F64, 8);
+ }
+ else if (!strcmp(token, "LLVector3"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLVector3, 12);
+ }
+ else if (!strcmp(token, "LLVector3d"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLVector3d, 24);
+ }
+ else if (!strcmp(token, "LLVector4"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLVector4, 16);
+ }
+ else if (!strcmp(token, "LLQuaternion"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLQuaternion, 12);
+ }
+ else if (!strcmp(token, "LLUUID"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLUUID, 16);
+ }
+ else if (!strcmp(token, "BOOL"))
+ {
+ var = LLMessageVariable(var_name, MVT_BOOL, 1);
+ }
+ else if (!strcmp(token, "IPADDR"))
+ {
+ var = LLMessageVariable(var_name, MVT_IP_ADDR, 4);
+ }
+ else if (!strcmp(token, "IPPORT"))
+ {
+ var = LLMessageVariable(var_name, MVT_IP_PORT, 2);
+ }
+ else if (!strcmp(token, "Fixed"))
+ {
+ // need to get the variable size
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable size, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is it a legal integer
+ if (!b_positive_integer_ok(token))
+ {
+ // nope!
+ llerrs << token << " is not a legal integer for"
+ " variable size" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a block
+ var = LLMessageVariable(var_name, MVT_FIXED, atoi(token));
+ }
+ else if (!strcmp(token, "Variable"))
+ {
+ // need to get the variable size
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable size, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is it a legal integer
+ if (!b_positive_integer_ok(token))
+ {
+ // nope!
+ llerrs << token << "is not a legal integer"
+ " for variable size" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a block
+ var = LLMessageVariable(var_name, MVT_VARIABLE, atoi(token));
+ }
+ else
+ {
+ // oops, bad variable type
+ llerrs << "Bad variable type! " << token
+ << " isn't Fixed or Variable" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // we got us a variable!
+ b_variable_end = TRUE;
+ continue;
+ }
+
+ // do we have a version number stuck in the file?
+ if (!strcmp(token, "version"))
+ {
+ // version number
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected version number, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ mMessageFileVersionNumber = (F32)atof(token);
+
+// llinfos << "### Message template version " << mMessageFileVersionNumber << " ###" << llendl;
+ continue;
+ }
+ }
+
+ llinfos << "Message template checksum = " << std::hex << mMessageFileChecksum << std::dec << llendl;
+ }
+ else
+ {
+ llwarns << "Failed to open template: " << filename << llendl;
+ mbError = TRUE;
+ return;
+ }
+ fclose(messagefilep);
+}
+
+
+LLMessageSystem::~LLMessageSystem()
+{
+ mMessageTemplates.clear(); // don't delete templates.
+ for_each(mMessageNumbers.begin(), mMessageNumbers.end(), DeletePairedPointer());
+ mMessageNumbers.clear();
+
+ if (!mbError)
+ {
+ end_net();
+ }
+
+ delete mCurrentRMessageData;
+ mCurrentRMessageData = NULL;
+
+ delete mCurrentSMessageData;
+ mCurrentSMessageData = NULL;
+
+ delete mPollInfop;
+ mPollInfop = NULL;
+}
+
+void LLMessageSystem::clearReceiveState()
+{
+ mReceiveSize = -1;
+ mCurrentRecvPacketID = 0;
+ mCurrentRMessageTemplate = NULL;
+ delete mCurrentRMessageData;
+ mCurrentRMessageData = NULL;
+ mIncomingCompressedSize = 0;
+ mLastSender.invalidate();
+}
+
+
+BOOL LLMessageSystem::poll(F32 seconds)
+{
+ S32 num_socks;
+ apr_status_t status;
+ status = apr_poll(&(mPollInfop->mPollFD), 1, &num_socks,(U64)(seconds*1000000.f));
+ if (status != APR_TIMEUP)
+ {
+ ll_apr_warn_status(status);
+ }
+ if (num_socks)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+// Returns TRUE if a valid, on-circuit message has been received.
+BOOL LLMessageSystem::checkMessages( S64 frame_count )
+{
+ BOOL valid_packet = FALSE;
+
+ LLTransferTargetVFile::updateQueue();
+
+ if (!mNumMessageCounts)
+ {
+ // This is the first message being handled after a resetReceiveCounts, we must be starting
+ // the message processing loop. Reset the timers.
+ mCurrentMessageTimeSeconds = totalTime() * SEC_PER_USEC;
+ mMessageCountTime = getMessageTimeSeconds();
+ }
+
+ // loop until either no packets or a valid packet
+ // i.e., burn through packets from unregistered circuits
+ do
+ {
+ clearReceiveState();
+
+ BOOL recv_reliable = FALSE;
+ BOOL recv_resent = FALSE;
+ S32 acks = 0;
+ S32 true_rcv_size = 0;
+
+ U8* buffer = mTrueReceiveBuffer;
+
+ mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer);
+ // If you want to dump all received packets into SecondLife.log, uncomment this
+ //dumpPacketToLog();
+
+ mReceiveSize = mTrueReceiveSize;
+ mLastSender = mPacketRing.getLastSender();
+
+ if (mReceiveSize < (S32) LL_MINIMUM_VALID_PACKET_SIZE)
+ {
+ // A receive size of zero is OK, that means that there are no more packets available.
+ // Ones that are non-zero but below the minimum packet size are worrisome.
+ if (mReceiveSize > 0)
+ {
+ llwarns << "Invalid (too short) packet discarded " << mReceiveSize << llendl;
+ callExceptionFunc(MX_PACKET_TOO_SHORT);
+ }
+ // no data in packet receive buffer
+ valid_packet = FALSE;
+ }
+ else
+ {
+ LLHost host;
+ LLCircuitData *cdp;
+
+ // note if packet acks are appended.
+ if(buffer[0] & LL_ACK_FLAG)
+ {
+ acks += buffer[--mReceiveSize];
+ true_rcv_size = mReceiveSize;
+ mReceiveSize -= acks * sizeof(TPACKETID);
+ }
+
+ // process the message as normal
+
+ mIncomingCompressedSize = zeroCodeExpand(&buffer,&mReceiveSize);
+ mCurrentRecvPacketID = buffer[1] + ((buffer[0] & 0x0f ) * 256);
+ if (sizeof(TPACKETID) == 4)
+ {
+ mCurrentRecvPacketID *= 256;
+ mCurrentRecvPacketID += buffer[2];
+ mCurrentRecvPacketID *= 256;
+ mCurrentRecvPacketID += buffer[3];
+ }
+
+ host = getSender();
+ //llinfos << host << ":" << mCurrentRecvPacketID << llendl;
+
+ // For testing the weird case we're having in the office where the first few packets
+ // on a connection get dropped
+ //if ((mCurrentRecvPacketID < 8) && !(buffer[0] & LL_RESENT_FLAG))
+ //{
+ // llinfos << "Evil! Dropping " << mCurrentRecvPacketID << " from " << host << " for fun!" << llendl;
+ // continue;
+ //}
+
+ cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ // This packet comes from a circuit we don't know about.
+
+ // Are we rejecting off-circuit packets?
+ if (mbProtected)
+ {
+ // cdp is already NULL, so we don't need to unset it.
+ }
+ else
+ {
+ // nope, open the new circuit
+ cdp = mCircuitInfo.addCircuitData(host, mCurrentRecvPacketID);
+
+ // I added this - I think it's correct - DJS
+ // reset packet in ID
+ cdp->setPacketInID(mCurrentRecvPacketID);
+
+ // And claim the packet is on the circuit we just added.
+ }
+ }
+ else
+ {
+ // this is an old circuit. . . is it still alive?
+ if (!cdp->isAlive())
+ {
+ // nope. don't accept if we're protected
+ if (mbProtected)
+ {
+ // don't accept packets from unexpected sources
+ cdp = NULL;
+ }
+ else
+ {
+ // wake up the circuit
+ cdp->setAlive(TRUE);
+
+ // reset packet in ID
+ cdp->setPacketInID(mCurrentRecvPacketID);
+ }
+ }
+ }
+
+ // At this point, cdp is now a pointer to the circuit that
+ // this message came in on if it's valid, and NULL if the
+ // circuit was bogus.
+
+ if(cdp && (acks > 0) && ((S32)(acks * sizeof(TPACKETID)) < (true_rcv_size)))
+ {
+ TPACKETID packet_id;
+ U32 mem_id=0;
+ for(S32 i = 0; i < acks; ++i)
+ {
+ true_rcv_size -= sizeof(TPACKETID);
+ memcpy(&mem_id, &mTrueReceiveBuffer[true_rcv_size], /* Flawfinder: ignore*/
+ sizeof(TPACKETID));
+ packet_id = ntohl(mem_id);
+ //llinfos << "got ack: " << packet_id << llendl;
+ cdp->ackReliablePacket(packet_id);
+ }
+ if (!cdp->getUnackedPacketCount())
+ {
+ // Remove this circuit from the list of circuits with unacked packets
+ mCircuitInfo.mUnackedCircuitMap.erase(cdp->mHost);
+ }
+ }
+
+ if (buffer[0] & LL_RELIABLE_FLAG)
+ {
+ recv_reliable = TRUE;
+ }
+ if (buffer[0] & LL_RESENT_FLAG)
+ {
+ recv_resent = TRUE;
+ if (cdp && cdp->isDuplicateResend(mCurrentRecvPacketID))
+ {
+ // We need to ACK here to suppress
+ // further resends of packets we've
+ // already seen.
+ if (recv_reliable)
+ {
+ //mAckList.addData(new LLPacketAck(host, mCurrentRecvPacketID));
+ // ***************************************
+ // TESTING CODE
+ //if(mCircuitInfo.mCurrentCircuit->mHost != host)
+ //{
+ // llwarns << "DISCARDED PACKET HOST MISMATCH! HOST: "
+ // << host << " CIRCUIT: "
+ // << mCircuitInfo.mCurrentCircuit->mHost
+ // << llendl;
+ //}
+ // ***************************************
+ //mCircuitInfo.mCurrentCircuit->mAcks.put(mCurrentRecvPacketID);
+ cdp->collectRAck(mCurrentRecvPacketID);
+ }
+
+ //llinfos << "Discarding duplicate resend from " << host << llendl;
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore*/
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize : mReceiveSize), mCurrentRecvPacketID);/* Flawfinder: ignore*/
+ str << buffer << "(unknown)"
+ << (recv_reliable ? " reliable" : "")
+ << " resent "
+ << ((acks > 0) ? "acks" : "")
+ << " DISCARD DUPLICATE";
+ llinfos << str.str() << llendl;
+ }
+ mPacketsIn++;
+ valid_packet = FALSE;
+ continue;
+ }
+ }
+
+ // UseCircuitCode can be a valid, off-circuit packet.
+ // But we don't want to acknowledge UseCircuitCode until the circuit is
+ // available, which is why the acknowledgement test is done above. JC
+
+ valid_packet = decodeTemplate( buffer, mReceiveSize, &mCurrentRMessageTemplate );
+ if( valid_packet )
+ {
+ mCurrentRMessageTemplate->mReceiveCount++;
+ lldebugst(LLERR_MESSAGE) << "MessageRecvd:" << mCurrentRMessageTemplate->mName << " from " << host << llendl;
+ }
+
+ // UseCircuitCode is allowed in even from an invalid circuit, so that
+ // we can toss circuits around.
+ if (valid_packet && !cdp && (mCurrentRMessageTemplate->mName != _PREHASH_UseCircuitCode) )
+ {
+ logMsgFromInvalidCircuit( host, recv_reliable );
+ clearReceiveState();
+ valid_packet = FALSE;
+ }
+
+ if (valid_packet && cdp && !cdp->getTrusted() && (mCurrentRMessageTemplate->getTrust() == MT_TRUST) )
+ {
+ logTrustedMsgFromUntrustedCircuit( host );
+ clearReceiveState();
+
+ sendDenyTrustedCircuit(host);
+ valid_packet = FALSE;
+ }
+
+ if (valid_packet
+ && mCurrentRMessageTemplate->isBanned(cdp && cdp->getTrusted()))
+ {
+ llwarns << "LLMessageSystem::checkMessages "
+ << "received banned message "
+ << mCurrentRMessageTemplate->mName
+ << " from "
+ << ((cdp && cdp->getTrusted()) ? "trusted " : "untrusted ")
+ << host << llendl;
+ clearReceiveState();
+ valid_packet = FALSE;
+ }
+
+ if( valid_packet )
+ {
+ logValidMsg(cdp, host, recv_reliable, recv_resent, (BOOL)(acks>0) );
+
+ valid_packet = decodeData( buffer, host );
+ }
+
+ // It's possible that the circuit went away, because ANY message can disable the circuit
+ // (for example, UseCircuit, CloseCircuit, DisableSimulator). Find it again.
+ cdp = mCircuitInfo.findCircuit(host);
+
+ if (valid_packet)
+ {
+ /* Code for dumping the complete contents of a message. Keep for future use in optimizing messages.
+ if( 1 )
+ {
+ static char* object_update = gMessageStringTable.getString("ObjectUpdate");
+ if(object_update == mCurrentRMessageTemplate->mName )
+ {
+ llinfos << "ObjectUpdate:" << llendl;
+ U32 i;
+ llinfos << " Zero Encoded: " << zero_unexpanded_size << llendl;
+ for( i = 0; i<zero_unexpanded_size; i++ )
+ {
+ llinfos << " " << i << ": " << (U32) zero_unexpanded_buffer[i] << llendl;
+ }
+ llinfos << "" << llendl;
+
+ llinfos << " Zero Unencoded: " << mReceiveSize << llendl;
+ for( i = 0; i<mReceiveSize; i++ )
+ {
+ llinfos << " " << i << ": " << (U32) buffer[i] << llendl;
+ }
+ llinfos << "" << llendl;
+
+ llinfos << " Blocks and variables: " << llendl;
+ S32 byte_count = 0;
+ for (LLMessageTemplate::message_block_map_t::iterator
+ iter = mCurrentRMessageTemplate->mMemberBlocks.begin(),
+ end = mCurrentRMessageTemplate->mMemberBlocks.end();
+ iter != end; iter++)
+ {
+ LLMessageBlock* block = iter->second;
+ const char* block_name = block->mName;
+ for (LLMsgBlkData::msg_var_data_map_t::iterator
+ iter = block->mMemberVariables.begin(),
+ end = block->mMemberVariables.end();
+ iter != end; iter++)
+ {
+ const char* var_name = iter->first;
+
+ if( getNumberOfBlocksFast( block_name ) < 1 )
+ {
+ llinfos << var_name << " has no blocks" << llendl;
+ }
+ for( S32 blocknum = 0; blocknum < getNumberOfBlocksFast( block_name ); blocknum++ )
+ {
+ char *bnamep = (char *)block_name + blocknum; // this works because it's just a hash. The bnamep is never derefference
+ char *vnamep = (char *)var_name;
+
+ LLMsgBlkData *msg_block_data = mCurrentRMessageData->mMemberBlocks[bnamep];
+
+ char errmsg[1024];
+ if (!msg_block_data)
+ {
+ sprintf(errmsg, "Block %s #%d not in message %s", block_name, blocknum, mCurrentRMessageData->mName);
+ llerrs << errmsg << llendl;
+ }
+
+ LLMsgVarData vardata = msg_block_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ sprintf(errmsg, "Variable %s not in message %s block %s", vnamep, mCurrentRMessageData->mName, bnamep);
+ llerrs << errmsg << llendl;
+ }
+
+ const S32 vardata_size = vardata.getSize();
+ if( vardata_size )
+ {
+ for( i = 0; i < vardata_size; i++ )
+ {
+ byte_count++;
+ llinfos << block_name << " " << var_name << " [" << blocknum << "][" << i << "]= " << (U32)(((U8*)vardata.getData())[i]) << llendl;
+ }
+ }
+ else
+ {
+ llinfos << block_name << " " << var_name << " [" << blocknum << "] 0 bytes" << llendl;
+ }
+ }
+ }
+ }
+ llinfos << "Byte count =" << byte_count << llendl;
+ }
+ }
+ */
+
+ mPacketsIn++;
+ mBytesIn += mTrueReceiveSize;
+
+ // ACK here for valid packets that we've seen
+ // for the first time.
+ if (cdp && recv_reliable)
+ {
+ // Add to the recently received list for duplicate suppression
+ cdp->mRecentlyReceivedReliablePackets[mCurrentRecvPacketID] = getMessageTimeUsecs();
+
+ // Put it onto the list of packets to be acked
+ cdp->collectRAck(mCurrentRecvPacketID);
+ mReliablePacketsIn++;
+ }
+ }
+ else
+ {
+ if (mbProtected && (!cdp))
+ {
+ llwarns << "Packet "
+ << (mCurrentRMessageTemplate ? mCurrentRMessageTemplate->mName : "")
+ << " from invalid circuit " << host << llendl;
+ mOffCircuitPackets++;
+ }
+ else
+ {
+ mInvalidOnCircuitPackets++;
+ }
+ }
+
+ // Code for dumping the complete contents of a message
+ // delete [] zero_unexpanded_buffer;
+ }
+ } while (!valid_packet && mReceiveSize > 0);
+
+ F64 mt_sec = getMessageTimeSeconds();
+ // Check to see if we need to print debug info
+ if ((mt_sec - mCircuitPrintTime) > mCircuitPrintFreq)
+ {
+ dumpCircuitInfo();
+ mCircuitPrintTime = mt_sec;
+ }
+
+ if( !valid_packet )
+ {
+ clearReceiveState();
+ }
+
+ return valid_packet;
+}
+
+S32 LLMessageSystem::getReceiveBytes() const
+{
+ if (getReceiveCompressedSize())
+ {
+ return getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ return getReceiveSize() * 8;
+ }
+}
+
+
+void LLMessageSystem::processAcks()
+{
+ F64 mt_sec = getMessageTimeSeconds();
+ {
+ gTransferManager.updateTransfers();
+
+ if (gXferManager)
+ {
+ gXferManager->retransmitUnackedPackets();
+ }
+
+ if (gAssetStorage)
+ {
+ gAssetStorage->checkForTimeouts();
+ }
+ }
+
+ BOOL dump = FALSE;
+ {
+ // Check the status of circuits
+ mCircuitInfo.updateWatchDogTimers(this);
+
+ //resend any necessary packets
+ mCircuitInfo.resendUnackedPackets(mUnackedListDepth, mUnackedListSize);
+
+ //cycle through ack list for each host we need to send acks to
+ mCircuitInfo.sendAcks();
+
+ if (!mDenyTrustedCircuitSet.empty())
+ {
+ llinfos << "Sending queued DenyTrustedCircuit messages." << llendl;
+ for (host_set_t::iterator hostit = mDenyTrustedCircuitSet.begin(); hostit != mDenyTrustedCircuitSet.end(); ++hostit)
+ {
+ reallySendDenyTrustedCircuit(*hostit);
+ }
+ mDenyTrustedCircuitSet.clear();
+ }
+
+ if (mMaxMessageCounts >= 0)
+ {
+ if (mNumMessageCounts >= mMaxMessageCounts)
+ {
+ dump = TRUE;
+ }
+ }
+
+ if (mMaxMessageTime >= 0.f)
+ {
+ // This is one of the only places where we're required to get REAL message system time.
+ mReceiveTime = (F32)(getMessageTimeSeconds(TRUE) - mMessageCountTime);
+ if (mReceiveTime > mMaxMessageTime)
+ {
+ dump = TRUE;
+ }
+ }
+ }
+
+ if (dump)
+ {
+ dumpReceiveCounts();
+ }
+ resetReceiveCounts();
+
+ if ((mt_sec - mResendDumpTime) > CIRCUIT_DUMP_TIMEOUT)
+ {
+ mResendDumpTime = mt_sec;
+ mCircuitInfo.dumpResends();
+ }
+}
+
+
+void LLMessageSystem::newMessageFast(const char *name)
+{
+ mbSBuilt = FALSE;
+ mbSClear = FALSE;
+
+ mCurrentSendTotal = 0;
+ mSendReliable = FALSE;
+
+ char *namep = (char *)name;
+
+ if (mMessageTemplates.count(namep) > 0)
+ {
+ mCurrentSMessageTemplate = mMessageTemplates[namep];
+ if (mCurrentSMessageData)
+ {
+ delete mCurrentSMessageData;
+ }
+ mCurrentSMessageData = new LLMsgData(namep);
+ mCurrentSMessageName = namep;
+ mCurrentSDataBlock = NULL;
+ mCurrentSBlockName = NULL;
+
+ // add at one of each block
+ LLMessageTemplate* msg_template = mMessageTemplates[namep];
+ for (LLMessageTemplate::message_block_map_t::iterator iter = msg_template->mMemberBlocks.begin();
+ iter != msg_template->mMemberBlocks.end(); iter++)
+ {
+ LLMessageBlock* ci = iter->second;
+ LLMsgBlkData *tblockp;
+ tblockp = new LLMsgBlkData(ci->mName, 0);
+ mCurrentSMessageData->addBlock(tblockp);
+ }
+ }
+ else
+ {
+ llerrs << "newMessage - Message " << name << " not registered" << llendl;
+ }
+}
+
+void LLMessageSystem::copyMessageRtoS()
+{
+ if (!mCurrentRMessageTemplate)
+ {
+ return;
+ }
+ newMessageFast(mCurrentRMessageTemplate->mName);
+
+ // copy the blocks
+ // counting variables used to encode multiple block info
+ S32 block_count = 0;
+ char *block_name = NULL;
+
+ // loop through msg blocks to loop through variables, totalling up size data and filling the new (send) message
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.begin();
+ LLMsgData::msg_blk_data_map_t::iterator end = mCurrentRMessageData->mMemberBlocks.end();
+ for(; iter != end; ++iter)
+ {
+ LLMsgBlkData* mbci = iter->second;
+ if(!mbci) continue;
+
+ // do we need to encode a block code?
+ if (block_count == 0)
+ {
+ block_count = mbci->mBlockNumber;
+ block_name = (char *)mbci->mName;
+ }
+
+ // counting down mutliple blocks
+ block_count--;
+
+ nextBlockFast(block_name);
+
+ // now loop through the variables
+ LLMsgBlkData::msg_var_data_map_t::iterator dit = mbci->mMemberVarData.begin();
+ LLMsgBlkData::msg_var_data_map_t::iterator dend = mbci->mMemberVarData.end();
+
+ for(; dit != dend; ++dit)
+ {
+ LLMsgVarData& mvci = *dit;
+ addDataFast(mvci.getName(), mvci.getData(), mvci.getType(), mvci.getSize());
+ }
+ }
+}
+
+void LLMessageSystem::clearMessage()
+{
+ mbSBuilt = FALSE;
+ mbSClear = TRUE;
+
+ mCurrentSendTotal = 0;
+ mSendReliable = FALSE;
+
+ mCurrentSMessageTemplate = NULL;
+
+ delete mCurrentSMessageData;
+ mCurrentSMessageData = NULL;
+
+ mCurrentSMessageName = NULL;
+ mCurrentSDataBlock = NULL;
+ mCurrentSBlockName = NULL;
+}
+
+
+// set block to add data to within current message
+void LLMessageSystem::nextBlockFast(const char *blockname)
+{
+ char *bnamep = (char *)blockname;
+
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to setBlock" << llendl;
+ return;
+ }
+
+ // now, does this block exist?
+ LLMessageTemplate::message_block_map_t::iterator temp_iter = mCurrentSMessageTemplate->mMemberBlocks.find(bnamep);
+ if (temp_iter == mCurrentSMessageTemplate->mMemberBlocks.end())
+ {
+ llerrs << "LLMessageSystem::nextBlockFast " << bnamep
+ << " not a block in " << mCurrentSMessageTemplate->mName << llendl;
+ return;
+ }
+
+ LLMessageBlock* template_data = temp_iter->second;
+
+ // ok, have we already set this block?
+ LLMsgBlkData* block_data = mCurrentSMessageData->mMemberBlocks[bnamep];
+ if (block_data->mBlockNumber == 0)
+ {
+ // nope! set this as the current block
+ block_data->mBlockNumber = 1;
+ mCurrentSDataBlock = block_data;
+ mCurrentSBlockName = bnamep;
+
+ // add placeholders for each of the variables
+ for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin();
+ iter != template_data->mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ mCurrentSDataBlock->addVariable(ci.getName(), ci.getType());
+ }
+ return;
+ }
+ else
+ {
+ // already have this block. . .
+ // are we supposed to have a new one?
+
+ // if the block is type MBT_SINGLE this is bad!
+ if (template_data->mType == MBT_SINGLE)
+ {
+ llerrs << "LLMessageSystem::nextBlockFast called multiple times"
+ << " for " << bnamep << " but is type MBT_SINGLE" << llendl;
+ return;
+ }
+
+
+ // if the block is type MBT_MULTIPLE then we need a known number, make sure that we're not exceeding it
+ if ( (template_data->mType == MBT_MULTIPLE)
+ &&(mCurrentSDataBlock->mBlockNumber == template_data->mNumber))
+ {
+ llerrs << "LLMessageSystem::nextBlockFast called "
+ << mCurrentSDataBlock->mBlockNumber << " times for " << bnamep
+ << " exceeding " << template_data->mNumber
+ << " specified in type MBT_MULTIPLE." << llendl;
+ return;
+ }
+
+ // ok, we can make a new one
+ // modify the name to avoid name collision by adding number to end
+ S32 count = block_data->mBlockNumber;
+
+ // incrememt base name's count
+ block_data->mBlockNumber++;
+
+ if (block_data->mBlockNumber > MAX_BLOCKS)
+ {
+ llerrs << "Trying to pack too many blocks into MBT_VARIABLE type (limited to " << MAX_BLOCKS << ")" << llendl;
+ }
+
+ // create new name
+ // Nota Bene: if things are working correctly, mCurrentMessageData->mMemberBlocks[blockname]->mBlockNumber == mCurrentDataBlock->mBlockNumber + 1
+
+ char *nbnamep = bnamep + count;
+
+ mCurrentSDataBlock = new LLMsgBlkData(bnamep, count);
+ mCurrentSDataBlock->mName = nbnamep;
+ mCurrentSMessageData->mMemberBlocks[nbnamep] = mCurrentSDataBlock;
+
+ // add placeholders for each of the variables
+ for (LLMessageBlock::message_variable_map_t::iterator
+ iter = template_data->mMemberVariables.begin(),
+ end = template_data->mMemberVariables.end();
+ iter != end; iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ mCurrentSDataBlock->addVariable(ci.getName(), ci.getType());
+ }
+ return;
+ }
+}
+
+// add data to variable in current block
+void LLMessageSystem::addDataFast(const char *varname, const void *data, EMsgVariableType type, S32 size)
+{
+ char *vnamep = (char *)varname;
+
+ // do we have a current message?
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to addData" << llendl;
+ return;
+ }
+
+ // do we have a current block?
+ if (!mCurrentSDataBlock)
+ {
+ llerrs << "setBlock not called prior to addData" << llendl;
+ return;
+ }
+
+ // kewl, add the data if it exists
+ LLMessageVariable* var_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]->mMemberVariables[vnamep];
+ if (!var_data || !var_data->getName())
+ {
+ llerrs << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << llendl;
+ return;
+ }
+
+ // ok, it seems ok. . . are we the correct size?
+ if (var_data->getType() == MVT_VARIABLE)
+ {
+ // Variable 1 can only store 255 bytes, make sure our data is smaller
+ if ((var_data->getSize() == 1) &&
+ (size > 255))
+ {
+ llwarns << "Field " << varname << " is a Variable 1 but program "
+ << "attempted to stuff more than 255 bytes in "
+ << "(" << size << "). Clamping size and truncating data." << llendl;
+ size = 255;
+ char *truncate = (char *)data;
+ truncate[255] = 0;
+ }
+
+ // no correct size for MVT_VARIABLE, instead we need to tell how many bytes the size will be encoded as
+ mCurrentSDataBlock->addData(vnamep, data, size, type, var_data->getSize());
+ mCurrentSendTotal += size;
+ }
+ else
+ {
+ if (size != var_data->getSize())
+ {
+ llerrs << varname << " is type MVT_FIXED but request size " << size << " doesn't match template size "
+ << var_data->getSize() << llendl;
+ return;
+ }
+ // alright, smash it in
+ mCurrentSDataBlock->addData(vnamep, data, size, type);
+ mCurrentSendTotal += size;
+ }
+}
+
+// add data to variable in current block - fails if variable isn't MVT_FIXED
+void LLMessageSystem::addDataFast(const char *varname, const void *data, EMsgVariableType type)
+{
+ char *vnamep = (char *)varname;
+
+ // do we have a current message?
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to addData" << llendl;
+ return;
+ }
+
+ // do we have a current block?
+ if (!mCurrentSDataBlock)
+ {
+ llerrs << "setBlock not called prior to addData" << llendl;
+ return;
+ }
+
+ // kewl, add the data if it exists
+ LLMessageVariable* var_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]->mMemberVariables[vnamep];
+ if (!var_data->getName())
+ {
+ llerrs << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << llendl;
+ return;
+ }
+
+ // ok, it seems ok. . . are we MVT_VARIABLE?
+ if (var_data->getType() == MVT_VARIABLE)
+ {
+ // nope
+ llerrs << vnamep << " is type MVT_VARIABLE. Call using addData(name, data, size)" << llendl;
+ return;
+ }
+ else
+ {
+ mCurrentSDataBlock->addData(vnamep, data, var_data->getSize(), type);
+ mCurrentSendTotal += var_data->getSize();
+ }
+}
+
+BOOL LLMessageSystem::isSendFull(const char* blockname)
+{
+ if(!blockname)
+ {
+ return (mCurrentSendTotal > MTUBYTES);
+ }
+ return isSendFullFast(gMessageStringTable.getString(blockname));
+}
+
+BOOL LLMessageSystem::isSendFullFast(const char* blockname)
+{
+ if(mCurrentSendTotal > MTUBYTES)
+ {
+ return TRUE;
+ }
+ if(!blockname)
+ {
+ return FALSE;
+ }
+ char* bnamep = (char*)blockname;
+ S32 max;
+
+ LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[bnamep];
+
+ switch(template_data->mType)
+ {
+ case MBT_SINGLE:
+ max = 1;
+ break;
+ case MBT_MULTIPLE:
+ max = template_data->mNumber;
+ break;
+ case MBT_VARIABLE:
+ default:
+ max = MAX_BLOCKS;
+ break;
+ }
+ if(mCurrentSMessageData->mMemberBlocks[bnamep]->mBlockNumber >= max)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+// blow away the last block of a message, return FALSE if that leaves no blocks or there wasn't a block to remove
+BOOL LLMessageSystem::removeLastBlock()
+{
+ if (mCurrentSBlockName)
+ {
+ if ( (mCurrentSMessageData)
+ &&(mCurrentSMessageTemplate))
+ {
+ if (mCurrentSMessageData->mMemberBlocks[mCurrentSBlockName]->mBlockNumber >= 1)
+ {
+ // At least one block for the current block name.
+
+ // Store the current block name for future reference.
+ char *block_name = mCurrentSBlockName;
+
+ // Decrement the sent total by the size of the
+ // data in the message block that we're currently building.
+
+ LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName];
+
+ for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin();
+ iter != template_data->mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ mCurrentSendTotal -= ci.getSize();
+ }
+
+
+ // Now we want to find the block that we're blowing away.
+
+ // Get the number of blocks.
+ LLMsgBlkData* block_data = mCurrentSMessageData->mMemberBlocks[block_name];
+ S32 num_blocks = block_data->mBlockNumber;
+
+ // Use the same (suspect?) algorithm that's used to generate
+ // the names in the nextBlock method to find it.
+ char *block_getting_whacked = block_name + num_blocks - 1;
+ LLMsgBlkData* whacked_data = mCurrentSMessageData->mMemberBlocks[block_getting_whacked];
+ delete whacked_data;
+ mCurrentSMessageData->mMemberBlocks.erase(block_getting_whacked);
+
+ if (num_blocks <= 1)
+ {
+ // we just blew away the last one, so return FALSE
+ return FALSE;
+ }
+ else
+ {
+ // Decrement the counter.
+ block_data->mBlockNumber--;
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+// make sure that all the desired data is in place and then copy the data into mSendBuffer
+void LLMessageSystem::buildMessage()
+{
+ // basic algorithm is to loop through the various pieces, building
+ // size and offset info if we encounter a -1 for mSize at any
+ // point that variable wasn't given data
+
+ // do we have a current message?
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to buildMessage" << llendl;
+ return;
+ }
+
+ // zero out some useful values
+
+ // leave room for circuit counter
+ mSendSize = LL_PACKET_ID_SIZE;
+
+ // encode message number and adjust total_offset
+ if (mCurrentSMessageTemplate->mFrequency == MFT_HIGH)
+ {
+// old, endian-dependant way
+// memcpy(&mSendBuffer[mSendSize], &mCurrentMessageTemplate->mMessageNumber, sizeof(U8));
+
+// new, independant way
+ mSendBuffer[mSendSize] = (U8)mCurrentSMessageTemplate->mMessageNumber;
+ mSendSize += sizeof(U8);
+ }
+ else if (mCurrentSMessageTemplate->mFrequency == MFT_MEDIUM)
+ {
+ U8 temp = 255;
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+
+ // mask off unsightly bits
+ temp = mCurrentSMessageTemplate->mMessageNumber & 255;
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+ }
+ else if (mCurrentSMessageTemplate->mFrequency == MFT_LOW)
+ {
+ U8 temp = 255;
+ U16 message_num;
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+
+ // mask off unsightly bits
+ message_num = mCurrentSMessageTemplate->mMessageNumber & 0xFFFF;
+
+ // convert to network byte order
+ message_num = htons(message_num);
+ memcpy(&mSendBuffer[mSendSize], &message_num, sizeof(U16)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U16);
+ }
+ else
+ {
+ llerrs << "unexpected message frequency in buildMessage" << llendl;
+ return;
+ }
+
+ // counting variables used to encode multiple block info
+ S32 block_count = 0;
+ U8 temp_block_number;
+
+ // loop through msg blocks to loop through variables, totalling up size data and copying into mSendBuffer
+ for (LLMsgData::msg_blk_data_map_t::iterator
+ iter = mCurrentSMessageData->mMemberBlocks.begin(),
+ end = mCurrentSMessageData->mMemberBlocks.end();
+ iter != end; iter++)
+ {
+ LLMsgBlkData* mbci = iter->second;
+ // do we need to encode a block code?
+ if (block_count == 0)
+ {
+ block_count = mbci->mBlockNumber;
+
+ LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[mbci->mName];
+
+ // ok, if this is the first block of a repeating pack, set block_count and, if it's type MBT_VARIABLE encode a byte for how many there are
+ if (template_data->mType == MBT_VARIABLE)
+ {
+ // remember that mBlockNumber is a S32
+ temp_block_number = (U8)mbci->mBlockNumber;
+ if ((S32)(mSendSize + sizeof(U8)) < MAX_BUFFER_SIZE)
+ {
+ memcpy(&mSendBuffer[mSendSize], &temp_block_number, sizeof(U8));
+ mSendSize += sizeof(U8);
+ }
+ else
+ {
+ // Just reporting error is likely not enough. Need
+ // to check how to abort or error out gracefully
+ // from this function. XXXTBD
+ llerrs << "buildMessage failed. Message excedding"
+ " sendBuffersize." << llendl;
+ }
+ }
+ else if (template_data->mType == MBT_MULTIPLE)
+ {
+ if (block_count != template_data->mNumber)
+ {
+ // nope! need to fill it in all the way!
+ llerrs << "Block " << mbci->mName
+ << " is type MBT_MULTIPLE but only has data for "
+ << block_count << " out of its "
+ << template_data->mNumber << " blocks" << llendl;
+ }
+ }
+ }
+
+ // counting down multiple blocks
+ block_count--;
+
+ // now loop through the variables
+ for (LLMsgBlkData::msg_var_data_map_t::iterator iter = mbci->mMemberVarData.begin();
+ iter != mbci->mMemberVarData.end(); iter++)
+ {
+ LLMsgVarData& mvci = *iter;
+ if (mvci.getSize() == -1)
+ {
+ // oops, this variable wasn't ever set!
+ llerrs << "The variable " << mvci.getName() << " in block "
+ << mbci->mName << " of message "
+ << mCurrentSMessageData->mName
+ << " wasn't set prior to buildMessage call" << llendl;
+ }
+ else
+ {
+ S32 data_size = mvci.getDataSize();
+ if(data_size > 0)
+ {
+ // The type is MVT_VARIABLE, which means that we
+ // need to encode a size argument. Otherwise,
+ // there is no need.
+ S32 size = mvci.getSize();
+ U8 sizeb;
+ U16 sizeh;
+ switch(data_size)
+ {
+ case 1:
+ sizeb = size;
+ htonmemcpy(&mSendBuffer[mSendSize], &sizeb, MVT_U8, 1);
+ break;
+ case 2:
+ sizeh = size;
+ htonmemcpy(&mSendBuffer[mSendSize], &sizeh, MVT_U16, 2);
+ break;
+ case 4:
+ htonmemcpy(&mSendBuffer[mSendSize], &size, MVT_S32, 4);
+ break;
+ default:
+ llerrs << "Attempting to build variable field with unknown size of " << size << llendl;
+ break;
+ }
+ mSendSize += mvci.getDataSize();
+ }
+
+ // if there is any data to pack, pack it
+ if((mvci.getData() != NULL) && mvci.getSize())
+ {
+ if(mSendSize + mvci.getSize() < (S32)sizeof(mSendBuffer))
+ {
+ memcpy(
+ &mSendBuffer[mSendSize],
+ mvci.getData(),
+ mvci.getSize());
+ mSendSize += mvci.getSize();
+ }
+ else
+ {
+ // Just reporting error is likely not
+ // enough. Need to check how to abort or error
+ // out gracefully from this function. XXXTBD
+ llerrs << "LLMessageSystem::buildMessage failed. "
+ << "Attempted to pack "
+ << mSendSize + mvci.getSize()
+ << " bytes into a buffer with size "
+ << mSendBuffer << "." << llendl
+ }
+ }
+ }
+ }
+ }
+ mbSBuilt = TRUE;
+}
+
+S32 LLMessageSystem::sendReliable(const LLHost &host)
+{
+ return sendReliable(host, LL_DEFAULT_RELIABLE_RETRIES, TRUE, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL);
+}
+
+
+S32 LLMessageSystem::sendSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data)
+{
+ F32 timeout;
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS,
+ LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged());
+ }
+ else
+ {
+ timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX;
+ }
+
+ return sendReliable(host, 0, FALSE, timeout, callback, callback_data);
+}
+
+// send the message via a UDP packet
+S32 LLMessageSystem::sendReliable( const LLHost &host,
+ S32 retries,
+ BOOL ping_based_timeout,
+ F32 timeout,
+ void (*callback)(void **,S32),
+ void ** callback_data)
+{
+ if (ping_based_timeout)
+ {
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, LL_RELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged());
+ }
+ else
+ {
+ timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, LL_RELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX);
+ }
+ }
+
+ mSendReliable = TRUE;
+ mReliablePacketParams.set(host, retries, ping_based_timeout, timeout,
+ callback, callback_data, mCurrentSMessageName);
+ return sendMessage(host);
+}
+
+void LLMessageSystem::forwardMessage(const LLHost &host)
+{
+ copyMessageRtoS();
+ sendMessage(host);
+}
+
+void LLMessageSystem::forwardReliable(const LLHost &host)
+{
+ copyMessageRtoS();
+ sendReliable(host);
+}
+
+void LLMessageSystem::forwardReliable(const U32 circuit_code)
+{
+ copyMessageRtoS();
+ sendReliable(findHost(circuit_code));
+}
+
+S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data)
+{
+ F32 timeout;
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS,
+ LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged());
+ }
+ else
+ {
+ timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX;
+ }
+
+ S32 send_bytes = 0;
+ if (mCurrentSendTotal)
+ {
+ mSendReliable = TRUE;
+ // No need for ping-based retry as not going to retry
+ mReliablePacketParams.set(host, 0, FALSE, timeout, callback, callback_data, mCurrentSMessageName);
+ send_bytes = sendMessage(host);
+ clearMessage();
+ }
+ else
+ {
+ delete callback_data;
+ }
+ return send_bytes;
+}
+
+S32 LLMessageSystem::flushReliable(const LLHost &host)
+{
+ S32 send_bytes = 0;
+ if (mCurrentSendTotal)
+ {
+ send_bytes = sendReliable(host);
+ }
+ clearMessage();
+ return send_bytes;
+}
+
+
+// This can be called from signal handlers,
+// so should should not use llinfos.
+S32 LLMessageSystem::sendMessage(const LLHost &host)
+{
+ if (!mbSBuilt)
+ {
+ buildMessage();
+ }
+
+ mCurrentSendTotal = 0;
+
+ if (!(host.isOk())) // if port and ip are zero, don't bother trying to send the message
+ {
+ return 0;
+ }
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ // this is a new circuit!
+ // are we protected?
+ if (mbProtected)
+ {
+ // yup! don't send packets to an unknown circuit
+ if(mVerboseLog)
+ {
+ llinfos << "MSG: -> " << host << "\tUNKNOWN CIRCUIT:\t"
+ << mCurrentSMessageName << llendl;
+ }
+ llwarns << "sendMessage - Trying to send "
+ << mCurrentSMessageName << " on unknown circuit "
+ << host << llendl;
+ return 0;
+ }
+ else
+ {
+ // nope, open the new circuit
+ cdp = mCircuitInfo.addCircuitData(host, 0);
+ }
+ }
+ else
+ {
+ // this is an old circuit. . . is it still alive?
+ if (!cdp->isAlive())
+ {
+ // nope. don't send to dead circuits
+ if(mVerboseLog)
+ {
+ llinfos << "MSG: -> " << host << "\tDEAD CIRCUIT\t\t"
+ << mCurrentSMessageName << llendl;
+ }
+ llwarns << "sendMessage - Trying to send message "
+ << mCurrentSMessageName << " to dead circuit "
+ << host << llendl;
+ return 0;
+ }
+ }
+
+ memset(mSendBuffer,0,LL_PACKET_ID_SIZE); // zero out the packet ID field
+
+ // add the send id to the front of the message
+ cdp->nextPacketOutID();
+
+ // Packet ID size is always 4
+ *((S32*)&mSendBuffer[0]) = htonl(cdp->getPacketOutID());
+
+ // Compress the message, which will usually reduce its size.
+ U8 * buf_ptr = (U8 *)mSendBuffer;
+ S32 buffer_length = mSendSize;
+ if(ME_ZEROCODED == mCurrentSMessageTemplate->getEncoding())
+ {
+ zeroCode(&buf_ptr, &buffer_length);
+ }
+
+ if (buffer_length > 1500)
+ {
+ if((mCurrentSMessageName != _PREHASH_ChildAgentUpdate)
+ && (mCurrentSMessageName != _PREHASH_SendXferPacket))
+ {
+ llwarns << "sendMessage - Trying to send "
+ << ((buffer_length > 4000) ? "EXTRA " : "")
+ << "BIG message " << mCurrentSMessageName << " - "
+ << buffer_length << llendl;
+ }
+ }
+ if (mSendReliable)
+ {
+ buf_ptr[0] |= LL_RELIABLE_FLAG;
+
+ if (!cdp->getUnackedPacketCount())
+ {
+ // We are adding the first packed onto the unacked packet list(s)
+ // Add this circuit to the list of circuits with unacked packets
+ mCircuitInfo.mUnackedCircuitMap[cdp->mHost] = cdp;
+ }
+
+ cdp->addReliablePacket(mSocket,buf_ptr,buffer_length, &mReliablePacketParams);
+ mReliablePacketsOut++;
+ }
+
+ // tack packet acks onto the end of this message
+ S32 space_left = (MTUBYTES - buffer_length) / sizeof(TPACKETID); // space left for packet ids
+ S32 ack_count = (S32)cdp->mAcks.size();
+ BOOL is_ack_appended = FALSE;
+ std::vector<TPACKETID> acks;
+ if((space_left > 0) && (ack_count > 0) &&
+ (mCurrentSMessageName != _PREHASH_PacketAck))
+ {
+ buf_ptr[0] |= LL_ACK_FLAG;
+ S32 append_ack_count = llmin(space_left, ack_count);
+ const S32 MAX_ACKS = 250;
+ append_ack_count = llmin(append_ack_count, MAX_ACKS);
+ std::vector<TPACKETID>::iterator iter = cdp->mAcks.begin();
+ std::vector<TPACKETID>::iterator last = cdp->mAcks.begin();
+ last += append_ack_count;
+ TPACKETID packet_id;
+ for( ; iter != last ; ++iter)
+ {
+ // grab the next packet id.
+ packet_id = (*iter);
+ if(mVerboseLog)
+ {
+ acks.push_back(packet_id);
+ }
+
+ // put it on the end of the buffer
+ packet_id = htonl(packet_id);
+
+ if((S32)(buffer_length + sizeof(TPACKETID)) < MAX_BUFFER_SIZE)
+ {
+ memcpy(&buf_ptr[buffer_length], &packet_id, sizeof(TPACKETID));
+ // Do the accounting
+ buffer_length += sizeof(TPACKETID);
+ }
+ else
+ {
+ // Just reporting error is likely not enough. Need to
+ // check how to abort or error out gracefully from
+ // this function. XXXTBD
+ // *NOTE: Actually hitting this error would indicate
+ // the calculation above for space_left, ack_count,
+ // append_acout_count is incorrect or that
+ // MAX_BUFFER_SIZE has fallen below MTU which is bad
+ // and probably programmer error.
+ llerrs << "Buffer packing failed due to size.." << llendl;
+ }
+ }
+
+ // clean up the source
+ cdp->mAcks.erase(cdp->mAcks.begin(), last);
+
+ // tack the count in the final byte
+ U8 count = (U8)append_ack_count;
+ buf_ptr[buffer_length++] = count;
+ is_ack_appended = TRUE;
+ }
+
+ BOOL success;
+ success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
+
+ if (!success)
+ {
+ mSendPacketFailureCount++;
+ }
+ else
+ {
+ // mCircuitInfo already points to the correct circuit data
+ cdp->addBytesOut( buffer_length );
+ }
+
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mSendSize, buffer_length, cdp->getPacketOutID()); /* Flawfinder: ignore */
+ str << buffer
+ << mCurrentSMessageTemplate->mName
+ << (mSendReliable ? " reliable " : "");
+ if(is_ack_appended)
+ {
+ str << "\tACKS:\t";
+ std::ostream_iterator<TPACKETID> append(str, " ");
+ std::copy(acks.begin(), acks.end(), append);
+ }
+ llinfos << str.str() << llendl;
+ }
+
+ lldebugst(LLERR_MESSAGE) << "MessageSent at: " << (S32)totalTime()
+ << ", " << mCurrentSMessageTemplate->mName
+ << " to " << host
+ << llendl;
+
+ // ok, clean up temp data
+ delete mCurrentSMessageData;
+ mCurrentSMessageData = NULL;
+
+ mPacketsOut++;
+ mBytesOut += buffer_length;
+
+ return buffer_length;
+}
+
+
+// Returns template for the message contained in buffer
+BOOL LLMessageSystem::decodeTemplate(
+ const U8* buffer, S32 buffer_size, // inputs
+ LLMessageTemplate** msg_template ) // outputs
+{
+ const U8* header = buffer + LL_PACKET_ID_SIZE;
+
+ // is there a message ready to go?
+ if (buffer_size <= 0)
+ {
+ llwarns << "No message waiting for decode!" << llendl;
+ return(FALSE);
+ }
+
+ U32 num = 0;
+
+ if (header[0] != 255)
+ {
+ // high frequency message
+ num = header[0];
+ }
+ else if ((buffer_size >= ((S32) LL_MINIMUM_VALID_PACKET_SIZE + 1)) && (header[1] != 255))
+ {
+ // medium frequency message
+ num = (255 << 8) | header[1];
+ }
+ else if ((buffer_size >= ((S32) LL_MINIMUM_VALID_PACKET_SIZE + 3)) && (header[1] == 255))
+ {
+ // low frequency message
+ U16 message_id_U16 = 0;
+ // I think this check busts the message system.
+ // it appears that if there is a NULL in the message #, it won't copy it....
+ // what was the goal?
+ //if(header[2])
+ memcpy(&message_id_U16, &header[2], 2);
+
+ // dependant on endian-ness:
+ // U32 temp = (255 << 24) | (255 << 16) | header[2];
+
+ // independant of endian-ness:
+ message_id_U16 = ntohs(message_id_U16);
+ num = 0xFFFF0000 | message_id_U16;
+ }
+ else // bogus packet received (too short)
+ {
+ llwarns << "Packet with unusable length received (too short): "
+ << buffer_size << llendl;
+ return(FALSE);
+ }
+
+ LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers,num);
+ if (temp)
+ {
+ *msg_template = temp;
+ }
+ else
+ {
+ llwarns << "Message #" << std::hex << num << std::dec
+ << " received but not registered!" << llendl;
+ callExceptionFunc(MX_UNREGISTERED_MESSAGE);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+void LLMessageSystem::logMsgFromInvalidCircuit( const LLHost& host, BOOL recv_reliable )
+{
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize: mReceiveSize), mCurrentRecvPacketID); /* Flawfinder: ignore */
+ str << buffer
+ << mCurrentRMessageTemplate->mName
+ << (recv_reliable ? " reliable" : "")
+ << " REJECTED";
+ llinfos << str.str() << llendl;
+ }
+ // nope!
+ // cout << "Rejecting unexpected message " << mCurrentMessageTemplate->mName << " from " << hex << ip << " , " << dec << port << endl;
+
+ // Keep track of rejected messages as well
+ if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
+ {
+ llwarns << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl;
+ }
+ else
+ {
+ mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber;
+ mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize;
+ mMessageCountList[mNumMessageCounts].mInvalid = TRUE;
+ mNumMessageCounts++;
+ }
+}
+
+void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host )
+{
+ llwarns << "Recieved trusted message on untrusted circuit. "
+ << "Will reply with deny. "
+ << "Message: " << mCurrentRMessageTemplate->mName
+ << " Host: " << host << llendl;
+ if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
+ {
+ llwarns << "got more than " << MAX_MESSAGE_COUNT_NUM
+ << " packets without clearing counts"
+ << llendl;
+ }
+ else
+ {
+ mMessageCountList[mNumMessageCounts].mMessageNum
+ = mCurrentRMessageTemplate->mMessageNumber;
+ mMessageCountList[mNumMessageCounts].mMessageBytes
+ = mReceiveSize;
+ mMessageCountList[mNumMessageCounts].mInvalid = TRUE;
+ mNumMessageCounts++;
+ }
+}
+
+void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, BOOL recv_reliable, BOOL recv_resent, BOOL recv_acks )
+{
+ if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
+ {
+ llwarns << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl;
+ }
+ else
+ {
+ mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber;
+ mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize;
+ mMessageCountList[mNumMessageCounts].mInvalid = FALSE;
+ mNumMessageCounts++;
+ }
+
+ if (cdp)
+ {
+ // update circuit packet ID tracking (missing/out of order packets)
+ cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent );
+ cdp->addBytesIn( mTrueReceiveSize );
+ }
+
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize : mReceiveSize), mCurrentRecvPacketID); /* Flawfinder: ignore */
+ str << buffer
+ << mCurrentRMessageTemplate->mName
+ << (recv_reliable ? " reliable" : "")
+ << (recv_resent ? " resent" : "")
+ << (recv_acks ? " acks" : "");
+ llinfos << str.str() << llendl;
+ }
+}
+
+
+void LLMessageSystem::logRanOffEndOfPacket( const LLHost& host )
+{
+ // we've run off the end of the packet!
+ llwarns << "Ran off end of packet " << mCurrentRMessageTemplate->mName
+ << " with id " << mCurrentRecvPacketID << " from " << host
+ << llendl;
+ if(mVerboseLog)
+ {
+ llinfos << "MSG: -> " << host << "\tREAD PAST END:\t"
+ << mCurrentRecvPacketID << " "
+ << mCurrentSMessageTemplate->mName << llendl;
+ }
+ callExceptionFunc(MX_RAN_OFF_END_OF_PACKET);
+}
+
+
+// decode a given message
+BOOL LLMessageSystem::decodeData(const U8* buffer, const LLHost& sender )
+{
+ llassert( mReceiveSize >= 0 );
+ llassert( mCurrentRMessageTemplate);
+ llassert( !mCurrentRMessageData );
+ delete mCurrentRMessageData; // just to make sure
+
+ S32 decode_pos = LL_PACKET_ID_SIZE + (S32)(mCurrentRMessageTemplate->mFrequency);
+
+ // create base working data set
+ mCurrentRMessageData = new LLMsgData(mCurrentRMessageTemplate->mName);
+
+ // loop through the template building the data structure as we go
+ for (LLMessageTemplate::message_block_map_t::iterator iter = mCurrentRMessageTemplate->mMemberBlocks.begin();
+ iter != mCurrentRMessageTemplate->mMemberBlocks.end(); iter++)
+ {
+ LLMessageBlock* mbci = iter->second;
+ U8 repeat_number;
+ S32 i;
+
+ // how many of this block?
+
+ if (mbci->mType == MBT_SINGLE)
+ {
+ // just one
+ repeat_number = 1;
+ }
+ else if (mbci->mType == MBT_MULTIPLE)
+ {
+ // a known number
+ repeat_number = mbci->mNumber;
+ }
+ else if (mbci->mType == MBT_VARIABLE)
+ {
+ // need to read the number from the message
+ // repeat number is a single byte
+ if (decode_pos >= mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+ repeat_number = buffer[decode_pos];
+ decode_pos++;
+ }
+ else
+ {
+ llerrs << "Unknown block type" << llendl;
+ return FALSE;
+ }
+
+ LLMsgBlkData* cur_data_block = NULL;
+
+ // now loop through the block
+ for (i = 0; i < repeat_number; i++)
+ {
+ if (i)
+ {
+ // build new name to prevent collisions
+ // TODO: This should really change to a vector
+ cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number);
+ cur_data_block->mName = mbci->mName + i;
+ }
+ else
+ {
+ cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number);
+ }
+
+ // add the block to the message
+ mCurrentRMessageData->addBlock(cur_data_block);
+
+ // now read the variables
+ for (LLMessageBlock::message_variable_map_t::iterator iter = mbci->mMemberVariables.begin();
+ iter != mbci->mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& mvci = *(iter->second);
+ // ok, build out the variables
+ // add variable block
+ cur_data_block->addVariable(mvci.getName(), mvci.getType());
+
+ // what type of variable?
+ if (mvci.getType() == MVT_VARIABLE)
+ {
+ // variable, get the number of bytes to read from the template
+ S32 data_size = mvci.getSize();
+ U8 tsizeb = 0;
+ U16 tsizeh = 0;
+ U32 tsize = 0;
+
+ if ((decode_pos + data_size) > mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+ switch(data_size)
+ {
+ case 1:
+ htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1);
+ tsize = tsizeb;
+ break;
+ case 2:
+ htonmemcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2);
+ tsize = tsizeh;
+ break;
+ case 4:
+ htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U32, 4);
+ break;
+ default:
+ llerrs << "Attempting to read variable field with unknown size of " << data_size << llendl;
+ break;
+
+ }
+ decode_pos += data_size;
+
+ if ((decode_pos + (S32)tsize) > mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+ cur_data_block->addData(mvci.getName(), &buffer[decode_pos], tsize, mvci.getType());
+ decode_pos += tsize;
+ }
+ else
+ {
+ // fixed!
+ // so, copy data pointer and set data size to fixed size
+
+ if ((decode_pos + mvci.getSize()) > mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+
+ cur_data_block->addData(mvci.getName(), &buffer[decode_pos], mvci.getSize(), mvci.getType());
+ decode_pos += mvci.getSize();
+ }
+ }
+ }
+ }
+
+ if (mCurrentRMessageData->mMemberBlocks.empty()
+ && !mCurrentRMessageTemplate->mMemberBlocks.empty())
+ {
+ lldebugs << "Empty message '" << mCurrentRMessageTemplate->mName << "' (no blocks)" << llendl;
+ return FALSE;
+ }
+
+ {
+ static LLTimer decode_timer;
+
+ if( mTimeDecodes )
+ {
+ decode_timer.reset();
+ }
+
+ // if( mCurrentRMessageTemplate->mName == _PREHASH_AgentToNewRegion )
+ // {
+ // VTResume(); // VTune
+ // }
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_MESSAGES);
+ if( !mCurrentRMessageTemplate->callHandlerFunc(this) )
+ {
+ llwarns << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << llendl;
+ }
+ }
+
+ // if( mCurrentRMessageTemplate->mName == _PREHASH_AgentToNewRegion )
+ // {
+ // VTPause(); // VTune
+ // }
+
+ if( mTimeDecodes )
+ {
+ F32 decode_time = decode_timer.getElapsedTimeF32();
+ mCurrentRMessageTemplate->mDecodeTimeThisFrame += decode_time;
+
+ mCurrentRMessageTemplate->mTotalDecoded++;
+ mCurrentRMessageTemplate->mTotalDecodeTime += decode_time;
+
+ if( mCurrentRMessageTemplate->mMaxDecodeTimePerMsg < decode_time )
+ {
+ mCurrentRMessageTemplate->mMaxDecodeTimePerMsg = decode_time;
+ }
+
+
+ if( decode_time > mTimeDecodesSpamThreshold )
+ {
+ lldebugs << "--------- Message " << mCurrentRMessageTemplate->mName << " decode took " << decode_time << " seconds. (" <<
+ mCurrentRMessageTemplate->mMaxDecodeTimePerMsg << " max, " <<
+ (mCurrentRMessageTemplate->mTotalDecodeTime / mCurrentRMessageTemplate->mTotalDecoded) << " avg)" << llendl;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLMessageSystem::getDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum, S32 max_size)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 2!" << llendl;
+ return;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentMessageData in getData!" << llendl;
+ return;
+ }
+
+ char *bnamep = (char *)blockname + blocknum; // this works because it's just a hash. The bnamep is never derefference
+ char *vnamep = (char *)varname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+ llerrs << "Block " << blockname << " #" << blocknum
+ << " not in message " << mCurrentRMessageData->mName << llendl;
+ return;
+ }
+
+ LLMsgBlkData *msg_block_data = iter->second;
+ LLMsgVarData& vardata = msg_block_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ llerrs << "Variable "<< vnamep << " not in message "
+ << mCurrentRMessageData->mName<< " block " << bnamep << llendl;
+ return;
+ }
+
+ if (size && size != vardata.getSize())
+ {
+ llerrs << "Msg " << mCurrentRMessageData->mName
+ << " variable " << vnamep
+ << " is size " << vardata.getSize()
+ << " but copying into buffer of size " << size
+ << llendl;
+ return;
+ }
+
+
+ const S32 vardata_size = vardata.getSize();
+ if( max_size >= vardata_size )
+ {
+ switch( vardata_size )
+ {
+ case 1:
+ *((U8*)datap) = *((U8*)vardata.getData());
+ break;
+ case 2:
+ *((U16*)datap) = *((U16*)vardata.getData());
+ break;
+ case 4:
+ *((U32*)datap) = *((U32*)vardata.getData());
+ break;
+ case 8:
+ ((U32*)datap)[0] = ((U32*)vardata.getData())[0];
+ ((U32*)datap)[1] = ((U32*)vardata.getData())[1];
+ break;
+ default:
+ memcpy(datap, vardata.getData(), vardata_size);
+ break;
+ }
+ }
+ else
+ {
+ llwarns << "Msg " << mCurrentRMessageData->mName
+ << " variable " << vnamep
+ << " is size " << vardata.getSize()
+ << " but truncated to max size of " << max_size
+ << llendl;
+
+ memcpy(datap, vardata.getData(), max_size);
+ }
+}
+
+S32 LLMessageSystem::getNumberOfBlocksFast(const char *blockname)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 3!" << llendl;
+ return -1;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentRMessageData in getData!" << llendl;
+ return -1;
+ }
+
+ char *bnamep = (char *)blockname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+// sprintf(errmsg, "Block %s not in message %s", bnamep, mCurrentRMessageData->mName);
+// llerrs << errmsg << llendl;
+// return -1;
+ return 0;
+ }
+
+ return (iter->second)->mBlockNumber;
+}
+
+S32 LLMessageSystem::getSizeFast(const char *blockname, const char *varname)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 4!" << llendl;
+ return -1;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentRMessageData in getData!" << llendl;
+ return -1;
+ }
+
+ char *bnamep = (char *)blockname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+ llerrs << "Block " << bnamep << " not in message "
+ << mCurrentRMessageData->mName << llendl;
+ return -1;
+ }
+
+ char *vnamep = (char *)varname;
+
+ LLMsgBlkData* msg_data = iter->second;
+ LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ llerrs << "Variable " << varname << " not in message "
+ << mCurrentRMessageData->mName << " block " << bnamep << llendl;
+ return -1;
+ }
+
+ if (mCurrentRMessageTemplate->mMemberBlocks[bnamep]->mType != MBT_SINGLE)
+ {
+ llerrs << "Block " << bnamep << " isn't type MBT_SINGLE,"
+ " use getSize with blocknum argument!" << llendl;
+ return -1;
+ }
+
+ return vardata.getSize();
+}
+
+
+S32 LLMessageSystem::getSizeFast(const char *blockname, S32 blocknum, const char *varname)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 5!" << llendl;
+ return -1;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentRMessageData in getData!" << llendl;
+ return -1;
+ }
+
+ char *bnamep = (char *)blockname + blocknum;
+ char *vnamep = (char *)varname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+ llerrs << "Block " << bnamep << " not in message "
+ << mCurrentRMessageData->mName << llendl;
+ return -1;
+ }
+
+ LLMsgBlkData* msg_data = iter->second;
+ LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ llerrs << "Variable " << vnamep << " not in message "
+ << mCurrentRMessageData->mName << " block " << bnamep << llendl;
+ return -1;
+ }
+
+ return vardata.getSize();
+}
+
+
+void LLMessageSystem::sanityCheck()
+{
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "mCurrentRMessageData is NULL" << llendl;
+ }
+
+ if (!mCurrentRMessageTemplate)
+ {
+ llerrs << "mCurrentRMessageTemplate is NULL" << llendl;
+ }
+
+// if (!mCurrentRTemplateBlock)
+// {
+// llerrs << "mCurrentRTemplateBlock is NULL" << llendl;
+// }
+
+// if (!mCurrentRDataBlock)
+// {
+// llerrs << "mCurrentRDataBlock is NULL" << llendl;
+// }
+
+ if (!mCurrentSMessageData)
+ {
+ llerrs << "mCurrentSMessageData is NULL" << llendl;
+ }
+
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "mCurrentSMessageTemplate is NULL" << llendl;
+ }
+
+// if (!mCurrentSTemplateBlock)
+// {
+// llerrs << "mCurrentSTemplateBlock is NULL" << llendl;
+// }
+
+ if (!mCurrentSDataBlock)
+ {
+ llerrs << "mCurrentSDataBlock is NULL" << llendl;
+ }
+}
+
+void LLMessageSystem::showCircuitInfo()
+{
+ llinfos << mCircuitInfo << llendl;
+}
+
+
+void LLMessageSystem::dumpCircuitInfo()
+{
+ lldebugst(LLERR_CIRCUIT_INFO) << mCircuitInfo << llendl;
+}
+
+/* virtual */
+U32 LLMessageSystem::getOurCircuitCode()
+{
+ return mOurCircuitCode;
+}
+
+LLString LLMessageSystem::getCircuitInfoString()
+{
+ LLString info_string;
+
+ info_string += mCircuitInfo.getInfoString();
+ return info_string;
+}
+
+// returns whether the given host is on a trusted circuit
+BOOL LLMessageSystem::getCircuitTrust(const LLHost &host)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->getTrusted();
+ }
+
+ return FALSE;
+}
+
+// Activate a circuit, and set its trust level (TRUE if trusted,
+// FALSE if not).
+void LLMessageSystem::enableCircuit(const LLHost &host, BOOL trusted)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ cdp = mCircuitInfo.addCircuitData(host, 0);
+ }
+ else
+ {
+ cdp->setAlive(TRUE);
+ }
+ cdp->setTrusted(trusted);
+}
+
+void LLMessageSystem::disableCircuit(const LLHost &host)
+{
+ llinfos << "LLMessageSystem::disableCircuit for " << host << llendl;
+ U32 code = gMessageSystem->findCircuitCode( host );
+
+ // Don't need to do this, as we're removing the circuit info anyway - djs 01/28/03
+
+ // don't clean up 0 circuit code entries
+ // because many hosts (neighbor sims, etc) can have the 0 circuit
+ if (code)
+ {
+ //if (mCircuitCodes.checkKey(code))
+ code_session_map_t::iterator it = mCircuitCodes.find(code);
+ if(it != mCircuitCodes.end())
+ {
+ llinfos << "Circuit " << code << " removed from list" << llendl;
+ //mCircuitCodes.removeData(code);
+ mCircuitCodes.erase(it);
+ }
+
+ U64 ip_port = 0;
+ std::map<U32, U64>::iterator iter = gMessageSystem->mCircuitCodeToIPPort.find(code);
+ if (iter != gMessageSystem->mCircuitCodeToIPPort.end())
+ {
+ ip_port = iter->second;
+
+ gMessageSystem->mCircuitCodeToIPPort.erase(iter);
+
+ U32 old_port = (U32)(ip_port & (U64)0xFFFFFFFF);
+ U32 old_ip = (U32)(ip_port >> 32);
+
+ llinfos << "Host " << LLHost(old_ip, old_port) << " circuit " << code << " removed from lookup table" << llendl;
+ gMessageSystem->mIPPortToCircuitCode.erase(ip_port);
+ }
+ }
+ else
+ {
+ // Sigh, since we can open circuits which don't have circuit
+ // codes, it's possible for this to happen...
+
+ //llwarns << "Couldn't find circuit code for " << host << llendl;
+ }
+
+ mCircuitInfo.removeCircuitData(host);
+}
+
+
+void LLMessageSystem::setCircuitAllowTimeout(const LLHost &host, BOOL allow)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ cdp->setAllowTimeout(allow);
+ }
+}
+
+void LLMessageSystem::setCircuitTimeoutCallback(const LLHost &host, void (*callback_func)(const LLHost & host, void *user_data), void *user_data)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ cdp->setTimeoutCallback(callback_func, user_data);
+ }
+}
+
+
+BOOL LLMessageSystem::checkCircuitBlocked(const U32 circuit)
+{
+ LLHost host = findHost(circuit);
+
+ if (!host.isOk())
+ {
+ //llinfos << "checkCircuitBlocked: Unknown circuit " << circuit << llendl;
+ return TRUE;
+ }
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->isBlocked();
+ }
+ else
+ {
+ llinfos << "checkCircuitBlocked(circuit): Unknown host - " << host << llendl;
+ return FALSE;
+ }
+}
+
+BOOL LLMessageSystem::checkCircuitAlive(const U32 circuit)
+{
+ LLHost host = findHost(circuit);
+
+ if (!host.isOk())
+ {
+ //llinfos << "checkCircuitAlive: Unknown circuit " << circuit << llendl;
+ return FALSE;
+ }
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->isAlive();
+ }
+ else
+ {
+ llinfos << "checkCircuitAlive(circuit): Unknown host - " << host << llendl;
+ return FALSE;
+ }
+}
+
+BOOL LLMessageSystem::checkCircuitAlive(const LLHost &host)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->isAlive();
+ }
+ else
+ {
+ //llinfos << "checkCircuitAlive(host): Unknown host - " << host << llendl;
+ return FALSE;
+ }
+}
+
+
+void LLMessageSystem::setCircuitProtection(BOOL b_protect)
+{
+ mbProtected = b_protect;
+}
+
+
+U32 LLMessageSystem::findCircuitCode(const LLHost &host)
+{
+ U64 ip64 = (U64) host.getAddress();
+ U64 port64 = (U64) host.getPort();
+ U64 ip_port = (ip64 << 32) | port64;
+
+ return get_if_there(mIPPortToCircuitCode, ip_port, U32(0));
+}
+
+LLHost LLMessageSystem::findHost(const U32 circuit_code)
+{
+ if (mCircuitCodeToIPPort.count(circuit_code) > 0)
+ {
+ return LLHost(mCircuitCodeToIPPort[circuit_code]);
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}
+
+void LLMessageSystem::setMaxMessageTime(const F32 seconds)
+{
+ mMaxMessageTime = seconds;
+}
+
+void LLMessageSystem::setMaxMessageCounts(const S32 num)
+{
+ mMaxMessageCounts = num;
+}
+
+
+std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg)
+{
+ U32 i;
+ if (msg.mbError)
+ {
+ s << "Message system not correctly initialized";
+ }
+ else
+ {
+ s << "Message system open on port " << msg.mPort << " and socket " << msg.mSocket << "\n";
+// s << "Message template file " << msg.mName << " loaded\n";
+
+ s << "\nHigh frequency messages:\n";
+
+ for (i = 1; msg.mMessageNumbers[i] && (i < 255); i++)
+ {
+ s << *(msg.mMessageNumbers[i]);
+ }
+
+ s << "\nMedium frequency messages:\n";
+
+ for (i = (255 << 8) + 1; msg.mMessageNumbers[i] && (i < (255 << 8) + 255); i++)
+ {
+ s << *msg.mMessageNumbers[i];
+ }
+
+ s << "\nLow frequency messages:\n";
+
+ for (i = (0xFFFF0000) + 1; msg.mMessageNumbers[i] && (i < 0xFFFFFFFF); i++)
+ {
+ s << *msg.mMessageNumbers[i];
+ }
+ }
+ return s;
+}
+
+LLMessageSystem *gMessageSystem = NULL;
+
+// update appropriate ping info
+void process_complete_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ U8 ping_id;
+ msgsystem->getU8Fast(_PREHASH_PingID, _PREHASH_PingID, ping_id);
+
+ LLCircuitData *cdp;
+ cdp = msgsystem->mCircuitInfo.findCircuit(msgsystem->getSender());
+
+ // stop the appropriate timer
+ if (cdp)
+ {
+ cdp->pingTimerStop(ping_id);
+ }
+}
+
+void process_start_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ U8 ping_id;
+ msgsystem->getU8Fast(_PREHASH_PingID, _PREHASH_PingID, ping_id);
+
+ LLCircuitData *cdp;
+ cdp = msgsystem->mCircuitInfo.findCircuit(msgsystem->getSender());
+ if (cdp)
+ {
+ // Grab the packet id of the oldest unacked packet
+ U32 packet_id;
+ msgsystem->getU32Fast(_PREHASH_PingID, _PREHASH_OldestUnacked, packet_id);
+ cdp->clearDuplicateList(packet_id);
+ }
+
+ // Send off the response
+ msgsystem->newMessageFast(_PREHASH_CompletePingCheck);
+ msgsystem->nextBlockFast(_PREHASH_PingID);
+ msgsystem->addU8(_PREHASH_PingID, ping_id);
+ msgsystem->sendMessage(msgsystem->getSender());
+}
+
+
+
+// Note: this is currently unused. --mark
+void open_circuit(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ U32 ip;
+ U16 port;
+
+ msgsystem->getIPAddrFast(_PREHASH_CircuitInfo, _PREHASH_IP, ip);
+ msgsystem->getIPPortFast(_PREHASH_CircuitInfo, _PREHASH_Port, port);
+
+ // By default, OpenCircuit's are untrusted
+ msgsystem->enableCircuit(LLHost(ip, port), FALSE);
+}
+
+void close_circuit(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ msgsystem->disableCircuit(msgsystem->getSender());
+}
+
+// static
+/*
+void LLMessageSystem::processAssignCircuitCode(LLMessageSystem* msg, void**)
+{
+ // if we already have a circuit code, we can bail
+ if(msg->mOurCircuitCode) return;
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id);
+ if(session_id != msg->getMySessionID())
+ {
+ llwarns << "AssignCircuitCode, bad session id. Expecting "
+ << msg->getMySessionID() << " but got " << session_id
+ << llendl;
+ return;
+ }
+ U32 code;
+ msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code);
+ if (!code)
+ {
+ llerrs << "Assigning circuit code of zero!" << llendl;
+ }
+
+ msg->mOurCircuitCode = code;
+ llinfos << "Circuit code " << code << " assigned." << llendl;
+}
+*/
+
+// static
+void LLMessageSystem::processAddCircuitCode(LLMessageSystem* msg, void**)
+{
+ U32 code;
+ msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id);
+ (void)msg->addCircuitCode(code, session_id);
+
+ // Send the ack back
+ //msg->newMessageFast(_PREHASH_AckAddCircuitCode);
+ //msg->nextBlockFast(_PREHASH_CircuitCode);
+ //msg->addU32Fast(_PREHASH_Code, code);
+ //msg->sendMessage(msg->getSender());
+}
+
+bool LLMessageSystem::addCircuitCode(U32 code, const LLUUID& session_id)
+{
+ if(!code)
+ {
+ llwarns << "addCircuitCode: zero circuit code" << llendl;
+ return false;
+ }
+ code_session_map_t::iterator it = mCircuitCodes.find(code);
+ if(it == mCircuitCodes.end())
+ {
+ llinfos << "New circuit code " << code << " added" << llendl;
+ //msg->mCircuitCodes[circuit_code] = circuit_code;
+
+ mCircuitCodes.insert(code_session_map_t::value_type(code, session_id));
+ }
+ else
+ {
+ llinfos << "Duplicate circuit code " << code << " added" << llendl;
+ }
+ return true;
+}
+
+//void ack_add_circuit_code(LLMessageSystem *msgsystem, void** /*user_data*/)
+//{
+ // By default, we do nothing. This particular message is only handled by the spaceserver
+//}
+
+// static
+void LLMessageSystem::processUseCircuitCode(LLMessageSystem* msg, void**)
+{
+ U32 circuit_code_in;
+ msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, circuit_code_in);
+
+ U32 ip = msg->getSenderIP();
+ U32 port = msg->getSenderPort();
+
+ U64 ip64 = ip;
+ U64 port64 = port;
+ U64 ip_port_in = (ip64 << 32) | port64;
+
+ if (circuit_code_in)
+ {
+ //if (!msg->mCircuitCodes.checkKey(circuit_code_in))
+ code_session_map_t::iterator it;
+ it = msg->mCircuitCodes.find(circuit_code_in);
+ if(it == msg->mCircuitCodes.end())
+ {
+ // Whoah, abort! We don't know anything about this circuit code.
+ llwarns << "UseCircuitCode for " << circuit_code_in
+ << " received without AddCircuitCode message - aborting"
+ << llendl;
+ return;
+ }
+
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_ID, id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id);
+ if(session_id != (*it).second)
+ {
+ llwarns << "UseCircuitCode unmatched session id. Got "
+ << session_id << " but expected " << (*it).second
+ << llendl;
+ return;
+ }
+
+ // Clean up previous references to this ip/port or circuit
+ U64 ip_port_old = get_if_there(msg->mCircuitCodeToIPPort, circuit_code_in, U64(0));
+ U32 circuit_code_old = get_if_there(msg->mIPPortToCircuitCode, ip_port_in, U32(0));
+
+ if (ip_port_old)
+ {
+ if ((ip_port_old == ip_port_in) && (circuit_code_old == circuit_code_in))
+ {
+ // Current information is the same as incoming info, ignore
+ llinfos << "Got duplicate UseCircuitCode for circuit " << circuit_code_in << " to " << msg->getSender() << llendl;
+ return;
+ }
+
+ // Hmm, got a different IP and port for the same circuit code.
+ U32 circut_code_old_ip_port = get_if_there(msg->mIPPortToCircuitCode, ip_port_old, U32(0));
+ msg->mCircuitCodeToIPPort.erase(circut_code_old_ip_port);
+ msg->mIPPortToCircuitCode.erase(ip_port_old);
+ U32 old_port = (U32)(ip_port_old & (U64)0xFFFFFFFF);
+ U32 old_ip = (U32)(ip_port_old >> 32);
+ llinfos << "Removing derelict lookup entry for circuit " << circuit_code_old << " to " << LLHost(old_ip, old_port) << llendl;
+ }
+
+ if (circuit_code_old)
+ {
+ LLHost cur_host(ip, port);
+
+ llwarns << "Disabling existing circuit for " << cur_host << llendl;
+ msg->disableCircuit(cur_host);
+ if (circuit_code_old == circuit_code_in)
+ {
+ llwarns << "Asymmetrical circuit to ip/port lookup!" << llendl;
+ llwarns << "Multiple circuit codes for " << cur_host << " probably!" << llendl;
+ llwarns << "Permanently disabling circuit" << llendl;
+ return;
+ }
+ else
+ {
+ llwarns << "Circuit code changed for " << msg->getSender()
+ << " from " << circuit_code_old << " to "
+ << circuit_code_in << llendl;
+ }
+ }
+
+ // Since this comes from the viewer, it's untrusted, but it
+ // passed the circuit code and session id check, so we will go
+ // ahead and persist the ID associated.
+ LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ BOOL had_circuit_already = cdp ? TRUE : FALSE;
+
+ msg->enableCircuit(msg->getSender(), FALSE);
+ cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ if(cdp)
+ {
+ cdp->setRemoteID(id);
+ cdp->setRemoteSessionID(session_id);
+ }
+
+ if (!had_circuit_already)
+ {
+ //
+ // HACK HACK HACK HACK HACK!
+ //
+ // This would NORMALLY happen inside logValidMsg, but at the point that this happens
+ // inside logValidMsg, there's no circuit for this message yet. So the awful thing that
+ // we do here is do it inside this message handler immediately AFTER the message is
+ // handled.
+ //
+ // We COULD not do this, but then what happens is that some of the circuit bookkeeping
+ // gets broken, especially the packets in count. That causes some later packets to flush
+ // the RecentlyReceivedReliable list, resulting in an error in which UseCircuitCode
+ // doesn't get properly duplicate suppressed. Not a BIG deal, but it's somewhat confusing
+ // (and bad from a state point of view). DJS 9/23/04
+ //
+ cdp->checkPacketInID(gMessageSystem->mCurrentRecvPacketID, FALSE ); // Since this is the first message on the circuit, by definition it's not resent.
+ }
+
+ msg->mIPPortToCircuitCode[ip_port_in] = circuit_code_in;
+ msg->mCircuitCodeToIPPort[circuit_code_in] = ip_port_in;
+
+ llinfos << "Circuit code " << circuit_code_in << " from "
+ << msg->getSender() << " for agent " << id << " in session "
+ << session_id << llendl;
+ }
+ else
+ {
+ llwarns << "Got zero circuit code in use_circuit_code" << llendl;
+ }
+}
+
+
+
+static void check_for_unrecognized_messages(
+ const char* type,
+ const LLSD& map,
+ LLMessageSystem::message_template_name_map_t& templates)
+{
+ for (LLSD::map_const_iterator iter = map.beginMap(),
+ end = map.endMap();
+ iter != end; ++iter)
+ {
+ const char* name = gMessageStringTable.getString(iter->first.c_str());
+
+ if (templates.find(name) == templates.end())
+ {
+ llinfos << " " << type
+ << " ban list contains unrecognized message "
+ << name << llendl;
+ }
+ }
+}
+
+void LLMessageSystem::setMessageBans(
+ const LLSD& trusted, const LLSD& untrusted)
+{
+ llinfos << "LLMessageSystem::setMessageBans:" << llendl;
+ bool any_set = false;
+
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; ++iter)
+ {
+ LLMessageTemplate* mt = iter->second;
+
+ std::string name(mt->mName);
+ bool ban_from_trusted
+ = trusted.has(name) && trusted.get(name).asBoolean();
+ bool ban_from_untrusted
+ = untrusted.has(name) && untrusted.get(name).asBoolean();
+
+ mt->mBanFromTrusted = ban_from_trusted;
+ mt->mBanFromUntrusted = ban_from_untrusted;
+
+ if (ban_from_trusted || ban_from_untrusted)
+ {
+ llinfos << " " << name << " banned from "
+ << (ban_from_trusted ? "TRUSTED " : " ")
+ << (ban_from_untrusted ? "UNTRUSTED " : " ")
+ << llendl;
+ any_set = true;
+ }
+ }
+
+ if (!any_set)
+ {
+ llinfos << " no messages banned" << llendl;
+ }
+
+ check_for_unrecognized_messages("trusted", trusted, mMessageTemplates);
+ check_for_unrecognized_messages("untrusted", untrusted, mMessageTemplates);
+}
+
+void process_packet_ack(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ TPACKETID packet_id;
+
+ LLHost host = msgsystem->getSender();
+ LLCircuitData *cdp = msgsystem->mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+
+ S32 ack_count = msgsystem->getNumberOfBlocksFast(_PREHASH_Packets);
+
+ for (S32 i = 0; i < ack_count; i++)
+ {
+ msgsystem->getU32Fast(_PREHASH_Packets, _PREHASH_ID, packet_id, i);
+// llinfos << "ack recvd' from " << host << " for packet " << (TPACKETID)packet_id << llendl;
+ cdp->ackReliablePacket(packet_id);
+ }
+ if (!cdp->getUnackedPacketCount())
+ {
+ // Remove this circuit from the list of circuits with unacked packets
+ gMessageSystem->mCircuitInfo.mUnackedCircuitMap.erase(host);
+ }
+ }
+}
+
+void send_template_reply(LLMessageSystem* msg, const LLUUID& token)
+{
+ msg->newMessageFast(_PREHASH_TemplateChecksumReply);
+ msg->nextBlockFast(_PREHASH_DataBlock);
+ msg->addU32Fast(_PREHASH_Checksum, msg->mMessageFileChecksum);
+ msg->addU8Fast(_PREHASH_MajorVersion, U8(msg->mSystemVersionMajor) );
+ msg->addU8Fast(_PREHASH_MinorVersion, U8(msg->mSystemVersionMinor) );
+ msg->addU8Fast(_PREHASH_PatchVersion, U8(msg->mSystemVersionPatch) );
+ msg->addU8Fast(_PREHASH_ServerVersion, U8(msg->mSystemVersionServer) );
+ msg->addU32Fast(_PREHASH_Flags, msg->mVersionFlags);
+ msg->nextBlockFast(_PREHASH_TokenBlock);
+ msg->addUUIDFast(_PREHASH_Token, token);
+ msg->sendMessage(msg->getSender());
+}
+
+void process_template_checksum_request(LLMessageSystem* msg, void**)
+{
+ llinfos << "Message template checksum request received from "
+ << msg->getSender() << llendl;
+ send_template_reply(msg, LLUUID::null);
+}
+
+void process_secured_template_checksum_request(LLMessageSystem* msg, void**)
+{
+ llinfos << "Secured message template checksum request received from "
+ << msg->getSender() << llendl;
+ LLUUID token;
+ msg->getUUIDFast(_PREHASH_TokenBlock, _PREHASH_Token, token);
+ send_template_reply(msg, token);
+}
+
+void process_log_control(LLMessageSystem* msg, void**)
+{
+ U8 level;
+ U32 mask;
+ BOOL time;
+ BOOL location;
+ BOOL remote_infos;
+
+ msg->getU8Fast(_PREHASH_Options, _PREHASH_Level, level);
+ msg->getU32Fast(_PREHASH_Options, _PREHASH_Mask, mask);
+ msg->getBOOLFast(_PREHASH_Options, _PREHASH_Time, time);
+ msg->getBOOLFast(_PREHASH_Options, _PREHASH_Location, location);
+ msg->getBOOLFast(_PREHASH_Options, _PREHASH_RemoteInfos, remote_infos);
+
+ gErrorStream.setLevel(LLErrorStream::ELevel(level));
+ gErrorStream.setDebugMask(mask);
+ gErrorStream.setTime(time);
+ gErrorStream.setPrintLocation(location);
+ gErrorStream.setElevatedRemote(remote_infos);
+
+ llinfos << "Logging set to level " << gErrorStream.getLevel()
+ << " mask " << std::hex << gErrorStream.getDebugMask() << std::dec
+ << " time " << gErrorStream.getTime()
+ << " loc " << gErrorStream.getPrintLocation()
+ << llendl;
+}
+
+void process_log_messages(LLMessageSystem* msg, void**)
+{
+ U8 log_message;
+
+ msg->getU8Fast(_PREHASH_Options, _PREHASH_Enable, log_message);
+
+ if (log_message)
+ {
+ llinfos << "Starting logging via message" << llendl;
+ msg->startLogging();
+ }
+ else
+ {
+ llinfos << "Stopping logging via message" << llendl;
+ msg->stopLogging();
+ }
+}
+
+// Make circuit trusted if the MD5 Digest matches, otherwise
+// notify remote end that they are not trusted.
+void process_create_trusted_circuit(LLMessageSystem *msg, void **)
+{
+ // don't try to create trust on machines with no shared secret
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty()) return;
+
+ LLUUID remote_id;
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_EndPointID, remote_id);
+
+ LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ if (!cdp)
+ {
+ llwarns << "Attempt to create trusted circuit without circuit data: "
+ << msg->getSender() << llendl;
+ return;
+ }
+
+ LLUUID local_id;
+ local_id = cdp->getLocalEndPointID();
+ if (remote_id == local_id)
+ {
+ // Don't respond to requests that use the same end point ID
+ return;
+ }
+
+ char their_digest[MD5HEX_STR_SIZE];
+ msg->getBinaryDataFast(_PREHASH_DataBlock, _PREHASH_Digest, their_digest, 32);
+ their_digest[MD5HEX_STR_SIZE - 1] = '\0';
+ if(msg->isMatchingDigestForWindowAndUUIDs(their_digest, TRUST_TIME_WINDOW, local_id, remote_id))
+ {
+ cdp->setTrusted(TRUE);
+ llinfos << "Trusted digest from " << msg->getSender() << llendl;
+ return;
+ }
+ else if (cdp->getTrusted())
+ {
+ // The digest is bad, but this circuit is already trusted.
+ // This means that this could just be the result of a stale deny sent from a while back, and
+ // the message system is being slow. Don't bother sending the deny, as it may continually
+ // ping-pong back and forth on a very hosed circuit.
+ llwarns << "Ignoring bad digest from known trusted circuit: " << their_digest
+ << " host: " << msg->getSender() << llendl;
+ return;
+ }
+ else
+ {
+ llwarns << "Bad digest from known circuit: " << their_digest
+ << " host: " << msg->getSender() << llendl;
+ msg->sendDenyTrustedCircuit(msg->getSender());
+ return;
+ }
+}
+
+void process_deny_trusted_circuit(LLMessageSystem *msg, void **)
+{
+ // don't try to create trust on machines with no shared secret
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty()) return;
+
+ LLUUID remote_id;
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_EndPointID, remote_id);
+
+ LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ if (!cdp)
+ {
+ return;
+ }
+
+ LLUUID local_id;
+ local_id = cdp->getLocalEndPointID();
+ if (remote_id == local_id)
+ {
+ // Don't respond to requests that use the same end point ID
+ return;
+ }
+
+ // Assume that we require trust to proceed, so resend.
+ // This catches the case where a circuit that was trusted
+ // times out, and allows us to re-establish it, but does
+ // mean that if our shared_secret or clock is wrong, we'll
+ // spin.
+ // FIXME: probably should keep a count of number of resends
+ // per circuit, and stop resending after a while.
+ llinfos << "Got DenyTrustedCircuit. Sending CreateTrustedCircuit to "
+ << msg->getSender() << llendl;
+ msg->sendCreateTrustedCircuit(msg->getSender(), local_id, remote_id);
+}
+
+#define LL_ENCRYPT_BUF_LENGTH 16384
+
+void encrypt_template(const char *src_name, const char *dest_name)
+{
+ // encrypt and decrypt are symmetric
+ decrypt_template(src_name, dest_name);
+}
+
+BOOL decrypt_template(const char *src_name, const char *dest_name)
+{
+ S32 buf_length = LL_ENCRYPT_BUF_LENGTH;
+ char buf[LL_ENCRYPT_BUF_LENGTH];
+
+ FILE* infp = NULL;
+ FILE* outfp = NULL;
+ BOOL success = FALSE;
+ char* bufp = NULL;
+ U32 key = 0;
+ S32 more_data = 0;
+
+ if(src_name==NULL)
+ {
+ llwarns << "Input src_name is NULL!!" << llendl;
+ goto exit;
+ }
+
+ infp = LLFile::fopen(src_name,"rb");
+ if (!infp)
+ {
+ llwarns << "could not open " << src_name << " for reading" << llendl;
+ goto exit;
+ }
+
+ if(dest_name==NULL)
+ {
+ llwarns << "Output dest_name is NULL!!" << llendl;
+ goto exit;
+ }
+
+ outfp = LLFile::fopen(dest_name,"w+b");
+ if (!outfp)
+ {
+ llwarns << "could not open " << src_name << " for writing" << llendl;
+ goto exit;
+ }
+
+ while ((buf_length = (S32)fread(buf,1,LL_ENCRYPT_BUF_LENGTH,infp)))
+ {
+ // unscrozzle bits here
+ bufp = buf;
+ more_data = buf_length;
+ while (more_data--)
+ {
+ *bufp = *bufp ^ ((key * 43) % 256);
+ key++;
+ bufp++;
+ }
+
+ if(buf_length != (S32)fwrite(buf,1,buf_length,outfp))
+ {
+ goto exit;
+ }
+ }
+ success = TRUE;
+
+ exit:
+ if(infp) fclose(infp);
+ if(outfp) fclose(outfp);
+ return success;
+}
+
+void dump_prehash_files()
+{
+ U32 i;
+ FILE *fp = LLFile::fopen("../../indra/llmessage/message_prehash.h", "w");
+ if (fp)
+ {
+ fprintf(
+ fp,
+ "/**\n"
+ " * @file message_prehash.h\n"
+ " * @brief header file of externs of prehashed variables plus defines.\n"
+ " *\n"
+ " * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.\n"
+ " * $License$\n"
+ " */\n\n"
+ "#ifndef LL_MESSAGE_PREHASH_H\n#define LL_MESSAGE_PREHASH_H\n\n");
+ fprintf(
+ fp,
+ "/**\n"
+ " * Generated from message template version number %.3f\n"
+ " */\n",
+ gMessageSystem->mMessageFileVersionNumber);
+ fprintf(fp, "\n\nextern F32 gPrehashVersionNumber;\n\n");
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.')
+ {
+ fprintf(fp, "extern char * _PREHASH_%s;\n", gMessageStringTable.mString[i]);
+ }
+ }
+ fprintf(fp, "\n\nvoid init_prehash_data();\n\n");
+ fprintf(fp, "\n\n");
+ fprintf(fp, "\n\n#endif\n");
+ fclose(fp);
+ }
+ fp = LLFile::fopen("../../indra/llmessage/message_prehash.cpp", "w");
+ if (fp)
+ {
+ fprintf(
+ fp,
+ "/**\n"
+ " * @file message_prehash.cpp\n"
+ " * @brief file of prehashed variables\n"
+ " *\n"
+ " * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.\n"
+ " * $License$\n"
+ " */\n\n"
+ "/**\n"
+ " * Generated from message template version number %.3f\n"
+ " */\n",
+ gMessageSystem->mMessageFileVersionNumber);
+ fprintf(fp, "#include \"linden_common.h\"\n");
+ fprintf(fp, "#include \"message.h\"\n\n");
+ fprintf(fp, "\n\nF32 gPrehashVersionNumber = %.3ff;\n\n", gMessageSystem->mMessageFileVersionNumber);
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.')
+ {
+ fprintf(fp, "char * _PREHASH_%s;\n", gMessageStringTable.mString[i]);
+ }
+ }
+ fprintf(fp, "\nvoid init_prehash_data()\n");
+ fprintf(fp, "{\n");
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.')
+ {
+ fprintf(fp, "\t_PREHASH_%s = gMessageStringTable.getString(\"%s\");\n", gMessageStringTable.mString[i], gMessageStringTable.mString[i]);
+ }
+ }
+ fprintf(fp, "}\n");
+ fclose(fp);
+ }
+}
+
+BOOL start_messaging_system(
+ const std::string& template_name,
+ U32 port,
+ S32 version_major,
+ S32 version_minor,
+ S32 version_patch,
+ BOOL b_dump_prehash_file,
+ const std::string& secret)
+{
+ gMessageSystem = new LLMessageSystem(
+ template_name.c_str(),
+ port,
+ version_major,
+ version_minor,
+ version_patch);
+ g_shared_secret.assign(secret);
+
+ if (!gMessageSystem)
+ {
+ llerrs << "Messaging system initialization failed." << llendl;
+ return FALSE;
+ }
+
+ // bail if system encountered an error.
+ if(!gMessageSystem->isOK())
+ {
+ return FALSE;
+ }
+
+ if (b_dump_prehash_file)
+ {
+ dump_prehash_files();
+ exit(0);
+ }
+ else
+ {
+ init_prehash_data();
+ if (gMessageSystem->mMessageFileVersionNumber != gPrehashVersionNumber)
+ {
+ llinfos << "Message template version does not match prehash version number" << llendl;
+ llinfos << "Run simulator with -prehash command line option to rebuild prehash data" << llendl;
+ }
+ else
+ {
+ llinfos << "Message template version matches prehash version number" << llendl;
+ }
+ }
+
+ gMessageSystem->setHandlerFuncFast(_PREHASH_StartPingCheck, process_start_ping_check, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_CompletePingCheck, process_complete_ping_check, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_OpenCircuit, open_circuit, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_CloseCircuit, close_circuit, NULL);
+
+ //gMessageSystem->setHandlerFuncFast(_PREHASH_AssignCircuitCode, LLMessageSystem::processAssignCircuitCode);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_AddCircuitCode, LLMessageSystem::processAddCircuitCode);
+ //gMessageSystem->setHandlerFuncFast(_PREHASH_AckAddCircuitCode, ack_add_circuit_code, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_UseCircuitCode, LLMessageSystem::processUseCircuitCode);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck, process_packet_ack, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_TemplateChecksumRequest, process_template_checksum_request, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_SecuredTemplateChecksumRequest, process_secured_template_checksum_request, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_LogControl, process_log_control, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_LogMessages, process_log_messages, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_CreateTrustedCircuit,
+ process_create_trusted_circuit,
+ NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_DenyTrustedCircuit,
+ process_deny_trusted_circuit,
+ NULL);
+
+ // We can hand this to the null_message_callback since it is a
+ // trusted message, so it will automatically be denied if it isn't
+ // trusted and ignored if it is -- exactly what we want.
+ gMessageSystem->setHandlerFunc(
+ "RequestTrustedCircuit",
+ null_message_callback,
+ NULL);
+
+ // Initialize the transfer manager
+ gTransferManager.init();
+
+ return TRUE;
+}
+
+void LLMessageSystem::startLogging()
+{
+ mVerboseLog = TRUE;
+ std::ostringstream str;
+ str << "START MESSAGE LOG" << std::endl;
+ str << "Legend:" << std::endl;
+ str << "\t<-\tincoming message" <<std::endl;
+ str << "\t->\toutgoing message" << std::endl;
+ str << " <> host size zero id name";
+ llinfos << str.str() << llendl;
+}
+
+void LLMessageSystem::stopLogging()
+{
+ if(mVerboseLog)
+ {
+ mVerboseLog = FALSE;
+ llinfos << "END MESSAGE LOG" << llendl;
+ }
+}
+
+void LLMessageSystem::summarizeLogs(std::ostream& str)
+{
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ char tmp_str[MAX_STRING]; /* Flawfinder: ignore */
+ F32 run_time = mMessageSystemTimer.getElapsedTimeF32();
+ str << "START MESSAGE LOG SUMMARY" << std::endl;
+ snprintf(buffer, MAX_STRING, "Run time: %12.3f seconds", run_time); /* Flawfinder: ignore */
+
+ // Incoming
+ str << buffer << std::endl << "Incoming:" << std::endl;
+ U64_to_str(mTotalBytesIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total bytes received: %20s (%5.2f kbits per second)", tmp_str, ((F32)mTotalBytesIn * 0.008f) / run_time); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mPacketsIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total packets received: %20s (%5.2f packets per second)", tmp_str, ((F32) mPacketsIn / run_time)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Average packet size: %20.0f bytes", (F32)mTotalBytesIn / (F32)mPacketsIn); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mReliablePacketsIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total reliable packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mReliablePacketsIn)/((F32) mPacketsIn + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mCompressedPacketsIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compressed packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mCompressedPacketsIn)/((F32) mPacketsIn + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ S64 savings = mUncompressedBytesIn - mCompressedBytesIn;
+ U64_to_str(savings, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compression savings: %20s bytes", tmp_str); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mCompressedPacketsIn +1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str, ((F32) mUncompressedBytesIn)/((F32) mCompressedBytesIn+1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mPacketsIn+1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str, ((F32) mTotalBytesIn + (F32) savings)/((F32) mTotalBytesIn + 1.f)); /* Flawfinder: ignore */
+
+ // Outgoing
+ str << buffer << std::endl << std::endl << "Outgoing:" << std::endl;
+ U64_to_str(mTotalBytesOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total bytes sent: %20s (%5.2f kbits per second)", tmp_str, ((F32)mTotalBytesOut * 0.008f) / run_time ); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mPacketsOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total packets sent: %20s (%5.2f packets per second)", tmp_str, ((F32)mPacketsOut / run_time)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Average packet size: %20.0f bytes", (F32)mTotalBytesOut / (F32)mPacketsOut); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mReliablePacketsOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total reliable packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mReliablePacketsOut)/((F32) mPacketsOut + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mCompressedPacketsOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compressed packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mCompressedPacketsOut)/((F32) mPacketsOut + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ savings = mUncompressedBytesOut - mCompressedBytesOut;
+ U64_to_str(savings, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compression savings: %20s bytes", tmp_str); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mCompressedPacketsOut +1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str, ((F32) mUncompressedBytesOut)/((F32) mCompressedBytesOut+1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mPacketsOut+1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str, ((F32) mTotalBytesOut + (F32) savings)/((F32) mTotalBytesOut + 1.f)); /* Flawfinder: ignore */
+ str << buffer << std::endl << std::endl;
+ snprintf(buffer, MAX_STRING, "SendPacket failures: %20d", mSendPacketFailureCount); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Dropped packets: %20d", mDroppedPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Resent packets: %20d", mResentPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Failed reliable resends: %20d", mFailedResendPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Off-circuit rejected packets: %17d", mOffCircuitPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "On-circuit invalid packets: %17d", mInvalidOnCircuitPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl << std::endl;
+
+ str << "Decoding: " << std::endl;
+ snprintf(buffer, MAX_STRING, "%35s%10s%10s%10s%10s", "Message", "Count", "Time", "Max", "Avg"); /* Flawfinder: ignore */
+ str << buffer << std:: endl;
+ F32 avg;
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ if(mt->mTotalDecoded > 0)
+ {
+ avg = mt->mTotalDecodeTime / (F32)mt->mTotalDecoded;
+ snprintf(buffer, MAX_STRING, "%35s%10u%10f%10f%10f", mt->mName, mt->mTotalDecoded, mt->mTotalDecodeTime, mt->mMaxDecodeTimePerMsg, avg); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ }
+ }
+ str << "END MESSAGE LOG SUMMARY" << std::endl;
+}
+
+void end_messaging_system()
+{
+ gTransferManager.cleanup();
+ LLTransferTargetVFile::updateQueue(true); // shutdown LLTransferTargetVFile
+ if (gMessageSystem)
+ {
+ gMessageSystem->stopLogging();
+
+ std::ostringstream str;
+ gMessageSystem->summarizeLogs(str);
+ llinfos << str.str().c_str() << llendl;
+
+ delete gMessageSystem;
+ gMessageSystem = NULL;
+ }
+}
+
+void LLMessageSystem::resetReceiveCounts()
+{
+ mNumMessageCounts = 0;
+
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ mt->mDecodeTimeThisFrame = 0.f;
+ }
+}
+
+
+void LLMessageSystem::dumpReceiveCounts()
+{
+ LLMessageTemplate *mt;
+
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ mt->mReceiveCount = 0;
+ mt->mReceiveBytes = 0;
+ mt->mReceiveInvalid = 0;
+ }
+
+ S32 i;
+ for (i = 0; i < mNumMessageCounts; i++)
+ {
+ mt = get_ptr_in_map(mMessageNumbers,mMessageCountList[i].mMessageNum);
+ if (mt)
+ {
+ mt->mReceiveCount++;
+ mt->mReceiveBytes += mMessageCountList[i].mMessageBytes;
+ if (mMessageCountList[i].mInvalid)
+ {
+ mt->mReceiveInvalid++;
+ }
+ }
+ }
+
+ if(mNumMessageCounts > 0)
+ {
+ llinfos << "Dump: " << mNumMessageCounts << " messages processed in " << mReceiveTime << " seconds" << llendl;
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ if (mt->mReceiveCount > 0)
+ {
+ llinfos << "Num: " << std::setw(3) << mt->mReceiveCount << " Bytes: " << std::setw(6) << mt->mReceiveBytes
+ << " Invalid: " << std::setw(3) << mt->mReceiveInvalid << " " << mt->mName << " " << llround(100 * mt->mDecodeTimeThisFrame / mReceiveTime) << "%" << llendl;
+ }
+ }
+ }
+}
+
+
+
+BOOL LLMessageSystem::isClear() const
+{
+ return mbSClear;
+}
+
+
+S32 LLMessageSystem::flush(const LLHost &host)
+{
+ if (mCurrentSendTotal)
+ {
+ S32 sentbytes = sendMessage(host);
+ clearMessage();
+ return sentbytes;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+U32 LLMessageSystem::getListenPort( void ) const
+{
+ return mPort;
+}
+
+
+S32 LLMessageSystem::zeroCode(U8 **data, S32 *data_size)
+{
+ S32 count = *data_size;
+
+ S32 net_gain = 0;
+ U8 num_zeroes = 0;
+
+ U8 *inptr = (U8 *)*data;
+ U8 *outptr = (U8 *)mEncodedSendBuffer;
+
+// skip the packet id field
+
+ for (U32 i=0;i<LL_PACKET_ID_SIZE;i++)
+ {
+ count--;
+ *outptr++ = *inptr++;
+ }
+
+// build encoded packet, keeping track of net size gain
+
+// sequential zero bytes are encoded as 0 [U8 count]
+// with 0 0 [count] representing wrap (>256 zeroes)
+
+ while (count--)
+ {
+ if (!(*inptr)) // in a zero count
+ {
+ if (num_zeroes)
+ {
+ if (++num_zeroes > 254)
+ {
+ *outptr++ = num_zeroes;
+ num_zeroes = 0;
+ }
+ net_gain--; // subseqent zeroes save one
+ }
+ else
+ {
+ *outptr++ = 0;
+ net_gain++; // starting a zero count adds one
+ num_zeroes = 1;
+ }
+ inptr++;
+ }
+ else
+ {
+ if (num_zeroes)
+ {
+ *outptr++ = num_zeroes;
+ num_zeroes = 0;
+ }
+ *outptr++ = *inptr++;
+ }
+ }
+
+ if (num_zeroes)
+ {
+ *outptr++ = num_zeroes;
+ }
+
+ if (net_gain < 0)
+ {
+ mCompressedPacketsOut++;
+ mUncompressedBytesOut += *data_size;
+
+ *data = mEncodedSendBuffer;
+ *data_size += net_gain;
+ mEncodedSendBuffer[0] |= LL_ZERO_CODE_FLAG; // set the head bit to indicate zero coding
+
+ mCompressedBytesOut += *data_size;
+
+ }
+ mTotalBytesOut += *data_size;
+
+ return(net_gain);
+}
+
+S32 LLMessageSystem::zeroCodeAdjustCurrentSendTotal()
+{
+ if (!mbSBuilt)
+ {
+ buildMessage();
+ }
+ mbSBuilt = FALSE;
+
+ S32 count = mSendSize;
+
+ S32 net_gain = 0;
+ U8 num_zeroes = 0;
+
+ U8 *inptr = (U8 *)mSendBuffer;
+
+// skip the packet id field
+
+ for (U32 i=0;i<LL_PACKET_ID_SIZE;i++)
+ {
+ count--;
+ inptr++;
+ }
+
+// don't actually build, just test
+
+// sequential zero bytes are encoded as 0 [U8 count]
+// with 0 0 [count] representing wrap (>256 zeroes)
+
+ while (count--)
+ {
+ if (!(*inptr)) // in a zero count
+ {
+ if (num_zeroes)
+ {
+ if (++num_zeroes > 254)
+ {
+ num_zeroes = 0;
+ }
+ net_gain--; // subseqent zeroes save one
+ }
+ else
+ {
+ net_gain++; // starting a zero count adds one
+ num_zeroes = 1;
+ }
+ inptr++;
+ }
+ else
+ {
+ if (num_zeroes)
+ {
+ num_zeroes = 0;
+ }
+ inptr++;
+ }
+ }
+ if (net_gain < 0)
+ {
+ return net_gain;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+
+S32 LLMessageSystem::zeroCodeExpand(U8 **data, S32 *data_size)
+{
+
+ if ((*data_size ) < LL_PACKET_ID_SIZE)
+ {
+ llwarns << "zeroCodeExpand() called with data_size of " << *data_size << llendl;
+ }
+
+ mTotalBytesIn += *data_size;
+
+ if (!(*data[0] & LL_ZERO_CODE_FLAG)) // if we're not zero-coded, just go 'way
+ {
+ return(0);
+ }
+
+ S32 in_size = *data_size;
+ mCompressedPacketsIn++;
+ mCompressedBytesIn += *data_size;
+
+ *data[0] &= (~LL_ZERO_CODE_FLAG);
+
+ S32 count = (*data_size);
+
+ U8 *inptr = (U8 *)*data;
+ U8 *outptr = (U8 *)mEncodedRecvBuffer;
+
+// skip the packet id field
+
+ for (U32 i=0;i<LL_PACKET_ID_SIZE;i++)
+ {
+ count--;
+ *outptr++ = *inptr++;
+ }
+
+// reconstruct encoded packet, keeping track of net size gain
+
+// sequential zero bytes are encoded as 0 [U8 count]
+// with 0 0 [count] representing wrap (>256 zeroes)
+
+ while (count--)
+ {
+ if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-1]))
+ {
+ llwarns << "attempt to write past reasonable encoded buffer size 1" << llendl;
+ callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
+ outptr = mEncodedRecvBuffer;
+ break;
+ }
+ if (!((*outptr++ = *inptr++)))
+ {
+ while (((count--)) && (!(*inptr)))
+ {
+ *outptr++ = *inptr++;
+ if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-256]))
+ {
+ llwarns << "attempt to write past reasonable encoded buffer size 2" << llendl;
+ callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
+ outptr = mEncodedRecvBuffer;
+ count = -1;
+ break;
+ }
+ memset(outptr,0,255);
+ outptr += 255;
+ }
+
+ if (count < 0)
+ {
+ break;
+ }
+
+ else
+ {
+ if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-(*inptr)]))
+ {
+ llwarns << "attempt to write past reasonable encoded buffer size 3" << llendl;
+ callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
+ outptr = mEncodedRecvBuffer;
+ }
+ memset(outptr,0,(*inptr) - 1);
+ outptr += ((*inptr) - 1);
+ inptr++;
+ }
+ }
+ }
+
+ *data = mEncodedRecvBuffer;
+ *data_size = (S32)(outptr - mEncodedRecvBuffer);
+ mUncompressedBytesIn += *data_size;
+
+ return(in_size);
+}
+
+
+void LLMessageSystem::addTemplate(LLMessageTemplate *templatep)
+{
+ if (mMessageTemplates.count(templatep->mName) > 0)
+ {
+ llerrs << templatep->mName << " already used as a template name!"
+ << llendl;
+ }
+ mMessageTemplates[templatep->mName] = templatep;
+ mMessageNumbers[templatep->mMessageNumber] = templatep;
+}
+
+
+void LLMessageSystem::setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
+{
+ LLMessageTemplate* msgtemplate = get_ptr_in_map(mMessageTemplates, name);
+ if (msgtemplate)
+ {
+ msgtemplate->setHandlerFunc(handler_func, user_data);
+ }
+ else
+ {
+ llerrs << name << " is not a known message name!" << llendl;
+ }
+}
+
+
+bool LLMessageSystem::callHandler(const char *name,
+ bool trustedSource, LLMessageSystem* msg)
+{
+ name = gMessageStringTable.getString(name);
+ LLMessageTemplate* msg_template = mMessageTemplates[(char*)name];
+ if (!msg_template)
+ {
+ llwarns << "LLMessageSystem::callHandler: unknown message "
+ << name << llendl;
+ return false;
+ }
+
+ if (msg_template->isBanned(trustedSource))
+ {
+ llwarns << "LLMessageSystem::callHandler: banned message "
+ << name
+ << " from "
+ << (trustedSource ? "trusted " : "untrusted ")
+ << "source" << llendl;
+ return false;
+ }
+
+ return msg_template->callHandlerFunc(msg);
+}
+
+
+void LLMessageSystem::setExceptionFunc(EMessageException e,
+ msg_exception_callback func,
+ void* data)
+{
+ callbacks_t::iterator it = mExceptionCallbacks.find(e);
+ if(it != mExceptionCallbacks.end())
+ {
+ mExceptionCallbacks.erase(it);
+ }
+ if(func)
+ {
+ mExceptionCallbacks.insert(callbacks_t::value_type(e, exception_t(func, data)));
+ }
+}
+
+BOOL LLMessageSystem::callExceptionFunc(EMessageException exception)
+{
+ callbacks_t::iterator it = mExceptionCallbacks.find(exception);
+ if(it != mExceptionCallbacks.end())
+ {
+ ((*it).second.first)(this, (*it).second.second,exception);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLMessageSystem::isCircuitCodeKnown(U32 code) const
+{
+ if(mCircuitCodes.find(code) == mCircuitCodes.end())
+ return FALSE;
+ return TRUE;
+}
+
+BOOL LLMessageSystem::isMessageFast(const char *msg)
+{
+ if (mCurrentRMessageTemplate)
+ {
+ return(msg == mCurrentRMessageTemplate->mName);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+char* LLMessageSystem::getMessageName()
+{
+ if (mCurrentRMessageTemplate)
+ {
+ return mCurrentRMessageTemplate->mName;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+const LLUUID& LLMessageSystem::getSenderID() const
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(mLastSender);
+ if (cdp)
+ {
+ return (cdp->mRemoteID);
+ }
+
+ return LLUUID::null;
+}
+
+const LLUUID& LLMessageSystem::getSenderSessionID() const
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(mLastSender);
+ if (cdp)
+ {
+ return (cdp->mRemoteSessionID);
+ }
+ return LLUUID::null;
+}
+
+void LLMessageSystem::addVector3Fast(const char *varname, const LLVector3& vec)
+{
+ addDataFast(varname, vec.mV, MVT_LLVector3, sizeof(vec.mV));
+}
+
+void LLMessageSystem::addVector3(const char *varname, const LLVector3& vec)
+{
+ addDataFast(gMessageStringTable.getString(varname), vec.mV, MVT_LLVector3, sizeof(vec.mV));
+}
+
+void LLMessageSystem::addVector4Fast(const char *varname, const LLVector4& vec)
+{
+ addDataFast(varname, vec.mV, MVT_LLVector4, sizeof(vec.mV));
+}
+
+void LLMessageSystem::addVector4(const char *varname, const LLVector4& vec)
+{
+ addDataFast(gMessageStringTable.getString(varname), vec.mV, MVT_LLVector4, sizeof(vec.mV));
+}
+
+
+void LLMessageSystem::addVector3dFast(const char *varname, const LLVector3d& vec)
+{
+ addDataFast(varname, vec.mdV, MVT_LLVector3d, sizeof(vec.mdV));
+}
+
+void LLMessageSystem::addVector3d(const char *varname, const LLVector3d& vec)
+{
+ addDataFast(gMessageStringTable.getString(varname), vec.mdV, MVT_LLVector3d, sizeof(vec.mdV));
+}
+
+
+void LLMessageSystem::addQuatFast(const char *varname, const LLQuaternion& quat)
+{
+ addDataFast(varname, quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3));
+}
+
+void LLMessageSystem::addQuat(const char *varname, const LLQuaternion& quat)
+{
+ addDataFast(gMessageStringTable.getString(varname), quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3));
+}
+
+
+void LLMessageSystem::addUUIDFast(const char *varname, const LLUUID& uuid)
+{
+ addDataFast(varname, uuid.mData, MVT_LLUUID, sizeof(uuid.mData));
+}
+
+void LLMessageSystem::addUUID(const char *varname, const LLUUID& uuid)
+{
+ addDataFast(gMessageStringTable.getString(varname), uuid.mData, MVT_LLUUID, sizeof(uuid.mData));
+}
+
+void LLMessageSystem::getF32Fast(const char *block, const char *var, F32 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(F32), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF32Fast " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+void LLMessageSystem::getF32(const char *block, const char *var, F32 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(F32), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF32 " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+void LLMessageSystem::getF64Fast(const char *block, const char *var, F64 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(F64), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF64Fast " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+void LLMessageSystem::getF64(const char *block, const char *var, F64 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(F64), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF64 " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+
+void LLMessageSystem::getVector3Fast(const char *block, const char *var, LLVector3 &v, S32 blocknum )
+{
+ getDataFast(block, var, v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3Fast " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector3(const char *block, const char *var, LLVector3 &v, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector4 " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector4Fast(const char *block, const char *var, LLVector4 &v, S32 blocknum )
+{
+ getDataFast(block, var, v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector4Fast " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector4(const char *block, const char *var, LLVector4 &v, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3 " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector3dFast(const char *block, const char *var, LLVector3d &v, S32 blocknum )
+{
+ getDataFast(block, var, v.mdV, sizeof(v.mdV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3dFast " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+
+}
+
+void LLMessageSystem::getVector3d(const char *block, const char *var, LLVector3d &v, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mdV, sizeof(v.mdV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3d " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getQuatFast(const char *block, const char *var, LLQuaternion &q, S32 blocknum )
+{
+ LLVector3 vec;
+ getDataFast(block, var, vec.mV, sizeof(vec.mV), blocknum);
+ if( vec.isFinite() )
+ {
+ q.unpackFromVector3( vec );
+ }
+ else
+ {
+ llwarns << "non-finite in getQuatFast " << block << " " << var << llendl;
+ q.loadIdentity();
+ }
+}
+
+void LLMessageSystem::getQuat(const char *block, const char *var, LLQuaternion &q, S32 blocknum )
+{
+ LLVector3 vec;
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), vec.mV, sizeof(vec.mV), blocknum);
+ if( vec.isFinite() )
+ {
+ q.unpackFromVector3( vec );
+ }
+ else
+ {
+ llwarns << "non-finite in getQuat " << block << " " << var << llendl;
+ q.loadIdentity();
+ }
+}
+
+void LLMessageSystem::getUUIDFast(const char *block, const char *var, LLUUID &u, S32 blocknum )
+{
+ getDataFast(block, var, u.mData, sizeof(u.mData), blocknum);
+}
+
+void LLMessageSystem::getUUID(const char *block, const char *var, LLUUID &u, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), u.mData, sizeof(u.mData), blocknum);
+}
+
+bool LLMessageSystem::generateDigestForNumberAndUUIDs(char* digest, const U32 number, const LLUUID &id1, const LLUUID &id2) const
+{
+ const char *colon = ":";
+ char tbuf[16]; /* Flawfinder: ignore */
+ LLMD5 d;
+ LLString id1string = id1.getString();
+ LLString id2string = id2.getString();
+ std::string shared_secret = get_shared_secret();
+ unsigned char * secret = (unsigned char*)shared_secret.c_str();
+ unsigned char * id1str = (unsigned char*)id1string.c_str();
+ unsigned char * id2str = (unsigned char*)id2string.c_str();
+
+ memset(digest, 0, MD5HEX_STR_SIZE);
+
+ if( secret != NULL)
+ {
+ d.update(secret, (U32)strlen((char *) secret));
+ }
+
+ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
+
+ snprintf(tbuf, sizeof(tbuf),"%i", number); /* Flawfinder: ignore */
+ d.update((unsigned char *) tbuf, (U32)strlen(tbuf)); /* Flawfinder: ignore */
+
+ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
+ if( (char*) id1str != NULL)
+ {
+ d.update(id1str, (U32)strlen((char *) id1str));
+ }
+ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
+
+ if( (char*) id2str != NULL)
+ {
+ d.update(id2str, (U32)strlen((char *) id2str));
+ }
+
+ d.finalize();
+ d.hex_digest(digest);
+ digest[MD5HEX_STR_SIZE - 1] = '\0';
+
+ return true;
+}
+
+bool LLMessageSystem::generateDigestForWindowAndUUIDs(char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const
+{
+ if(0 == window) return false;
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to generate complex digest on a machine without a shared secret!" << llendl;
+ }
+
+ U32 now = time(NULL);
+
+ now /= window;
+
+ bool result = generateDigestForNumberAndUUIDs(digest, now, id1, id2);
+
+ return result;
+}
+
+bool LLMessageSystem::isMatchingDigestForWindowAndUUIDs(const char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const
+{
+ if(0 == window) return false;
+
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to compare complex digests on a machine without a shared secret!" << llendl;
+ }
+
+ char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ U32 now = time(NULL);
+
+ now /= window;
+
+ // Check 1 window ago, now, and one window from now to catch edge
+ // conditions. Process them as current window, one window ago, and
+ // one window in the future to catch the edges.
+ const S32 WINDOW_BIN_COUNT = 3;
+ U32 window_bin[WINDOW_BIN_COUNT];
+ window_bin[0] = now;
+ window_bin[1] = now - 1;
+ window_bin[2] = now + 1;
+ for(S32 i = 0; i < WINDOW_BIN_COUNT; ++i)
+ {
+ generateDigestForNumberAndUUIDs(our_digest, window_bin[i], id2, id1);
+ if(0 == strncmp(digest, our_digest, MD5HEX_STR_BYTES))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLMessageSystem::generateDigestForNumber(char* digest, const U32 number) const
+{
+ memset(digest, 0, MD5HEX_STR_SIZE);
+
+ LLMD5 d;
+ std::string shared_secret = get_shared_secret();
+ d = LLMD5((const unsigned char *)shared_secret.c_str(), number);
+ d.hex_digest(digest);
+ digest[MD5HEX_STR_SIZE - 1] = '\0';
+
+ return true;
+}
+
+bool LLMessageSystem::generateDigestForWindow(char* digest, const S32 window) const
+{
+ if(0 == window) return false;
+
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to generate simple digest on a machine without a shared secret!" << llendl;
+ }
+
+ U32 now = time(NULL);
+
+ now /= window;
+
+ bool result = generateDigestForNumber(digest, now);
+
+ return result;
+}
+
+bool LLMessageSystem::isMatchingDigestForWindow(const char* digest, S32 const window) const
+{
+ if(0 == window) return false;
+
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to compare simple digests on a machine without a shared secret!" << llendl;
+ }
+
+ char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ U32 now = (S32)time(NULL);
+
+ now /= window;
+
+ // Check 1 window ago, now, and one window from now to catch edge
+ // conditions. Process them as current window, one window ago, and
+ // one window in the future to catch the edges.
+ const S32 WINDOW_BIN_COUNT = 3;
+ U32 window_bin[WINDOW_BIN_COUNT];
+ window_bin[0] = now;
+ window_bin[1] = now - 1;
+ window_bin[2] = now + 1;
+ for(S32 i = 0; i < WINDOW_BIN_COUNT; ++i)
+ {
+ generateDigestForNumber(our_digest, window_bin[i]);
+ if(0 == strncmp(digest, our_digest, MD5HEX_STR_BYTES))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LLMessageSystem::sendCreateTrustedCircuit(const LLHost &host, const LLUUID & id1, const LLUUID & id2)
+{
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty()) return;
+ char digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ if (id1.isNull())
+ {
+ llwarns << "Can't send CreateTrustedCircuit to " << host << " because we don't have the local end point ID" << llendl;
+ return;
+ }
+ if (id2.isNull())
+ {
+ llwarns << "Can't send CreateTrustedCircuit to " << host << " because we don't have the remote end point ID" << llendl;
+ return;
+ }
+ generateDigestForWindowAndUUIDs(digest, TRUST_TIME_WINDOW, id1, id2);
+ newMessageFast(_PREHASH_CreateTrustedCircuit);
+ nextBlockFast(_PREHASH_DataBlock);
+ addUUIDFast(_PREHASH_EndPointID, id1);
+ addBinaryDataFast(_PREHASH_Digest, digest, MD5HEX_STR_BYTES);
+ llinfos << "xmitting digest: " << digest << " Host: " << host << llendl;
+ sendMessage(host);
+}
+
+void LLMessageSystem::sendDenyTrustedCircuit(const LLHost &host)
+{
+ mDenyTrustedCircuitSet.insert(host);
+}
+
+void LLMessageSystem::reallySendDenyTrustedCircuit(const LLHost &host)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ llwarns << "Not sending DenyTrustedCircuit to host without a circuit." << llendl;
+ return;
+ }
+ llinfos << "Sending DenyTrustedCircuit to " << host << llendl;
+ newMessageFast(_PREHASH_DenyTrustedCircuit);
+ nextBlockFast(_PREHASH_DataBlock);
+ addUUIDFast(_PREHASH_EndPointID, cdp->getLocalEndPointID());
+ sendMessage(host);
+}
+
+void null_message_callback(LLMessageSystem *msg, void **data)
+{
+ // Nothing should ever go here, but we use this to register messages
+ // that we are expecting to see (and spinning on) at startup.
+ return;
+}
+
+// Try to establish a bidirectional trust metric by pinging a host until it's
+// up, and then sending auth messages.
+void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_count )
+{
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to establish bidirectional trust on a machine without a shared secret!" << llendl;
+ }
+ LLTimer timeout;
+
+ timeout.setTimerExpirySec(20.0);
+ setHandlerFuncFast(_PREHASH_StartPingCheck, null_message_callback, NULL);
+ setHandlerFuncFast(_PREHASH_CompletePingCheck, null_message_callback,
+ NULL);
+
+ while (! timeout.hasExpired())
+ {
+ newMessageFast(_PREHASH_StartPingCheck);
+ nextBlockFast(_PREHASH_PingID);
+ addU8Fast(_PREHASH_PingID, 0);
+ addU32Fast(_PREHASH_OldestUnacked, 0);
+ sendMessage(host);
+ if (checkMessages( frame_count ))
+ {
+ if (isMessageFast(_PREHASH_CompletePingCheck) &&
+ (getSender() == host))
+ {
+ break;
+ }
+ }
+ processAcks();
+ ms_sleep(1);
+ }
+
+ // Send a request, a deny, and give the host 2 seconds to complete
+ // the trust handshake.
+ newMessage("RequestTrustedCircuit");
+ sendMessage(host);
+ reallySendDenyTrustedCircuit(host);
+ setHandlerFuncFast(_PREHASH_StartPingCheck, process_start_ping_check, NULL);
+ setHandlerFuncFast(_PREHASH_CompletePingCheck, process_complete_ping_check, NULL);
+
+ timeout.setTimerExpirySec(2.0);
+ LLCircuitData* cdp = NULL;
+ while(!timeout.hasExpired())
+ {
+ cdp = mCircuitInfo.findCircuit(host);
+ if(!cdp) break; // no circuit anymore, no point continuing.
+ if(cdp->getTrusted()) break; // circuit is trusted.
+ checkMessages(frame_count);
+ processAcks();
+ ms_sleep(1);
+ }
+}
+
+
+void LLMessageSystem::dumpPacketToLog()
+{
+ llwarns << "Packet Dump from:" << mPacketRing.getLastSender() << llendl;
+ llwarns << "Packet Size:" << mTrueReceiveSize << llendl;
+ char line_buffer[256]; /* Flawfinder: ignore */
+ S32 i;
+ S32 cur_line_pos = 0;
+
+ S32 cur_line = 0;
+ for (i = 0; i < mTrueReceiveSize; i++)
+ {
+ snprintf(line_buffer + cur_line_pos*3, sizeof(line_buffer),"%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */
+ cur_line_pos++;
+ if (cur_line_pos >= 16)
+ {
+ cur_line_pos = 0;
+ llwarns << "PD:" << cur_line << "PD:" << line_buffer << llendl;
+ cur_line++;
+ }
+ }
+ if (cur_line_pos)
+ {
+ llwarns << "PD:" << cur_line << "PD:" << line_buffer << llendl;
+ }
+}
+
+//static
+U64 LLMessageSystem::getMessageTimeUsecs(const BOOL update)
+{
+ if (gMessageSystem)
+ {
+ if (update)
+ {
+ gMessageSystem->mCurrentMessageTimeSeconds = totalTime()*SEC_PER_USEC;
+ }
+ return (U64)(gMessageSystem->mCurrentMessageTimeSeconds * USEC_PER_SEC);
+ }
+ else
+ {
+ return totalTime();
+ }
+}
+
+//static
+F64 LLMessageSystem::getMessageTimeSeconds(const BOOL update)
+{
+ if (gMessageSystem)
+ {
+ if (update)
+ {
+ gMessageSystem->mCurrentMessageTimeSeconds = totalTime()*SEC_PER_USEC;
+ }
+ return gMessageSystem->mCurrentMessageTimeSeconds;
+ }
+ else
+ {
+ return totalTime()*SEC_PER_USEC;
+ }
+}
+
+std::string get_shared_secret()
+{
+ static const std::string SHARED_SECRET_KEY("shared_secret");
+ if(g_shared_secret.empty())
+ {
+ LLApp* app = LLApp::instance();
+ if(app) return app->getOption(SHARED_SECRET_KEY);
+ }
+ return g_shared_secret;
+}
+
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
new file mode 100644
index 0000000000..c33016669d
--- /dev/null
+++ b/indra/llmessage/message.h
@@ -0,0 +1,1253 @@
+/**
+ * @file message.h
+ * @brief LLMessageSystem class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MESSAGE_H
+#define LL_MESSAGE_H
+
+#include <cstring>
+#include <stdio.h>
+#include <map>
+#include <set>
+
+#if LL_LINUX
+#include <endian.h>
+#include <netinet/in.h>
+#endif
+
+#if LL_WINDOWS
+#include "winsock2.h" // htons etc.
+#endif
+
+#include "llerror.h"
+#include "net.h"
+#include "string_table.h"
+#include "llptrskipmap.h"
+#include "llcircuit.h"
+#include "lltimer.h"
+#include "llpacketring.h"
+#include "llhost.h"
+#include "llpacketack.h"
+#include "doublelinkedlist.h"
+#include "message_prehash.h"
+#include "llstl.h"
+#include "lldarray.h"
+
+const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
+const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
+
+const S32 MESSAGE_MAX_PER_FRAME = 400;
+
+// FIXME: This needs to be moved to a server-side only header.
+// 30 Sep 2002 mark
+//extern char *MESSAGE_SHARED_SECRET;
+
+class LLMessageStringTable
+{
+public:
+ LLMessageStringTable();
+ ~LLMessageStringTable();
+
+ char *getString(const char *str);
+
+ U32 mUsed;
+ BOOL mEmpty[MESSAGE_NUMBER_OF_HASH_BUCKETS];
+ char mString[MESSAGE_NUMBER_OF_HASH_BUCKETS][MESSAGE_MAX_STRINGS_LENGTH]; /* Flawfinder: ignore */
+};
+
+extern LLMessageStringTable gMessageStringTable;
+
+// Individual Messages are described with the following format
+// Note that to ease parsing, keywords are used
+//
+// // Comment (Comment like a C++ single line comment)
+// Comments can only be placed between Messages
+// {
+// MessageName (same naming restrictions as C variable)
+// Frequency ("High", "Medium", or "Low" - determines whether message ID is 8, 16, or 32-bits --
+// there can 254 messages in the first 2 groups, 32K in the last group)
+// (A message can be made up only of the Name if it is only a signal)
+// Trust ("Trusted", "NotTrusted" - determines if a message will be accepted
+// on a circuit. "Trusted" messages are not accepted from NotTrusted circuits
+// while NotTrusted messages are accepted on any circuit. An example of a
+// NotTrusted circuit is any circuit from the viewer.)
+// Encoding ("Zerocoded", "Unencoded" - zerocoded messages attempt to compress sequences of
+// zeros, but if there is no space win, it discards the compression and goes unencoded)
+// {
+// Block Name (same naming restrictions as C variable)
+// Block Type ("Single", "Multiple", or "Variable" - determines if the block is coded once,
+// a known number of times, or has a 8 bit argument encoded to tell the decoder
+// how many times the group is repeated)
+// Block Repeat Number (Optional - used only with the "Multiple" type - tells how many times the field is repeated
+// {
+// Variable 1 Name (same naming restrictions as C variable)
+// Variable Type ("Fixed" or "Variable" - determines if the variable is of fixed size or needs to
+// encode an argument describing the size in bytes)
+// Variable Size (In bytes, either of the "Fixed" variable itself or of the size argument)
+//
+// repeat variables
+//
+// }
+//
+// Repeat for number of variables in block
+// }
+//
+// Repeat for number of blocks in message
+// }
+// Repeat for number of messages in file
+//
+
+// Constants
+const S32 MAX_MESSAGE_INTERNAL_NAME_SIZE = 255;
+const S32 MAX_BUFFER_SIZE = NET_BUFFER_SIZE;
+const S32 MAX_BLOCKS = 255;
+
+const U8 LL_ZERO_CODE_FLAG = 0x80;
+const U8 LL_RELIABLE_FLAG = 0x40;
+const U8 LL_RESENT_FLAG = 0x20;
+const U8 LL_ACK_FLAG = 0x10;
+
+const S32 LL_MINIMUM_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 1; // 4 bytes id + 1 byte message name (high)
+
+const S32 LL_DEFAULT_RELIABLE_RETRIES = 3;
+const F32 LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS = 1.f;
+const F32 LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS = 1.f;
+const F32 LL_PING_BASED_TIMEOUT_DUMMY = 0.0f;
+
+// FIXME: These factors shouldn't include the msec to sec conversion implicitly
+const F32 LL_SEMIRELIABLE_TIMEOUT_FACTOR = 5.f / 1000.f; // factor * averaged ping
+const F32 LL_RELIABLE_TIMEOUT_FACTOR = 5.f / 1000.f; // factor * averaged ping
+const F32 LL_FILE_XFER_TIMEOUT_FACTOR = 5.f / 1000.f; // factor * averaged ping
+const F32 LL_LOST_TIMEOUT_FACTOR = 16.f / 1000.f; // factor * averaged ping for marking packets "Lost"
+const F32 LL_MAX_LOST_TIMEOUT = 5.f; // Maximum amount of time before considering something "lost"
+
+const S32 MAX_MESSAGE_COUNT_NUM = 1024;
+
+// Forward declarations
+class LLCircuit;
+class LLVector3;
+class LLVector4;
+class LLVector3d;
+class LLQuaternion;
+class LLSD;
+class LLUUID;
+class LLMessageSystem;
+
+// message data pieces are used to collect the data called for by the message template
+
+// iterator typedefs precede each class as needed
+typedef enum e_message_variable_type
+{
+ MVT_NULL,
+ MVT_FIXED,
+ MVT_VARIABLE,
+ MVT_U8,
+ MVT_U16,
+ MVT_U32,
+ MVT_U64,
+ MVT_S8,
+ MVT_S16,
+ MVT_S32,
+ MVT_S64,
+ MVT_F32,
+ MVT_F64,
+ MVT_LLVector3,
+ MVT_LLVector3d,
+ MVT_LLVector4,
+ MVT_LLQuaternion,
+ MVT_LLUUID,
+ MVT_BOOL,
+ MVT_IP_ADDR,
+ MVT_IP_PORT,
+ MVT_U16Vec3,
+ MVT_U16Quat,
+ MVT_S16Array,
+ MVT_EOL
+} EMsgVariableType;
+
+// message system exceptional condition handlers.
+enum EMessageException
+{
+ MX_UNREGISTERED_MESSAGE, // message number not part of template
+ MX_PACKET_TOO_SHORT, // invalid packet, shorter than minimum packet size
+ MX_RAN_OFF_END_OF_PACKET, // ran off the end of the packet during decode
+ MX_WROTE_PAST_BUFFER_SIZE // wrote past buffer size in zero code expand
+};
+typedef void (*msg_exception_callback)(LLMessageSystem*,void*,EMessageException);
+
+
+
+class LLMsgData;
+class LLMsgBlkData;
+class LLMessageTemplate;
+
+class LLMessagePollInfo;
+
+class LLMessageSystem
+{
+public:
+ U8 mSendBuffer[MAX_BUFFER_SIZE];
+ // Encoded send buffer needs to be slightly larger since the zero
+ // coding can potentially increase the size of the send data.
+ U8 mEncodedSendBuffer[2 * MAX_BUFFER_SIZE];
+ S32 mSendSize;
+ S32 mCurrentSendTotal;
+
+ LLPacketRing mPacketRing;
+ LLReliablePacketParams mReliablePacketParams;
+
+ //LLLinkedList<LLPacketAck> mAckList;
+
+ // Set this flag to TRUE when you want *very* verbose logs.
+ BOOL mVerboseLog;
+
+ U32 mMessageFileChecksum;
+ F32 mMessageFileVersionNumber;
+
+ typedef std::map<const char *, LLMessageTemplate*> message_template_name_map_t;
+ typedef std::map<U32, LLMessageTemplate*> message_template_number_map_t;
+
+private:
+ message_template_name_map_t mMessageTemplates;
+ message_template_number_map_t mMessageNumbers;
+
+public:
+ S32 mSystemVersionMajor;
+ S32 mSystemVersionMinor;
+ S32 mSystemVersionPatch;
+ S32 mSystemVersionServer;
+ U32 mVersionFlags;
+
+
+ BOOL mbProtected;
+
+ U32 mNumberHighFreqMessages;
+ U32 mNumberMediumFreqMessages;
+ U32 mNumberLowFreqMessages;
+ S32 mPort;
+ S32 mSocket;
+
+ U32 mPacketsIn; // total packets in, including compressed and uncompressed
+ U32 mPacketsOut; // total packets out, including compressed and uncompressed
+
+ U64 mBytesIn; // total bytes in, including compressed and uncompressed
+ U64 mBytesOut; // total bytes out, including compressed and uncompressed
+
+ U32 mCompressedPacketsIn; // total compressed packets in
+ U32 mCompressedPacketsOut; // total compressed packets out
+
+ U32 mReliablePacketsIn; // total reliable packets in
+ U32 mReliablePacketsOut; // total reliable packets out
+
+ U32 mDroppedPackets; // total dropped packets in
+ U32 mResentPackets; // total resent packets out
+ U32 mFailedResendPackets; // total resend failure packets out
+ U32 mOffCircuitPackets; // total # of off-circuit packets rejected
+ U32 mInvalidOnCircuitPackets; // total # of on-circuit but invalid packets rejected
+
+ S64 mUncompressedBytesIn; // total uncompressed size of compressed packets in
+ S64 mUncompressedBytesOut; // total uncompressed size of compressed packets out
+ S64 mCompressedBytesIn; // total compressed size of compressed packets in
+ S64 mCompressedBytesOut; // total compressed size of compressed packets out
+ S64 mTotalBytesIn; // total size of all uncompressed packets in
+ S64 mTotalBytesOut; // total size of all uncompressed packets out
+
+ BOOL mSendReliable; // does the outgoing message require a pos ack?
+
+ LLCircuit mCircuitInfo;
+ F64 mCircuitPrintTime; // used to print circuit debug info every couple minutes
+ F32 mCircuitPrintFreq; // seconds
+
+ std::map<U64, U32> mIPPortToCircuitCode;
+ std::map<U32, U64> mCircuitCodeToIPPort;
+ U32 mOurCircuitCode;
+ S32 mSendPacketFailureCount;
+ S32 mUnackedListDepth;
+ S32 mUnackedListSize;
+ S32 mDSMaxListDepth;
+
+public:
+ // Read file and build message templates
+ LLMessageSystem(const char *filename, U32 port, S32 version_major,
+ S32 version_minor, S32 version_patch);
+
+public:
+ // Subclass use.
+ LLMessageSystem();
+
+public:
+ virtual ~LLMessageSystem();
+
+ BOOL isOK() const { return !mbError; }
+ S32 getErrorCode() const { return mErrorCode; }
+
+ // Read file and build message templates filename must point to a
+ // valid string which specifies the path of a valid linden
+ // template.
+ void loadTemplateFile(const char* filename);
+
+
+ // methods for building, sending, receiving, and handling messages
+ void setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL);
+ void setHandlerFunc(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL)
+ {
+ setHandlerFuncFast(gMessageStringTable.getString(name), handler_func, user_data);
+ }
+
+ bool callHandler(const char *name, bool trustedSource,
+ LLMessageSystem* msg);
+
+ // Set a callback function for a message system exception.
+ void setExceptionFunc(EMessageException exception, msg_exception_callback func, void* data = NULL);
+ // Call the specified exception func, and return TRUE if a
+ // function was found and called. Otherwise return FALSE.
+ BOOL callExceptionFunc(EMessageException exception);
+
+ // This method returns true if the code is in the circuit codes map.
+ BOOL isCircuitCodeKnown(U32 code) const;
+
+ // usually called in response to an AddCircuitCode message, but
+ // may also be called by the login process.
+ bool addCircuitCode(U32 code, const LLUUID& session_id);
+
+ BOOL poll(F32 seconds); // Number of seconds that we want to block waiting for data, returns if data was received
+ BOOL checkMessages( S64 frame_count = 0 );
+ void processAcks();
+
+ BOOL isMessageFast(const char *msg);
+ BOOL isMessage(const char *msg)
+ {
+ return isMessageFast(gMessageStringTable.getString(msg));
+ }
+
+ void dumpPacketToLog();
+
+ char *getMessageName();
+
+ const LLHost& getSender() const;
+ U32 getSenderIP() const; // getSender() is preferred
+ U32 getSenderPort() const; // getSender() is preferred
+
+ // This method returns the uuid associated with the sender. The
+ // UUID will be null if it is not yet known or is a server
+ // circuit.
+ const LLUUID& getSenderID() const;
+
+ // This method returns the session id associated with the last
+ // sender.
+ const LLUUID& getSenderSessionID() const;
+
+ // set & get the session id (useful for viewers for now.)
+ void setMySessionID(const LLUUID& session_id) { mSessionID = session_id; }
+ const LLUUID& getMySessionID() { return mSessionID; }
+
+ virtual void newMessageFast(const char *name);
+ void newMessage(const char *name)
+ {
+ newMessageFast(gMessageStringTable.getString(name));
+ }
+
+ void copyMessageRtoS();
+ void clearMessage();
+
+ virtual void nextBlockFast(const char *blockname);
+ void nextBlock(const char *blockname)
+ {
+ nextBlockFast(gMessageStringTable.getString(blockname));
+ }
+private:
+ void addDataFast(const char *varname, const void *data, EMsgVariableType type, S32 size); // Use only for types not in system already
+ void addData(const char *varname, const void *data, EMsgVariableType type, S32 size)
+ {
+ addDataFast(gMessageStringTable.getString(varname), data, type, size);
+ }
+
+
+ void addDataFast(const char *varname, const void *data, EMsgVariableType type); // DEPRECATED - not typed, doesn't check storage space
+ void addData(const char *varname, const void *data, EMsgVariableType type)
+ {
+ addDataFast(gMessageStringTable.getString(varname), data, type);
+ }
+public:
+ void addBinaryDataFast(const char *varname, const void *data, S32 size)
+ {
+ addDataFast(varname, data, MVT_FIXED, size);
+ }
+ void addBinaryData(const char *varname, const void *data, S32 size)
+ {
+ addDataFast(gMessageStringTable.getString(varname), data, MVT_FIXED, size);
+ }
+
+ void addBOOLFast( const char* varname, BOOL b); // typed, checks storage space
+ void addBOOL( const char* varname, BOOL b); // typed, checks storage space
+ void addS8Fast( const char *varname, S8 s); // typed, checks storage space
+ void addS8( const char *varname, S8 s); // typed, checks storage space
+ void addU8Fast( const char *varname, U8 u); // typed, checks storage space
+ void addU8( const char *varname, U8 u); // typed, checks storage space
+ void addS16Fast( const char *varname, S16 i); // typed, checks storage space
+ void addS16( const char *varname, S16 i); // typed, checks storage space
+ void addU16Fast( const char *varname, U16 i); // typed, checks storage space
+ void addU16( const char *varname, U16 i); // typed, checks storage space
+ void addF32Fast( const char *varname, F32 f); // typed, checks storage space
+ void addF32( const char *varname, F32 f); // typed, checks storage space
+ void addS32Fast( const char *varname, S32 s); // typed, checks storage space
+ void addS32( const char *varname, S32 s); // typed, checks storage space
+ virtual void addU32Fast( const char *varname, U32 u); // typed, checks storage space
+ void addU32( const char *varname, U32 u); // typed, checks storage space
+ void addU64Fast( const char *varname, U64 lu); // typed, checks storage space
+ void addU64( const char *varname, U64 lu); // typed, checks storage space
+ void addF64Fast( const char *varname, F64 d); // typed, checks storage space
+ void addF64( const char *varname, F64 d); // typed, checks storage space
+ void addVector3Fast( const char *varname, const LLVector3& vec); // typed, checks storage space
+ void addVector3( const char *varname, const LLVector3& vec); // typed, checks storage space
+ void addVector4Fast( const char *varname, const LLVector4& vec); // typed, checks storage space
+ void addVector4( const char *varname, const LLVector4& vec); // typed, checks storage space
+ void addVector3dFast( const char *varname, const LLVector3d& vec); // typed, checks storage space
+ void addVector3d( const char *varname, const LLVector3d& vec); // typed, checks storage space
+ void addQuatFast( const char *varname, const LLQuaternion& quat); // typed, checks storage space
+ void addQuat( const char *varname, const LLQuaternion& quat); // typed, checks storage space
+ virtual void addUUIDFast( const char *varname, const LLUUID& uuid); // typed, checks storage space
+ void addUUID( const char *varname, const LLUUID& uuid); // typed, checks storage space
+ void addIPAddrFast( const char *varname, const U32 ip); // typed, checks storage space
+ void addIPAddr( const char *varname, const U32 ip); // typed, checks storage space
+ void addIPPortFast( const char *varname, const U16 port); // typed, checks storage space
+ void addIPPort( const char *varname, const U16 port); // typed, checks storage space
+ void addStringFast( const char* varname, const char* s); // typed, checks storage space
+ void addString( const char* varname, const char* s); // typed, checks storage space
+ void addStringFast( const char* varname, const std::string& s); // typed, checks storage space
+ void addString( const char* varname, const std::string& s); // typed, checks storage space
+
+ S32 getCurrentSendTotal() const { return mCurrentSendTotal; }
+
+ // This method checks for current send total and returns true if
+ // you need to go to the next block type or need to start a new
+ // message. Specify the current blockname to check block counts,
+ // otherwise the method only checks against MTU.
+ BOOL isSendFull(const char* blockname = NULL);
+ BOOL isSendFullFast(const char* blockname = NULL);
+
+ BOOL removeLastBlock();
+
+ void buildMessage();
+
+ S32 zeroCode(U8 **data, S32 *data_size);
+ S32 zeroCodeExpand(U8 **data, S32 *data_size);
+ S32 zeroCodeAdjustCurrentSendTotal();
+
+ // Uses ping-based retry
+ virtual S32 sendReliable(const LLHost &host);
+
+ // Uses ping-based retry
+ S32 sendReliable(const U32 circuit) { return sendReliable(findHost(circuit)); }
+
+ // Use this one if you DON'T want automatic ping-based retry.
+ S32 sendReliable( const LLHost &host,
+ S32 retries,
+ BOOL ping_based_retries,
+ F32 timeout,
+ void (*callback)(void **,S32),
+ void ** callback_data);
+
+ S32 sendSemiReliable( const LLHost &host,
+ void (*callback)(void **,S32), void ** callback_data);
+
+ // flush sends a message only if data's been pushed on it.
+ S32 flushSemiReliable( const LLHost &host,
+ void (*callback)(void **,S32), void ** callback_data);
+
+ S32 flushReliable( const LLHost &host );
+
+ void forwardMessage(const LLHost &host);
+ void forwardReliable(const LLHost &host);
+ void forwardReliable(const U32 circuit_code);
+
+ S32 sendMessage(const LLHost &host);
+ S32 sendMessage(const U32 circuit);
+
+ BOOL decodeData(const U8 *buffer, const LLHost &host);
+
+ // TODO: Consolide these functions
+ // TODO: Make these private, force use of typed functions.
+ // If size is not 0, an error is generated if size doesn't exactly match the size of the data.
+ // At all times, the number if bytes written to *datap is <= max_size.
+private:
+ void getDataFast(const char *blockname, const char *varname, void *datap, S32 size = 0, S32 blocknum = 0, S32 max_size = S32_MAX);
+ void getData(const char *blockname, const char *varname, void *datap, S32 size = 0, S32 blocknum = 0, S32 max_size = S32_MAX)
+ {
+ getDataFast(gMessageStringTable.getString(blockname), gMessageStringTable.getString(varname), datap, size, blocknum, max_size);
+ }
+public:
+ void getBinaryDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX)
+ {
+ getDataFast(blockname, varname, datap, size, blocknum, max_size);
+ }
+ void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX)
+ {
+ getDataFast(gMessageStringTable.getString(blockname), gMessageStringTable.getString(varname), datap, size, blocknum, max_size);
+ }
+
+ void getBOOLFast( const char *block, const char *var, BOOL &data, S32 blocknum = 0);
+ void getBOOL( const char *block, const char *var, BOOL &data, S32 blocknum = 0);
+ void getS8Fast( const char *block, const char *var, S8 &data, S32 blocknum = 0);
+ void getS8( const char *block, const char *var, S8 &data, S32 blocknum = 0);
+ void getU8Fast( const char *block, const char *var, U8 &data, S32 blocknum = 0);
+ void getU8( const char *block, const char *var, U8 &data, S32 blocknum = 0);
+ void getS16Fast( const char *block, const char *var, S16 &data, S32 blocknum = 0);
+ void getS16( const char *block, const char *var, S16 &data, S32 blocknum = 0);
+ void getU16Fast( const char *block, const char *var, U16 &data, S32 blocknum = 0);
+ void getU16( const char *block, const char *var, U16 &data, S32 blocknum = 0);
+ void getS32Fast( const char *block, const char *var, S32 &data, S32 blocknum = 0);
+ void getS32( const char *block, const char *var, S32 &data, S32 blocknum = 0);
+ void getF32Fast( const char *block, const char *var, F32 &data, S32 blocknum = 0);
+ void getF32( const char *block, const char *var, F32 &data, S32 blocknum = 0);
+ virtual void getU32Fast( const char *block, const char *var, U32 &data, S32 blocknum = 0);
+ void getU32( const char *block, const char *var, U32 &data, S32 blocknum = 0);
+ virtual void getU64Fast( const char *block, const char *var, U64 &data, S32 blocknum = 0);
+ void getU64( const char *block, const char *var, U64 &data, S32 blocknum = 0);
+ void getF64Fast( const char *block, const char *var, F64 &data, S32 blocknum = 0);
+ void getF64( const char *block, const char *var, F64 &data, S32 blocknum = 0);
+ void getVector3Fast( const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0);
+ void getVector3( const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0);
+ void getVector4Fast( const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0);
+ void getVector4( const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0);
+ void getVector3dFast(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0);
+ void getVector3d(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0);
+ void getQuatFast( const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0);
+ void getQuat( const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0);
+ virtual void getUUIDFast( const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0);
+ void getUUID( const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0);
+ virtual void getIPAddrFast( const char *block, const char *var, U32 &ip, S32 blocknum = 0);
+ void getIPAddr( const char *block, const char *var, U32 &ip, S32 blocknum = 0);
+ virtual void getIPPortFast( const char *block, const char *var, U16 &port, S32 blocknum = 0);
+ void getIPPort( const char *block, const char *var, U16 &port, S32 blocknum = 0);
+ virtual void getStringFast( const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0);
+ void getString( const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0);
+
+
+ // Utility functions to generate a replay-resistant digest check
+ // against the shared secret. The window specifies how much of a
+ // time window is allowed - 1 second is good for tight
+ // connections, but multi-process windows might want to be upwards
+ // of 5 seconds. For generateDigest, you want to pass in a
+ // character array of at least MD5HEX_STR_SIZE so that the hex
+ // digest and null termination will fit.
+ bool generateDigestForNumberAndUUIDs(char* digest, const U32 number, const LLUUID &id1, const LLUUID &id2) const;
+ bool generateDigestForWindowAndUUIDs(char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const;
+ bool isMatchingDigestForWindowAndUUIDs(const char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const;
+
+ bool generateDigestForNumber(char* digest, const U32 number) const;
+ bool generateDigestForWindow(char* digest, const S32 window) const;
+ bool isMatchingDigestForWindow(const char* digest, const S32 window) const;
+
+ void showCircuitInfo();
+ LLString getCircuitInfoString();
+
+ virtual U32 getOurCircuitCode();
+
+ void enableCircuit(const LLHost &host, BOOL trusted);
+ void disableCircuit(const LLHost &host);
+
+ // Use this to establish trust on startup and in response to
+ // DenyTrustedCircuit.
+ void sendCreateTrustedCircuit(const LLHost& host, const LLUUID & id1, const LLUUID & id2);
+
+ // Use this to inform a peer that they aren't currently trusted...
+ // This now enqueues the request so that we can ensure that we only send
+ // one deny per circuit per message loop so that this doesn't become a DoS.
+ // The actual sending is done by reallySendDenyTrustedCircuit()
+ void sendDenyTrustedCircuit(const LLHost &host);
+
+private:
+ // A list of the circuits that need to be sent DenyTrustedCircuit messages.
+ typedef std::set<LLHost> host_set_t;
+ host_set_t mDenyTrustedCircuitSet;
+
+ // Really sends the DenyTrustedCircuit message to a given host
+ // related to sendDenyTrustedCircuit()
+ void reallySendDenyTrustedCircuit(const LLHost &host);
+
+
+public:
+ // Use this to establish trust to and from a host. This blocks
+ // until trust has been established, and probably should only be
+ // used on startup.
+ void establishBidirectionalTrust(const LLHost &host, S64 frame_count = 0);
+
+ // returns whether the given host is on a trusted circuit
+ BOOL getCircuitTrust(const LLHost &host);
+
+ void setCircuitAllowTimeout(const LLHost &host, BOOL allow);
+ void setCircuitTimeoutCallback(const LLHost &host, void (*callback_func)(const LLHost &host, void *user_data), void *user_data);
+
+ BOOL checkCircuitBlocked(const U32 circuit);
+ BOOL checkCircuitAlive(const U32 circuit);
+ BOOL checkCircuitAlive(const LLHost &host);
+ void setCircuitProtection(BOOL b_protect);
+ U32 findCircuitCode(const LLHost &host);
+ LLHost findHost(const U32 circuit_code);
+ void sanityCheck();
+
+ S32 getNumberOfBlocksFast(const char *blockname);
+ S32 getNumberOfBlocks(const char *blockname)
+ {
+ return getNumberOfBlocksFast(gMessageStringTable.getString(blockname));
+ }
+ S32 getSizeFast(const char *blockname, const char *varname);
+ S32 getSize(const char *blockname, const char *varname)
+ {
+ return getSizeFast(gMessageStringTable.getString(blockname), gMessageStringTable.getString(varname));
+ }
+ S32 getSizeFast(const char *blockname, S32 blocknum, const char *varname); // size in bytes of variable length data
+ S32 getSize(const char *blockname, S32 blocknum, const char *varname)
+ {
+ return getSizeFast(gMessageStringTable.getString(blockname), blocknum, gMessageStringTable.getString(varname));
+ }
+
+ void resetReceiveCounts(); // resets receive counts for all message types to 0
+ void dumpReceiveCounts(); // dumps receive count for each message type to llinfos
+ void dumpCircuitInfo(); // Circuit information to llinfos
+
+ BOOL isClear() const; // returns mbSClear;
+ S32 flush(const LLHost &host);
+
+ U32 getListenPort( void ) const;
+
+ void startLogging(); // start verbose logging
+ void stopLogging(); // flush and close file
+ void summarizeLogs(std::ostream& str); // log statistics
+
+ S32 getReceiveSize() const { return mReceiveSize; }
+ S32 getReceiveCompressedSize() const { return mIncomingCompressedSize; }
+ S32 getReceiveBytes() const;
+
+ S32 getUnackedListSize() const { return mUnackedListSize; }
+
+ const char* getCurrentSMessageName() const { return mCurrentSMessageName; }
+ const char* getCurrentSBlockName() const { return mCurrentSBlockName; }
+
+ // friends
+ friend std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg);
+
+ void setMaxMessageTime(const F32 seconds); // Max time to process messages before warning and dumping (neg to disable)
+ void setMaxMessageCounts(const S32 num); // Max number of messages before dumping (neg to disable)
+
+ // statics
+public:
+ static U64 getMessageTimeUsecs(const BOOL update = FALSE); // Get the current message system time in microseconds
+ static F64 getMessageTimeSeconds(const BOOL update = FALSE); // Get the current message system time in seconds
+
+ static void setTimeDecodes( BOOL b )
+ { LLMessageSystem::mTimeDecodes = b; }
+
+ static void setTimeDecodesSpamThreshold( F32 seconds )
+ { LLMessageSystem::mTimeDecodesSpamThreshold = seconds; }
+
+ // message handlers internal to the message systesm
+ //static void processAssignCircuitCode(LLMessageSystem* msg, void**);
+ static void processAddCircuitCode(LLMessageSystem* msg, void**);
+ static void processUseCircuitCode(LLMessageSystem* msg, void**);
+
+ void setMessageBans(const LLSD& trusted, const LLSD& untrusted);
+
+private:
+ // data used in those internal handlers
+
+ // The mCircuitCodes is a map from circuit codes to session
+ // ids. This allows us to verify sessions on connect.
+ typedef std::map<U32, LLUUID> code_session_map_t;
+ code_session_map_t mCircuitCodes;
+
+ // Viewers need to track a process session in order to make sure
+ // that no one gives them a bad circuit code.
+ LLUUID mSessionID;
+
+private:
+ void addTemplate(LLMessageTemplate *templatep);
+ void clearReceiveState();
+ BOOL decodeTemplate( const U8* buffer, S32 buffer_size, LLMessageTemplate** msg_template );
+
+ void logMsgFromInvalidCircuit( const LLHost& sender, BOOL recv_reliable );
+ void logTrustedMsgFromUntrustedCircuit( const LLHost& sender );
+ void logValidMsg(LLCircuitData *cdp, const LLHost& sender, BOOL recv_reliable, BOOL recv_resent, BOOL recv_acks );
+ void logRanOffEndOfPacket( const LLHost& sender );
+
+private:
+ class LLMessageCountInfo
+ {
+ public:
+ U32 mMessageNum;
+ U32 mMessageBytes;
+ BOOL mInvalid;
+ };
+
+ LLMessagePollInfo *mPollInfop;
+
+ U8 mEncodedRecvBuffer[MAX_BUFFER_SIZE];
+ U8 mTrueReceiveBuffer[MAX_BUFFER_SIZE];
+ S32 mTrueReceiveSize;
+
+ // Must be valid during decode
+ S32 mReceiveSize;
+ TPACKETID mCurrentRecvPacketID; // packet ID of current receive packet (for reporting)
+ LLMessageTemplate *mCurrentRMessageTemplate;
+ LLMsgData *mCurrentRMessageData;
+ S32 mIncomingCompressedSize; // original size of compressed msg (0 if uncomp.)
+ LLHost mLastSender;
+
+ // send message storage
+ LLMsgData *mCurrentSMessageData;
+ LLMessageTemplate *mCurrentSMessageTemplate;
+ LLMsgBlkData *mCurrentSDataBlock;
+ char *mCurrentSMessageName;
+ char *mCurrentSBlockName;
+
+ BOOL mbError;
+ S32 mErrorCode;
+
+ BOOL mbSBuilt; // is send message built?
+ BOOL mbSClear; // is the send message clear?
+
+ F64 mResendDumpTime; // The last time we dumped resends
+
+ LLMessageCountInfo mMessageCountList[MAX_MESSAGE_COUNT_NUM];
+ S32 mNumMessageCounts;
+ F32 mReceiveTime;
+ F32 mMaxMessageTime; // Max number of seconds for processing messages
+ S32 mMaxMessageCounts; // Max number of messages to process before dumping.
+ F64 mMessageCountTime;
+
+ F64 mCurrentMessageTimeSeconds; // The current "message system time" (updated the first call to checkMessages after a resetReceiveCount
+
+ // message system exceptions
+ typedef std::pair<msg_exception_callback, void*> exception_t;
+ typedef std::map<EMessageException, exception_t> callbacks_t;
+ callbacks_t mExceptionCallbacks;
+
+ // stuff for logging
+ LLTimer mMessageSystemTimer;
+
+ static F32 mTimeDecodesSpamThreshold; // If mTimeDecodes is on, all this many seconds for each msg decode before spamming
+ static BOOL mTimeDecodes; // Measure time for all message decodes if TRUE;
+
+ void LLMessageSystem::init(); // ctor shared initialisation.
+};
+
+
+// external hook into messaging system
+extern LLMessageSystem *gMessageSystem;
+//extern const char* MESSAGE_LOG_FILENAME;
+
+void encrypt_template(const char *src_name, const char *dest_name);
+BOOL decrypt_template(const char *src_name, const char *dest_name);
+
+// Must specific overall system version, which is used to determine
+// if a patch is available in the message template checksum verification.
+// Return TRUE if able to initialize system.
+BOOL start_messaging_system(
+ const std::string& template_name,
+ U32 port,
+ S32 version_major,
+ S32 version_minor,
+ S32 version_patch,
+ BOOL b_dump_prehash_file,
+ const std::string& secret);
+
+void end_messaging_system();
+
+void null_message_callback(LLMessageSystem *msg, void **data);
+void process_log_control(LLMessageSystem* msg, void**);
+
+//
+// Inlines
+//
+
+static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, size_t n)
+{
+ char *s = (char *)vs;
+ const char *ct = (const char *)vct;
+#ifdef LL_BIG_ENDIAN
+ S32 i, length;
+#endif
+ switch(type)
+ {
+ case MVT_FIXED:
+ case MVT_VARIABLE:
+ case MVT_U8:
+ case MVT_S8:
+ case MVT_BOOL:
+ case MVT_LLUUID:
+ case MVT_IP_ADDR: // these two are swizzled in the getters and setters
+ case MVT_IP_PORT: // these two are swizzled in the getters and setters
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+
+ case MVT_U16:
+ case MVT_S16:
+ if (n != 2)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ *(s + 1) = *(ct);
+ *(s) = *(ct + 1);
+ return(vs);
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U32:
+ case MVT_S32:
+ case MVT_F32:
+ if (n != 4)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ *(s + 3) = *(ct);
+ *(s + 2) = *(ct + 1);
+ *(s + 1) = *(ct + 2);
+ *(s) = *(ct + 3);
+ return(vs);
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U64:
+ case MVT_S64:
+ case MVT_F64:
+ if (n != 8)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ *(s + 7) = *(ct);
+ *(s + 6) = *(ct + 1);
+ *(s + 5) = *(ct + 2);
+ *(s + 4) = *(ct + 3);
+ *(s + 3) = *(ct + 4);
+ *(s + 2) = *(ct + 5);
+ *(s + 1) = *(ct + 6);
+ *(s) = *(ct + 7);
+ return(vs);
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_LLVector3:
+ case MVT_LLQuaternion: // We only send x, y, z and infer w (we set x, y, z to ensure that w >= 0)
+ if (n != 12)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 8, ct + 8, MVT_F32, 4);
+ htonmemcpy(s + 4, ct + 4, MVT_F32, 4);
+ return(htonmemcpy(s, ct, MVT_F32, 4));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_LLVector3d:
+ if (n != 24)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 16, ct + 16, MVT_F64, 8);
+ htonmemcpy(s + 8, ct + 8, MVT_F64, 8);
+ return(htonmemcpy(s, ct, MVT_F64, 8));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_LLVector4:
+ if (n != 16)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 12, ct + 12, MVT_F32, 4);
+ htonmemcpy(s + 8, ct + 8, MVT_F32, 4);
+ htonmemcpy(s + 4, ct + 4, MVT_F32, 4);
+ return(htonmemcpy(s, ct, MVT_F32, 4));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U16Vec3:
+ if (n != 6)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 4, ct + 4, MVT_U16, 2);
+ htonmemcpy(s + 2, ct + 2, MVT_U16, 2);
+ return(htonmemcpy(s, ct, MVT_U16, 2));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U16Quat:
+ if (n != 8)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 6, ct + 6, MVT_U16, 2);
+ htonmemcpy(s + 4, ct + 4, MVT_U16, 2);
+ htonmemcpy(s + 2, ct + 2, MVT_U16, 2);
+ return(htonmemcpy(s, ct, MVT_U16, 2));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_S16Array:
+ if (n % 2)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ length = n % 2;
+ for (i = 1; i < length; i++)
+ {
+ htonmemcpy(s + i*2, ct + i*2, MVT_S16, 2);
+ }
+ return(htonmemcpy(s, ct, MVT_S16, 2));
+#else
+ return(memcpy(s,ct,n));
+#endif
+
+ default:
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+ }
+}
+
+inline void *ntohmemcpy(void *s, const void *ct, EMsgVariableType type, size_t n)
+{
+ return(htonmemcpy(s,ct,type, n));
+}
+
+
+inline const LLHost& LLMessageSystem::getSender() const
+{
+ return mLastSender;
+}
+
+inline U32 LLMessageSystem::getSenderIP() const
+{
+ return mLastSender.getAddress();
+}
+
+inline U32 LLMessageSystem::getSenderPort() const
+{
+ return mLastSender.getPort();
+}
+
+inline void LLMessageSystem::addS8Fast(const char *varname, S8 s)
+{
+ addDataFast(varname, &s, MVT_S8, sizeof(s));
+}
+
+inline void LLMessageSystem::addS8(const char *varname, S8 s)
+{
+ addDataFast(gMessageStringTable.getString(varname), &s, MVT_S8, sizeof(s));
+}
+
+inline void LLMessageSystem::addU8Fast(const char *varname, U8 u)
+{
+ addDataFast(varname, &u, MVT_U8, sizeof(u));
+}
+
+inline void LLMessageSystem::addU8(const char *varname, U8 u)
+{
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_U8, sizeof(u));
+}
+
+inline void LLMessageSystem::addS16Fast(const char *varname, S16 i)
+{
+ addDataFast(varname, &i, MVT_S16, sizeof(i));
+}
+
+inline void LLMessageSystem::addS16(const char *varname, S16 i)
+{
+ addDataFast(gMessageStringTable.getString(varname), &i, MVT_S16, sizeof(i));
+}
+
+inline void LLMessageSystem::addU16Fast(const char *varname, U16 i)
+{
+ addDataFast(varname, &i, MVT_U16, sizeof(i));
+}
+
+inline void LLMessageSystem::addU16(const char *varname, U16 i)
+{
+ addDataFast(gMessageStringTable.getString(varname), &i, MVT_U16, sizeof(i));
+}
+
+inline void LLMessageSystem::addF32Fast(const char *varname, F32 f)
+{
+ addDataFast(varname, &f, MVT_F32, sizeof(f));
+}
+
+inline void LLMessageSystem::addF32(const char *varname, F32 f)
+{
+ addDataFast(gMessageStringTable.getString(varname), &f, MVT_F32, sizeof(f));
+}
+
+inline void LLMessageSystem::addS32Fast(const char *varname, S32 s)
+{
+ addDataFast(varname, &s, MVT_S32, sizeof(s));
+}
+
+inline void LLMessageSystem::addS32(const char *varname, S32 s)
+{
+ addDataFast(gMessageStringTable.getString(varname), &s, MVT_S32, sizeof(s));
+}
+
+inline void LLMessageSystem::addU32Fast(const char *varname, U32 u)
+{
+ addDataFast(varname, &u, MVT_U32, sizeof(u));
+}
+
+inline void LLMessageSystem::addU32(const char *varname, U32 u)
+{
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_U32, sizeof(u));
+}
+
+inline void LLMessageSystem::addU64Fast(const char *varname, U64 lu)
+{
+ addDataFast(varname, &lu, MVT_U64, sizeof(lu));
+}
+
+inline void LLMessageSystem::addU64(const char *varname, U64 lu)
+{
+ addDataFast(gMessageStringTable.getString(varname), &lu, MVT_U64, sizeof(lu));
+}
+
+inline void LLMessageSystem::addF64Fast(const char *varname, F64 d)
+{
+ addDataFast(varname, &d, MVT_F64, sizeof(d));
+}
+
+inline void LLMessageSystem::addF64(const char *varname, F64 d)
+{
+ addDataFast(gMessageStringTable.getString(varname), &d, MVT_F64, sizeof(d));
+}
+
+inline void LLMessageSystem::addIPAddrFast(const char *varname, U32 u)
+{
+ addDataFast(varname, &u, MVT_IP_ADDR, sizeof(u));
+}
+
+inline void LLMessageSystem::addIPAddr(const char *varname, U32 u)
+{
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_IP_ADDR, sizeof(u));
+}
+
+inline void LLMessageSystem::addIPPortFast(const char *varname, U16 u)
+{
+ u = htons(u);
+ addDataFast(varname, &u, MVT_IP_PORT, sizeof(u));
+}
+
+inline void LLMessageSystem::addIPPort(const char *varname, U16 u)
+{
+ u = htons(u);
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_IP_PORT, sizeof(u));
+}
+
+inline void LLMessageSystem::addBOOLFast(const char* varname, BOOL b)
+{
+ // Can't just cast a BOOL (actually a U32) to a U8.
+ // In some cases the low order bits will be zero.
+ U8 temp = (b != 0);
+ addDataFast(varname, &temp, MVT_BOOL, sizeof(temp));
+}
+
+inline void LLMessageSystem::addBOOL(const char* varname, BOOL b)
+{
+ // Can't just cast a BOOL (actually a U32) to a U8.
+ // In some cases the low order bits will be zero.
+ U8 temp = (b != 0);
+ addDataFast(gMessageStringTable.getString(varname), &temp, MVT_BOOL, sizeof(temp));
+}
+
+inline void LLMessageSystem::addStringFast(const char* varname, const char* s)
+{
+ if (s)
+ addDataFast( varname, (void *)s, MVT_VARIABLE, (S32)strlen(s) + 1); /* Flawfinder: ignore */
+ else
+ addDataFast( varname, NULL, MVT_VARIABLE, 0);
+}
+
+inline void LLMessageSystem::addString(const char* varname, const char* s)
+{
+ if (s)
+ addDataFast( gMessageStringTable.getString(varname), (void *)s, MVT_VARIABLE, (S32)strlen(s) + 1); /* Flawfinder: ignore */
+ else
+ addDataFast( gMessageStringTable.getString(varname), NULL, MVT_VARIABLE, 0);
+}
+
+inline void LLMessageSystem::addStringFast(const char* varname, const std::string& s)
+{
+ if (s.size())
+ addDataFast( varname, (void *)s.c_str(), MVT_VARIABLE, (S32)(s.size()) + 1);
+ else
+ addDataFast( varname, NULL, MVT_VARIABLE, 0);
+}
+
+inline void LLMessageSystem::addString(const char* varname, const std::string& s)
+{
+ if (s.size())
+ addDataFast( gMessageStringTable.getString(varname), (void *)s.c_str(), MVT_VARIABLE, (S32)(s.size()) + 1);
+ else
+ addDataFast( gMessageStringTable.getString(varname), NULL, MVT_VARIABLE, 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Retrieval aliases
+//-----------------------------------------------------------------------------
+inline void LLMessageSystem::getS8Fast(const char *block, const char *var, S8 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(S8), blocknum);
+}
+
+inline void LLMessageSystem::getS8(const char *block, const char *var, S8 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(S8), blocknum);
+}
+
+inline void LLMessageSystem::getU8Fast(const char *block, const char *var, U8 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(U8), blocknum);
+}
+
+inline void LLMessageSystem::getU8(const char *block, const char *var, U8 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(U8), blocknum);
+}
+
+inline void LLMessageSystem::getBOOLFast(const char *block, const char *var, BOOL &b, S32 blocknum )
+{
+ U8 value;
+ getDataFast(block, var, &value, sizeof(U8), blocknum);
+ b = (BOOL) value;
+}
+
+inline void LLMessageSystem::getBOOL(const char *block, const char *var, BOOL &b, S32 blocknum )
+{
+ U8 value;
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &value, sizeof(U8), blocknum);
+ b = (BOOL) value;
+}
+
+inline void LLMessageSystem::getS16Fast(const char *block, const char *var, S16 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(S16), blocknum);
+}
+
+inline void LLMessageSystem::getS16(const char *block, const char *var, S16 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(S16), blocknum);
+}
+
+inline void LLMessageSystem::getU16Fast(const char *block, const char *var, U16 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(U16), blocknum);
+}
+
+inline void LLMessageSystem::getU16(const char *block, const char *var, U16 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(U16), blocknum);
+}
+
+inline void LLMessageSystem::getS32Fast(const char *block, const char *var, S32 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(S32), blocknum);
+}
+
+inline void LLMessageSystem::getS32(const char *block, const char *var, S32 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(S32), blocknum);
+}
+
+inline void LLMessageSystem::getU32Fast(const char *block, const char *var, U32 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getU32(const char *block, const char *var, U32 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getU64Fast(const char *block, const char *var, U64 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(U64), blocknum);
+}
+
+inline void LLMessageSystem::getU64(const char *block, const char *var, U64 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(U64), blocknum);
+}
+
+
+inline void LLMessageSystem::getIPAddrFast(const char *block, const char *var, U32 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getIPAddr(const char *block, const char *var, U32 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getIPPortFast(const char *block, const char *var, U16 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(U16), blocknum);
+ u = ntohs(u);
+}
+
+inline void LLMessageSystem::getIPPort(const char *block, const char *var, U16 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(U16), blocknum);
+ u = ntohs(u);
+}
+
+
+inline void LLMessageSystem::getStringFast(const char *block, const char *var, S32 buffer_size, char *s, S32 blocknum )
+{
+ s[0] = '\0';
+ getDataFast(block, var, s, 0, blocknum, buffer_size);
+ s[buffer_size - 1] = '\0';
+}
+
+inline void LLMessageSystem::getString(const char *block, const char *var, S32 buffer_size, char *s, S32 blocknum )
+{
+ s[0] = '\0';
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), s, 0, blocknum, buffer_size);
+ s[buffer_size - 1] = '\0';
+}
+
+//-----------------------------------------------------------------------------
+// Transmission aliases
+//-----------------------------------------------------------------------------
+//inline S32 LLMessageSystem::sendMessage(U32 ip, U32 port, BOOL zero_code)
+//{
+// return sendMessage(LLHost(ip, port), zero_code);
+//}
+
+//inline S32 LLMessageSystem::sendMessage(const char *ip_str, U32 port, BOOL zero_code)
+//{
+// return sendMessage(LLHost(ip_str, port), zero_code);
+//}
+
+inline S32 LLMessageSystem::sendMessage(const U32 circuit)//, BOOL zero_code)
+{
+ return sendMessage(findHost(circuit));//, zero_code);
+}
+
+#endif
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
new file mode 100644
index 0000000000..18be31af58
--- /dev/null
+++ b/indra/llmessage/message_prehash.cpp
@@ -0,0 +1,2964 @@
+/**
+ * @file message_prehash.cpp
+ * @brief file of prehashed variables
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Generated from message template version number 1.053
+ */
+#include "linden_common.h"
+#include "message.h"
+
+
+
+F32 gPrehashVersionNumber = 1.053f;
+
+char * _PREHASH_X;
+char * _PREHASH_Y;
+char * _PREHASH_Z;
+char * _PREHASH_AddFlags;
+char * _PREHASH_ReservedNewbie;
+char * _PREHASH_FailureInfo;
+char * _PREHASH_MapData;
+char * _PREHASH_AddItem;
+char * _PREHASH_MeanCollision;
+char * _PREHASH_RezScript;
+char * _PREHASH_AvatarSitResponse;
+char * _PREHASH_InventoryAssetResponse;
+char * _PREHASH_KillObject;
+char * _PREHASH_ProposalID;
+char * _PREHASH_SerialNum;
+char * _PREHASH_Duration;
+char * _PREHASH_ScriptQuestion;
+char * _PREHASH_AddCircuitCode;
+char * _PREHASH_UseCircuitCode;
+char * _PREHASH_ViewerCircuitCode;
+char * _PREHASH_ScriptAnswerYes;
+char * _PREHASH_PartnerID;
+char * _PREHASH_DirLandQuery;
+char * _PREHASH_TeleportStart;
+char * _PREHASH_LogMessages;
+char * _PREHASH_AboutText;
+char * _PREHASH_VisualParam;
+char * _PREHASH_GroupPrims;
+char * _PREHASH_SelectedPrims;
+char * _PREHASH_ID;
+char * _PREHASH_UUIDNameRequest;
+char * _PREHASH_UUIDGroupNameRequest;
+char * _PREHASH_MoneyTransactionsRequest;
+char * _PREHASH_GroupAccountTransactionsRequest;
+char * _PREHASH_MapNameRequest;
+char * _PREHASH_MailTaskSimRequest;
+char * _PREHASH_UpdateSimulator;
+char * _PREHASH_BillableFactor;
+char * _PREHASH_ObjectBonusFactor;
+char * _PREHASH_EnableSimulator;
+char * _PREHASH_DisableSimulator;
+char * _PREHASH_ConfirmEnableSimulator;
+char * _PREHASH_LayerType;
+char * _PREHASH_OwnerRole;
+char * _PREHASH_ParcelOverlay;
+char * _PREHASH_AdjustBalance;
+char * _PREHASH_GroupOwned;
+char * _PREHASH_IP;
+char * _PREHASH_ChatFromViewer;
+char * _PREHASH_AvgAgentsInView;
+char * _PREHASH_AgentsInView;
+char * _PREHASH_GroupTitle;
+char * _PREHASH_MapLayerReply;
+char * _PREHASH_CompoundMsgID;
+char * _PREHASH_CameraConstraint;
+char * _PREHASH_DownloadTotals;
+char * _PREHASH_GenCounter;
+char * _PREHASH_FrozenData;
+char * _PREHASH_ChildAgentDying;
+char * _PREHASH_To;
+char * _PREHASH_CopyInventoryFromNotecard;
+char * _PREHASH_RezObjectFromNotecard;
+char * _PREHASH_ParcelDirFeeCurrent;
+char * _PREHASH_SeedCapability;
+char * _PREHASH_ObjectDuplicate;
+char * _PREHASH_InventoryData;
+char * _PREHASH_ReplyData;
+char * _PREHASH_ResetList;
+char * _PREHASH_MediaID;
+char * _PREHASH_RelatedRights;
+char * _PREHASH_RedirectGridX;
+char * _PREHASH_RedirectGridY;
+char * _PREHASH_TransferID;
+char * _PREHASH_Transacted;
+char * _PREHASH_TexturesChanged;
+char * _PREHASH_UserLookAt;
+char * _PREHASH_TestBlock1;
+char * _PREHASH_SensedData;
+char * _PREHASH_UpdateBlock;
+char * _PREHASH_ClassifiedGodDelete;
+char * _PREHASH_ObjectGrabUpdate;
+char * _PREHASH_TaxDate;
+char * _PREHASH_LocationPos;
+char * _PREHASH_StartDateTime;
+char * _PREHASH_ObjectUpdateCached;
+char * _PREHASH_Packets;
+char * _PREHASH_FailureType;
+char * _PREHASH_UpdateGroupInfo;
+char * _PREHASH_ObjectPermissions;
+char * _PREHASH_RevokePermissions;
+char * _PREHASH_UpdateFlags;
+char * _PREHASH_ObjectExportSelected;
+char * _PREHASH_RezSelected;
+char * _PREHASH_AutoPilot;
+char * _PREHASH_UpdateMuteListEntry;
+char * _PREHASH_RemoveMuteListEntry;
+char * _PREHASH_SetSimStatusInDatabase;
+char * _PREHASH_SetSimPresenceInDatabase;
+char * _PREHASH_CameraProperty;
+char * _PREHASH_BrushSize;
+char * _PREHASH_StartExpungeProcess;
+char * _PREHASH_SimulatorSetMap;
+char * _PREHASH_RegionPresenceRequestByRegionID;
+char * _PREHASH_ParcelObjectOwnersReply;
+char * _PREHASH_GroupMembersReply;
+char * _PREHASH_GroupRoleMembersReply;
+char * _PREHASH_RequestRegionInfo;
+char * _PREHASH_AABBMax;
+char * _PREHASH_RequestPayPrice;
+char * _PREHASH_SimulatorPresentAtLocation;
+char * _PREHASH_AgentRequestSit;
+char * _PREHASH_AABBMin;
+char * _PREHASH_ClassifiedFlags;
+char * _PREHASH_ControlFlags;
+char * _PREHASH_TeleportRequest;
+char * _PREHASH_SpaceLocationTeleportRequest;
+char * _PREHASH_LeaderBoardRequest;
+char * _PREHASH_ScriptTeleportRequest;
+char * _PREHASH_DateUTC;
+char * _PREHASH_TaskIDs;
+char * _PREHASH_EstateCovenantRequest;
+char * _PREHASH_RequestResult;
+char * _PREHASH_ReputationAgentAssign;
+char * _PREHASH_CanAcceptAgents;
+char * _PREHASH_ObjectSaleInfo;
+char * _PREHASH_KillChildAgents;
+char * _PREHASH_Balance;
+char * _PREHASH_DerezContainer;
+char * _PREHASH_ObjectData;
+char * _PREHASH_CameraAtAxis;
+char * _PREHASH_InfoBlock;
+char * _PREHASH_OwnershipCost;
+char * _PREHASH_AvatarNotesUpdate;
+char * _PREHASH_PID;
+char * _PREHASH_TimeString;
+char * _PREHASH_DirPopularReply;
+char * _PREHASH_TerrainHeightRange00;
+char * _PREHASH_SimData;
+char * _PREHASH_TerrainHeightRange01;
+char * _PREHASH_TerrainHeightRange10;
+char * _PREHASH_TerrainHeightRange11;
+char * _PREHASH_UpdateInventoryItem;
+char * _PREHASH_UpdateCreateInventoryItem;
+char * _PREHASH_MoveInventoryItem;
+char * _PREHASH_CopyInventoryItem;
+char * _PREHASH_RemoveInventoryItem;
+char * _PREHASH_CreateInventoryItem;
+char * _PREHASH_PathTwistBegin;
+char * _PREHASH_CRC;
+char * _PREHASH_AttachmentPoint;
+char * _PREHASH_TelehubBlock;
+char * _PREHASH_FOVBlock;
+char * _PREHASH_StartLocationData;
+char * _PREHASH_PositionData;
+char * _PREHASH_TimeSinceLast;
+char * _PREHASH_MapImage;
+char * _PREHASH_Objects;
+char * _PREHASH_URL;
+char * _PREHASH_CreationDate;
+char * _PREHASH_JointPivot;
+char * _PREHASH_RateeID;
+char * _PREHASH_FPS;
+char * _PREHASH_HasTelehub;
+char * _PREHASH_PathEnd;
+char * _PREHASH_ScriptDataReply;
+char * _PREHASH_MapBlockReply;
+char * _PREHASH_PropertiesData;
+char * _PREHASH_ViewerEffect;
+char * _PREHASH_FreezeUser;
+char * _PREHASH_OwnerPrims;
+char * _PREHASH_ObjectGrab;
+char * _PREHASH_ToAgentID;
+char * _PREHASH_SimulatorMapUpdate;
+char * _PREHASH_TransferPacket;
+char * _PREHASH_ObjectName;
+char * _PREHASH_GroupPowers;
+char * _PREHASH_OriginalName;
+char * _PREHASH_CompletePingCheck;
+char * _PREHASH_OnlineStatus;
+char * _PREHASH_ObjectDrop;
+char * _PREHASH_UseBigPackets;
+char * _PREHASH_GroupNoticesListReply;
+char * _PREHASH_ParcelAccessListReply;
+char * _PREHASH_RpcChannelReply;
+char * _PREHASH_RegionPresenceResponse;
+char * _PREHASH_AgentPresenceResponse;
+char * _PREHASH_CharterMember;
+char * _PREHASH_EdgeData;
+char * _PREHASH_NameData;
+char * _PREHASH_RegionPushOverride;
+char * _PREHASH_SimName;
+char * _PREHASH_UserReport;
+char * _PREHASH_DownloadPriority;
+char * _PREHASH_ToAgentId;
+char * _PREHASH_Mag;
+char * _PREHASH_DirPopularQuery;
+char * _PREHASH_ParcelPropertiesRequestByID;
+char * _PREHASH_ObjectLink;
+char * _PREHASH_RpcScriptReplyInbound;
+char * _PREHASH_BoardData;
+char * _PREHASH_RezData;
+char * _PREHASH_RemoveInventoryObjects;
+char * _PREHASH_GroupProposalBallot;
+char * _PREHASH_RPCServerIP;
+char * _PREHASH_Far;
+char * _PREHASH_GodSessionID;
+char * _PREHASH_ViewerDigest;
+char * _PREHASH_FLAboutText;
+char * _PREHASH_RegionHandshakeReply;
+char * _PREHASH_GroupActiveProposalItemReply;
+char * _PREHASH_MapItemReply;
+char * _PREHASH_Seconds;
+char * _PREHASH_UpdateUserInfo;
+char * _PREHASH_AggregatePermTexturesOwner;
+char * _PREHASH_Set;
+char * _PREHASH_NewName;
+char * _PREHASH_Key;
+char * _PREHASH_AgentID;
+char * _PREHASH_OnlineStatusRequest;
+char * _PREHASH_EventNotificationRemoveRequest;
+char * _PREHASH_NewFolderID;
+char * _PREHASH_Arc;
+char * _PREHASH_RegionX;
+char * _PREHASH_RegionY;
+char * _PREHASH_RequestData;
+char * _PREHASH_Msg;
+char * _PREHASH_Top;
+char * _PREHASH_MiscStats;
+char * _PREHASH_ImageID;
+char * _PREHASH_DataPacket;
+char * _PREHASH_ObjectDehinge;
+char * _PREHASH_You;
+char * _PREHASH_ScriptControlChange;
+char * _PREHASH_LoadURL;
+char * _PREHASH_SetCPURatio;
+char * _PREHASH_NameValueData;
+char * _PREHASH_AtomicPassObject;
+char * _PREHASH_ErrorMessage;
+char * _PREHASH_ViewerFrozenMessage;
+char * _PREHASH_HealthMessage;
+char * _PREHASH_LogTextMessage;
+char * _PREHASH_TimeDilation;
+char * _PREHASH_RemoveContribution;
+char * _PREHASH_Contribution;
+char * _PREHASH_SetGroupContribution;
+char * _PREHASH_Offline;
+char * _PREHASH_AgentIsNowWearing;
+char * _PREHASH_SecPerDay;
+char * _PREHASH_Members;
+char * _PREHASH_FailedResends;
+char * _PREHASH_CameraCenter;
+char * _PREHASH_CameraLeftAxis;
+char * _PREHASH_ExBlock;
+char * _PREHASH_Channel;
+char * _PREHASH_NetTest;
+char * _PREHASH_DiscardLevel;
+char * _PREHASH_LayerID;
+char * _PREHASH_RatorID;
+char * _PREHASH_GrabOffset;
+char * _PREHASH_SimPort;
+char * _PREHASH_PricePerMeter;
+char * _PREHASH_RegionFlags;
+char * _PREHASH_VoteResult;
+char * _PREHASH_ParcelDirFeeEstimate;
+char * _PREHASH_ModifyBlock;
+char * _PREHASH_InventoryBlock;
+char * _PREHASH_ReplyBlock;
+char * _PREHASH_ValidUntil;
+char * _PREHASH_VelocityInterpolateOn;
+char * _PREHASH_ClassifiedDelete;
+char * _PREHASH_RegionDenyAnonymous;
+char * _PREHASH_FLImageID;
+char * _PREHASH_AllowPublish;
+char * _PREHASH_SitName;
+char * _PREHASH_RegionsVisited;
+char * _PREHASH_DirClassifiedReply;
+char * _PREHASH_AvatarClassifiedReply;
+char * _PREHASH_ReputationIndividualReply;
+char * _PREHASH_MediaURL;
+char * _PREHASH_CompleteAgentMovement;
+char * _PREHASH_SpaceIP;
+char * _PREHASH_ClassifiedID;
+char * _PREHASH_LocalID;
+char * _PREHASH_RemoveItem;
+char * _PREHASH_LogFailedMoneyTransaction;
+char * _PREHASH_ViewerStartAuction;
+char * _PREHASH_StartAuction;
+char * _PREHASH_NameValueName;
+char * _PREHASH_AngVelX;
+char * _PREHASH_DuplicateFlags;
+char * _PREHASH_AngVelY;
+char * _PREHASH_AngVelZ;
+char * _PREHASH_TextColor;
+char * _PREHASH_SlaveID;
+char * _PREHASH_Charter;
+char * _PREHASH_AlertData;
+char * _PREHASH_TargetBlock;
+char * _PREHASH_CheckParcelAuctions;
+char * _PREHASH_ParcelAuctions;
+char * _PREHASH_OwnerIsGroup;
+char * _PREHASH_NameValuePair;
+char * _PREHASH_RemoveNameValuePair;
+char * _PREHASH_GetNameValuePair;
+char * _PREHASH_BulkUpdateInventory;
+char * _PREHASH_UpdateTaskInventory;
+char * _PREHASH_RemoveTaskInventory;
+char * _PREHASH_MoveTaskInventory;
+char * _PREHASH_RequestTaskInventory;
+char * _PREHASH_ReplyTaskInventory;
+char * _PREHASH_DeclineInventory;
+char * _PREHASH_AggregatePermInventory;
+char * _PREHASH_SimulatorInfo;
+char * _PREHASH_MoneyTransactionsReply;
+char * _PREHASH_GroupAccountTransactionsReply;
+char * _PREHASH_MailTaskSimReply;
+char * _PREHASH_WearableData;
+char * _PREHASH_StatisticsData;
+char * _PREHASH_Enabled;
+char * _PREHASH_Savings;
+char * _PREHASH_SimulatorLoad;
+char * _PREHASH_InternalRegionIP;
+char * _PREHASH_ExternalRegionIP;
+char * _PREHASH_TotalPairs;
+char * _PREHASH_CreateGroupRequest;
+char * _PREHASH_JoinGroupRequest;
+char * _PREHASH_LeaveGroupRequest;
+char * _PREHASH_InviteGroupRequest;
+char * _PREHASH_LiveHelpGroupRequest;
+char * _PREHASH_ServerVersion;
+char * _PREHASH_PriceParcelClaimFactor;
+char * _PREHASH_BillableArea;
+char * _PREHASH_ObjectID;
+char * _PREHASH_ObjectFlagUpdate;
+char * _PREHASH_GroupRoleUpdate;
+char * _PREHASH_RequestInventoryAsset;
+char * _PREHASH_RedoLand;
+char * _PREHASH_TravelAccess;
+char * _PREHASH_ChangedGrid;
+char * _PREHASH_AgentDropGroup;
+char * _PREHASH_Details;
+char * _PREHASH_LocationX;
+char * _PREHASH_SaleType;
+char * _PREHASH_LocationY;
+char * _PREHASH_LocationZ;
+char * _PREHASH_EconomyData;
+char * _PREHASH_HeadRotation;
+char * _PREHASH_DeleteOnCompletion;
+char * _PREHASH_PublicPort;
+char * _PREHASH_DirClassifiedQuery;
+char * _PREHASH_CallbackID;
+char * _PREHASH_RequestParcelTransfer;
+char * _PREHASH_RoleCount;
+char * _PREHASH_ObjectCapacity;
+char * _PREHASH_RequestID;
+char * _PREHASH_RequestXfer;
+char * _PREHASH_ObjectTaxCurrent;
+char * _PREHASH_LightTaxCurrent;
+char * _PREHASH_LandTaxCurrent;
+char * _PREHASH_GroupTaxCurrent;
+char * _PREHASH_FetchInventoryDescendents;
+char * _PREHASH_InventoryDescendents;
+char * _PREHASH_Descendents;
+char * _PREHASH_PurgeInventoryDescendents;
+char * _PREHASH_ShowDir;
+char * _PREHASH_IsOwner;
+char * _PREHASH_Timestamp;
+char * _PREHASH_GlobalPos;
+char * _PREHASH_GrabOffsetInitial;
+char * _PREHASH_IsTrial;
+char * _PREHASH_FinalizeLogout;
+char * _PREHASH_ObjectDuplicateOnRay;
+char * _PREHASH_GroupMembershipCount;
+char * _PREHASH_MethodData;
+char * _PREHASH_ActivateGestures;
+char * _PREHASH_DeactivateGestures;
+char * _PREHASH_ProposalData;
+char * _PREHASH_PosGlobal;
+char * _PREHASH_SearchID;
+char * _PREHASH_RezMultipleAttachmentsFromInv;
+char * _PREHASH_SearchName;
+char * _PREHASH_VersionString;
+char * _PREHASH_CreateGroupReply;
+char * _PREHASH_LeaveGroupReply;
+char * _PREHASH_ActualArea;
+char * _PREHASH_Message;
+char * _PREHASH_ClickAction;
+char * _PREHASH_AssetUploadComplete;
+char * _PREHASH_RequestType;
+char * _PREHASH_UUID;
+char * _PREHASH_BaseMask;
+char * _PREHASH_NetBlock;
+char * _PREHASH_GlobalX;
+char * _PREHASH_GlobalY;
+char * _PREHASH_CopyRotates;
+char * _PREHASH_KickUserAck;
+char * _PREHASH_TopPick;
+char * _PREHASH_SessionID;
+char * _PREHASH_GlobalZ;
+char * _PREHASH_DeclineFriendship;
+char * _PREHASH_FormFriendship;
+char * _PREHASH_TerminateFriendship;
+char * _PREHASH_TaskData;
+char * _PREHASH_SimWideMaxPrims;
+char * _PREHASH_TotalPrims;
+char * _PREHASH_SourceFilename;
+char * _PREHASH_ProfileBegin;
+char * _PREHASH_MoneyDetailsRequest;
+char * _PREHASH_Request;
+char * _PREHASH_GroupAccountDetailsRequest;
+char * _PREHASH_GroupActiveProposalsRequest;
+char * _PREHASH_StringValue;
+char * _PREHASH_ClosestSimulator;
+char * _PREHASH_Version;
+char * _PREHASH_OtherCount;
+char * _PREHASH_MemberCount;
+char * _PREHASH_ChatData;
+char * _PREHASH_IsGroupOwned;
+char * _PREHASH_EnergyEfficiency;
+char * _PREHASH_MaxPlace;
+char * _PREHASH_PickInfoUpdate;
+char * _PREHASH_PickDelete;
+char * _PREHASH_ScriptReset;
+char * _PREHASH_Requester;
+char * _PREHASH_ForSale;
+char * _PREHASH_NearestLandingRegionReply;
+char * _PREHASH_RecordAgentPresence;
+char * _PREHASH_EraseAgentPresence;
+char * _PREHASH_ParcelID;
+char * _PREHASH_Godlike;
+char * _PREHASH_TotalDebits;
+char * _PREHASH_Direction;
+char * _PREHASH_Appearance;
+char * _PREHASH_HealthData;
+char * _PREHASH_LeftAxis;
+char * _PREHASH_LocationBlock;
+char * _PREHASH_ObjectImage;
+char * _PREHASH_TerrainStartHeight00;
+char * _PREHASH_TerrainStartHeight01;
+char * _PREHASH_TerrainStartHeight10;
+char * _PREHASH_ObjectHinge;
+char * _PREHASH_TerrainStartHeight11;
+char * _PREHASH_MetersPerGrid;
+char * _PREHASH_WaterHeight;
+char * _PREHASH_FetchInventoryReply;
+char * _PREHASH_MoneySummaryReply;
+char * _PREHASH_GroupAccountSummaryReply;
+char * _PREHASH_AttachedSound;
+char * _PREHASH_ParamInUse;
+char * _PREHASH_GodKickUser;
+char * _PREHASH_PickName;
+char * _PREHASH_TaskName;
+char * _PREHASH_ParcelGodReserveForNewbie;
+char * _PREHASH_SubType;
+char * _PREHASH_ObjectCount;
+char * _PREHASH_RegionPresenceRequestByHandle;
+char * _PREHASH_RezSingleAttachmentFromInv;
+char * _PREHASH_ChildAgentUpdate;
+char * _PREHASH_ToID;
+char * _PREHASH_ViewerPort;
+char * _PREHASH_IsOwnerGroup;
+char * _PREHASH_AgentHeightWidth;
+char * _PREHASH_VerticalAngle;
+char * _PREHASH_WearableType;
+char * _PREHASH_AggregatePermNextOwner;
+char * _PREHASH_ShowInList;
+char * _PREHASH_PositionSuggestion;
+char * _PREHASH_UpdateParcel;
+char * _PREHASH_ClearAgentSessions;
+char * _PREHASH_SetAlwaysRun;
+char * _PREHASH_NVPair;
+char * _PREHASH_ObjectSpinStart;
+char * _PREHASH_UseEstateSun;
+char * _PREHASH_LogoutBlock;
+char * _PREHASH_RelayLogControl;
+char * _PREHASH_RegionID;
+char * _PREHASH_Creator;
+char * _PREHASH_ProposalText;
+char * _PREHASH_DirEventsReply;
+char * _PREHASH_EventInfoReply;
+char * _PREHASH_UserInfoReply;
+char * _PREHASH_PathRadiusOffset;
+char * _PREHASH_SessionInfo;
+char * _PREHASH_TextureData;
+char * _PREHASH_ChatPass;
+char * _PREHASH_TargetID;
+char * _PREHASH_DefaultPayPrice;
+char * _PREHASH_UserLocation;
+char * _PREHASH_MaxPrims;
+char * _PREHASH_RegionIP;
+char * _PREHASH_LandmarkID;
+char * _PREHASH_InitiateDownload;
+char * _PREHASH_Name;
+char * _PREHASH_OtherCleanTime;
+char * _PREHASH_ParcelSetOtherCleanTime;
+char * _PREHASH_TeleportPriceExponent;
+char * _PREHASH_Gain;
+char * _PREHASH_VelX;
+char * _PREHASH_PacketAck;
+char * _PREHASH_PathSkew;
+char * _PREHASH_Negative;
+char * _PREHASH_VelY;
+char * _PREHASH_SimulatorShutdownRequest;
+char * _PREHASH_NearestLandingRegionRequest;
+char * _PREHASH_VelZ;
+char * _PREHASH_OtherID;
+char * _PREHASH_MemberID;
+char * _PREHASH_MapLayerRequest;
+char * _PREHASH_PatchVersion;
+char * _PREHASH_ObjectScale;
+char * _PREHASH_TargetIP;
+char * _PREHASH_Redo;
+char * _PREHASH_MoneyBalance;
+char * _PREHASH_TrackAgent;
+char * _PREHASH_MaxX;
+char * _PREHASH_Data;
+char * _PREHASH_MaxY;
+char * _PREHASH_TextureAnim;
+char * _PREHASH_ReturnIDs;
+char * _PREHASH_Date;
+char * _PREHASH_GestureUpdate;
+char * _PREHASH_AgentWearablesUpdate;
+char * _PREHASH_AgentDataUpdate;
+char * _PREHASH_Hash;
+char * _PREHASH_GroupDataUpdate;
+char * _PREHASH_AgentGroupDataUpdate;
+char * _PREHASH_Left;
+char * _PREHASH_Mask;
+char * _PREHASH_ForceMouselook;
+char * _PREHASH_Success;
+char * _PREHASH_ObjectGroup;
+char * _PREHASH_SunHour;
+char * _PREHASH_MinX;
+char * _PREHASH_ScriptSensorReply;
+char * _PREHASH_MinY;
+char * _PREHASH_Command;
+char * _PREHASH_Desc;
+char * _PREHASH_AttachmentNeedsSave;
+char * _PREHASH_HistoryItemData;
+char * _PREHASH_AgentCachedTexture;
+char * _PREHASH_Subject;
+char * _PREHASH_East;
+char * _PREHASH_GodExpungeUser;
+char * _PREHASH_QueryReplies;
+char * _PREHASH_ObjectCategory;
+char * _PREHASH_Time;
+char * _PREHASH_CreateLandmarkForEvent;
+char * _PREHASH_ParentID;
+char * _PREHASH_Ping;
+char * _PREHASH_Perp;
+char * _PREHASH_Code;
+char * _PREHASH_InvType;
+char * _PREHASH_AgentFOV;
+char * _PREHASH_BulkMoneyTransfer;
+char * _PREHASH_Audible;
+char * _PREHASH_AuctionData;
+char * _PREHASH_IDBlock;
+char * _PREHASH_ReputationData;
+char * _PREHASH_West;
+char * _PREHASH_Undo;
+char * _PREHASH_TotalNumItems;
+char * _PREHASH_Info;
+char * _PREHASH_Area;
+char * _PREHASH_Behavior;
+char * _PREHASH_SimCrashed;
+char * _PREHASH_Text;
+char * _PREHASH_AgentToNewRegion;
+char * _PREHASH_PriceGroupCreate;
+char * _PREHASH_ObjectShape;
+char * _PREHASH_GroupRoleDataReply;
+char * _PREHASH_PosX;
+char * _PREHASH_PosY;
+char * _PREHASH_MuteCRC;
+char * _PREHASH_PosZ;
+char * _PREHASH_Size;
+char * _PREHASH_FromAddress;
+char * _PREHASH_Body;
+char * _PREHASH_FileData;
+char * _PREHASH_List;
+char * _PREHASH_KickUser;
+char * _PREHASH_OtherPrims;
+char * _PREHASH_RunTime;
+char * _PREHASH_GrantUserRights;
+char * _PREHASH_RpcScriptRequestInboundForward;
+char * _PREHASH_More;
+char * _PREHASH_Majority;
+char * _PREHASH_MetersTraveled;
+char * _PREHASH_Stat;
+char * _PREHASH_SoundID;
+char * _PREHASH_Item;
+char * _PREHASH_User;
+char * _PREHASH_RemoteInfos;
+char * _PREHASH_Prey;
+char * _PREHASH_UsecSinceStart;
+char * _PREHASH_RayStart;
+char * _PREHASH_ParcelData;
+char * _PREHASH_CameraUpAxis;
+char * _PREHASH_ScriptDialog;
+char * _PREHASH_MasterParcelData;
+char * _PREHASH_Invalid;
+char * _PREHASH_MinPlace;
+char * _PREHASH_ProfileCurve;
+char * _PREHASH_ParcelAccessListUpdate;
+char * _PREHASH_MuteListUpdate;
+char * _PREHASH_SendPacket;
+char * _PREHASH_SendXferPacket;
+char * _PREHASH_RegionDenyIdentified;
+char * _PREHASH_NotecardItemID;
+char * _PREHASH_LastName;
+char * _PREHASH_From;
+char * _PREHASH_RoleChange;
+char * _PREHASH_Port;
+char * _PREHASH_MemberTitle;
+char * _PREHASH_LogParcelChanges;
+char * _PREHASH_AgentCachedTextureResponse;
+char * _PREHASH_DeRezObject;
+char * _PREHASH_IsTemporary;
+char * _PREHASH_InsigniaID;
+char * _PREHASH_CheckFlags;
+char * _PREHASH_TransferPriority;
+char * _PREHASH_EventID;
+char * _PREHASH_Selected;
+char * _PREHASH_FromAgentId;
+char * _PREHASH_Type;
+char * _PREHASH_ChatType;
+char * _PREHASH_ReportData;
+char * _PREHASH_LeaderBoardData;
+char * _PREHASH_RequestBlock;
+char * _PREHASH_GrantData;
+char * _PREHASH_DetachAttachmentIntoInv;
+char * _PREHASH_ParcelDisableObjects;
+char * _PREHASH_Sections;
+char * _PREHASH_GodLevel;
+char * _PREHASH_PayPriceReply;
+char * _PREHASH_QueryID;
+char * _PREHASH_CameraEyeOffset;
+char * _PREHASH_AgentPosition;
+char * _PREHASH_GrabPosition;
+char * _PREHASH_OnlineNotification;
+char * _PREHASH_OfflineNotification;
+char * _PREHASH_SendPostcard;
+char * _PREHASH_RequestFlags;
+char * _PREHASH_MoneyHistoryRequest;
+char * _PREHASH_MoneySummaryRequest;
+char * _PREHASH_GroupAccountSummaryRequest;
+char * _PREHASH_GroupVoteHistoryRequest;
+char * _PREHASH_ParamValue;
+char * _PREHASH_Checksum;
+char * _PREHASH_MaxAgents;
+char * _PREHASH_CreateNewOutfitAttachments;
+char * _PREHASH_RegionHandle;
+char * _PREHASH_TeleportProgress;
+char * _PREHASH_AgentQuitCopy;
+char * _PREHASH_ToViewer;
+char * _PREHASH_AvatarInterestsUpdate;
+char * _PREHASH_GroupNoticeID;
+char * _PREHASH_ParcelName;
+char * _PREHASH_PriceObjectRent;
+char * _PREHASH_ConnectAgentToUserserver;
+char * _PREHASH_ConnectToUserserver;
+char * _PREHASH_OfferCallingCard;
+char * _PREHASH_AgentAccess;
+char * _PREHASH_AcceptCallingCard;
+char * _PREHASH_DeclineCallingCard;
+char * _PREHASH_DataHomeLocationReply;
+char * _PREHASH_EventLocationReply;
+char * _PREHASH_TerseDateID;
+char * _PREHASH_ObjectOwner;
+char * _PREHASH_AssetID;
+char * _PREHASH_AlertMessage;
+char * _PREHASH_AgentAlertMessage;
+char * _PREHASH_EstateOwnerMessage;
+char * _PREHASH_ParcelMediaCommandMessage;
+char * _PREHASH_Auction;
+char * _PREHASH_Category;
+char * _PREHASH_FilePath;
+char * _PREHASH_ItemFlags;
+char * _PREHASH_Invoice;
+char * _PREHASH_IntervalDays;
+char * _PREHASH_PathScaleX;
+char * _PREHASH_FromTaskID;
+char * _PREHASH_TimeInfo;
+char * _PREHASH_PathScaleY;
+char * _PREHASH_PublicCount;
+char * _PREHASH_ParcelJoin;
+char * _PREHASH_GroupRolesCount;
+char * _PREHASH_SimulatorBlock;
+char * _PREHASH_GroupID;
+char * _PREHASH_AgentVel;
+char * _PREHASH_RequestImage;
+char * _PREHASH_NetStats;
+char * _PREHASH_AgentPos;
+char * _PREHASH_AgentSit;
+char * _PREHASH_Material;
+char * _PREHASH_ObjectDeGrab;
+char * _PREHASH_VelocityInterpolateOff;
+char * _PREHASH_AuthorizedBuyerID;
+char * _PREHASH_AvatarPropertiesReply;
+char * _PREHASH_GroupProfileReply;
+char * _PREHASH_SimOwner;
+char * _PREHASH_SalePrice;
+char * _PREHASH_Animation;
+char * _PREHASH_OwnerID;
+char * _PREHASH_NearestLandingRegionUpdated;
+char * _PREHASH_PassToAgent;
+char * _PREHASH_PreyAgent;
+char * _PREHASH_SimStats;
+char * _PREHASH_Options;
+char * _PREHASH_LogoutReply;
+char * _PREHASH_FeatureDisabled;
+char * _PREHASH_ObjectLocalID;
+char * _PREHASH_Dropped;
+char * _PREHASH_WebProfilesDisabled;
+char * _PREHASH_Destination;
+char * _PREHASH_MasterID;
+char * _PREHASH_TransferData;
+char * _PREHASH_WantToMask;
+char * _PREHASH_AvatarData;
+char * _PREHASH_ParcelSelectObjects;
+char * _PREHASH_ExtraParams;
+char * _PREHASH_LogLogin;
+char * _PREHASH_CreatorID;
+char * _PREHASH_Summary;
+char * _PREHASH_BuyObjectInventory;
+char * _PREHASH_FetchInventory;
+char * _PREHASH_InventoryID;
+char * _PREHASH_PacketNumber;
+char * _PREHASH_SetFollowCamProperties;
+char * _PREHASH_ClearFollowCamProperties;
+char * _PREHASH_SequenceID;
+char * _PREHASH_DataServerLogout;
+char * _PREHASH_NameValue;
+char * _PREHASH_PathShearX;
+char * _PREHASH_PathShearY;
+char * _PREHASH_Velocity;
+char * _PREHASH_SecPerYear;
+char * _PREHASH_FirstName;
+char * _PREHASH_AttachedSoundGainChange;
+char * _PREHASH_LocationID;
+char * _PREHASH_Running;
+char * _PREHASH_AgentThrottle;
+char * _PREHASH_NeighborList;
+char * _PREHASH_PathTaperX;
+char * _PREHASH_PathTaperY;
+char * _PREHASH_AgentRelated;
+char * _PREHASH_GranterBlock;
+char * _PREHASH_UseCachedMuteList;
+char * _PREHASH_FailStats;
+char * _PREHASH_Tempfile;
+char * _PREHASH_BuyerID;
+char * _PREHASH_DirPeopleReply;
+char * _PREHASH_TransferInfo;
+char * _PREHASH_AvatarPickerRequestBackend;
+char * _PREHASH_AvatarPropertiesRequestBackend;
+char * _PREHASH_UpdateData;
+char * _PREHASH_SimFPS;
+char * _PREHASH_ReporterID;
+char * _PREHASH_ButtonLabel;
+char * _PREHASH_GranterID;
+char * _PREHASH_WantToText;
+char * _PREHASH_ReportType;
+char * _PREHASH_DataBlock;
+char * _PREHASH_SimulatorReady;
+char * _PREHASH_AnimationSourceList;
+char * _PREHASH_SubscribeLoad;
+char * _PREHASH_UnsubscribeLoad;
+char * _PREHASH_Packet;
+char * _PREHASH_UndoLand;
+char * _PREHASH_SimAccess;
+char * _PREHASH_MembershipFee;
+char * _PREHASH_InviteGroupResponse;
+char * _PREHASH_CreateInventoryFolder;
+char * _PREHASH_UpdateInventoryFolder;
+char * _PREHASH_MoveInventoryFolder;
+char * _PREHASH_RemoveInventoryFolder;
+char * _PREHASH_MoneyData;
+char * _PREHASH_ObjectDeselect;
+char * _PREHASH_NewAssetID;
+char * _PREHASH_ObjectAdd;
+char * _PREHASH_RayEndIsIntersection;
+char * _PREHASH_CompleteAuction;
+char * _PREHASH_CircuitCode;
+char * _PREHASH_AgentMovementComplete;
+char * _PREHASH_ViewerIP;
+char * _PREHASH_Header;
+char * _PREHASH_GestureFlags;
+char * _PREHASH_XferID;
+char * _PREHASH_StatValue;
+char * _PREHASH_PickID;
+char * _PREHASH_TaskID;
+char * _PREHASH_GridsPerEdge;
+char * _PREHASH_RayEnd;
+char * _PREHASH_Throttles;
+char * _PREHASH_RebakeAvatarTextures;
+char * _PREHASH_UpAxis;
+char * _PREHASH_AgentTextures;
+char * _PREHASH_NotecardData;
+char * _PREHASH_Radius;
+char * _PREHASH_OffCircuit;
+char * _PREHASH_Access;
+char * _PREHASH_TitleRoleID;
+char * _PREHASH_SquareMetersCredit;
+char * _PREHASH_Filename;
+char * _PREHASH_SecuredTemplateChecksumRequest;
+char * _PREHASH_TemplateChecksumRequest;
+char * _PREHASH_AgentPresenceRequest;
+char * _PREHASH_ClassifiedInfoRequest;
+char * _PREHASH_ParcelInfoRequest;
+char * _PREHASH_ParcelObjectOwnersRequest;
+char * _PREHASH_TeleportLandmarkRequest;
+char * _PREHASH_EventInfoRequest;
+char * _PREHASH_ChatFromSimulator;
+char * _PREHASH_PickInfoRequest;
+char * _PREHASH_MoneyBalanceRequest;
+char * _PREHASH_GroupMembersRequest;
+char * _PREHASH_GroupRoleMembersRequest;
+char * _PREHASH_OldFolderID;
+char * _PREHASH_UserInfoRequest;
+char * _PREHASH_TextureID;
+char * _PREHASH_ProfileURL;
+char * _PREHASH_Handle;
+char * _PREHASH_StartParcelRenameAck;
+char * _PREHASH_ButtonIndex;
+char * _PREHASH_GetScriptRunning;
+char * _PREHASH_SetScriptRunning;
+char * _PREHASH_Health;
+char * _PREHASH_FileID;
+char * _PREHASH_CircuitInfo;
+char * _PREHASH_ObjectBuy;
+char * _PREHASH_ProfileEnd;
+char * _PREHASH_Effect;
+char * _PREHASH_TestMessage;
+char * _PREHASH_ScriptMailRegistration;
+char * _PREHASH_AgentSetAppearance;
+char * _PREHASH_AvatarAppearance;
+char * _PREHASH_RegionData;
+char * _PREHASH_RequestingRegionData;
+char * _PREHASH_LandingRegionData;
+char * _PREHASH_SitTransform;
+char * _PREHASH_TerrainBase0;
+char * _PREHASH_SkillsMask;
+char * _PREHASH_AtAxis;
+char * _PREHASH_TerrainBase1;
+char * _PREHASH_Reason;
+char * _PREHASH_TerrainBase2;
+char * _PREHASH_TerrainBase3;
+char * _PREHASH_Params;
+char * _PREHASH_PingID;
+char * _PREHASH_Change;
+char * _PREHASH_Height;
+char * _PREHASH_Region;
+char * _PREHASH_MoneyHistoryReply;
+char * _PREHASH_TelehubInfo;
+char * _PREHASH_StateSave;
+char * _PREHASH_RoleData;
+char * _PREHASH_AgentAnimation;
+char * _PREHASH_AvatarAnimation;
+char * _PREHASH_LogDwellTime;
+char * _PREHASH_ParcelGodMarkAsContent;
+char * _PREHASH_UsePhysics;
+char * _PREHASH_RegionDenyTransacted;
+char * _PREHASH_JointType;
+char * _PREHASH_TaxEstimate;
+char * _PREHASH_ObjectTaxEstimate;
+char * _PREHASH_LightTaxEstimate;
+char * _PREHASH_TeleportLandingStatusChanged;
+char * _PREHASH_LandTaxEstimate;
+char * _PREHASH_GroupTaxEstimate;
+char * _PREHASH_AvgViewerFPS;
+char * _PREHASH_Buttons;
+char * _PREHASH_Sender;
+char * _PREHASH_Dialog;
+char * _PREHASH_TargetData;
+char * _PREHASH_DestID;
+char * _PREHASH_PricePublicObjectDelete;
+char * _PREHASH_ObjectDelete;
+char * _PREHASH_Delete;
+char * _PREHASH_EventGodDelete;
+char * _PREHASH_LastTaxDate;
+char * _PREHASH_MapImageID;
+char * _PREHASH_EndDateTime;
+char * _PREHASH_TerrainDetail0;
+char * _PREHASH_TerrainDetail1;
+char * _PREHASH_TerrainDetail2;
+char * _PREHASH_TerrainDetail3;
+char * _PREHASH_Offset;
+char * _PREHASH_ObjectDelink;
+char * _PREHASH_TargetObject;
+char * _PREHASH_IsEstateManager;
+char * _PREHASH_CancelAuction;
+char * _PREHASH_ObjectDetach;
+char * _PREHASH_Compressed;
+char * _PREHASH_PathBegin;
+char * _PREHASH_BypassRaycast;
+char * _PREHASH_WinnerID;
+char * _PREHASH_ChannelType;
+char * _PREHASH_NonExemptMembers;
+char * _PREHASH_Agents;
+char * _PREHASH_SimulatorStart;
+char * _PREHASH_Enable;
+char * _PREHASH_MemberData;
+char * _PREHASH_ToGroupID;
+char * _PREHASH_ImageNotInDatabase;
+char * _PREHASH_StartDate;
+char * _PREHASH_AnimID;
+char * _PREHASH_Serial;
+char * _PREHASH_ControlPort;
+char * _PREHASH_ModifyLand;
+char * _PREHASH_Digest;
+char * _PREHASH_Victim;
+char * _PREHASH_Script;
+char * _PREHASH_TemplateChecksumReply;
+char * _PREHASH_PickInfoReply;
+char * _PREHASH_MoneyBalanceReply;
+char * _PREHASH_RoutedMoneyBalanceReply;
+char * _PREHASH_RoleID;
+char * _PREHASH_RegionInfo;
+char * _PREHASH_Sequence;
+char * _PREHASH_GodUpdateRegionInfo;
+char * _PREHASH_LocalX;
+char * _PREHASH_LocalY;
+char * _PREHASH_StartAnim;
+char * _PREHASH_Location;
+char * _PREHASH_Action;
+char * _PREHASH_Rights;
+char * _PREHASH_SearchDir;
+char * _PREHASH_Active;
+char * _PREHASH_TransferRequest;
+char * _PREHASH_ScriptSensorRequest;
+char * _PREHASH_MoneyTransferRequest;
+char * _PREHASH_EjectGroupMemberRequest;
+char * _PREHASH_SkillsText;
+char * _PREHASH_Resent;
+char * _PREHASH_Center;
+char * _PREHASH_SharedData;
+char * _PREHASH_PSBlock;
+char * _PREHASH_UUIDNameBlock;
+char * _PREHASH_Viewer;
+char * _PREHASH_GroupNoticeDelete;
+char * _PREHASH_GroupTitleUpdate;
+char * _PREHASH_Method;
+char * _PREHASH_TouchName;
+char * _PREHASH_UpdateType;
+char * _PREHASH_KickedFromEstateID;
+char * _PREHASH_CandidateID;
+char * _PREHASH_ParamData;
+char * _PREHASH_GodlikeMessage;
+char * _PREHASH_SystemMessage;
+char * _PREHASH_BodyRotation;
+char * _PREHASH_SearchRegions;
+char * _PREHASH_Ignore;
+char * _PREHASH_AnimationData;
+char * _PREHASH_StatID;
+char * _PREHASH_ItemID;
+char * _PREHASH_AvatarStatisticsReply;
+char * _PREHASH_ScriptDialogReply;
+char * _PREHASH_RegionIDAndHandleReply;
+char * _PREHASH_CameraAtOffset;
+char * _PREHASH_VoteID;
+char * _PREHASH_ParcelGodForceOwner;
+char * _PREHASH_Filter;
+char * _PREHASH_InviteData;
+char * _PREHASH_PCode;
+char * _PREHASH_SearchPos;
+char * _PREHASH_PreyID;
+char * _PREHASH_TerrainLowerLimit;
+char * _PREHASH_EventFlags;
+char * _PREHASH_TallyVotes;
+char * _PREHASH_Result;
+char * _PREHASH_LookAt;
+char * _PREHASH_PayButton;
+char * _PREHASH_SelfCount;
+char * _PREHASH_PacketCount;
+char * _PREHASH_ParcelBuyPass;
+char * _PREHASH_Identified;
+char * _PREHASH_OldItemID;
+char * _PREHASH_RegionPort;
+char * _PREHASH_PriceEnergyUnit;
+char * _PREHASH_Bitmap;
+char * _PREHASH_TrackAgentSession;
+char * _PREHASH_CacheMissType;
+char * _PREHASH_VFileID;
+char * _PREHASH_GroupInsigniaID;
+char * _PREHASH_FromID;
+char * _PREHASH_Online;
+char * _PREHASH_KickFlags;
+char * _PREHASH_CovenantID;
+char * _PREHASH_SysCPU;
+char * _PREHASH_EMail;
+char * _PREHASH_AggregatePermTextures;
+char * _PREHASH_ChatChannel;
+char * _PREHASH_ReturnID;
+char * _PREHASH_ObjectAttach;
+char * _PREHASH_TargetPort;
+char * _PREHASH_ObjectSpinStop;
+char * _PREHASH_FullID;
+char * _PREHASH_ActivateGroup;
+char * _PREHASH_SysGPU;
+char * _PREHASH_AvatarInterestsReply;
+char * _PREHASH_StartLure;
+char * _PREHASH_SysRAM;
+char * _PREHASH_ObjectPosition;
+char * _PREHASH_SitPosition;
+char * _PREHASH_StartTime;
+char * _PREHASH_BornOn;
+char * _PREHASH_CameraCollidePlane;
+char * _PREHASH_EconomyDataRequest;
+char * _PREHASH_TeleportLureRequest;
+char * _PREHASH_FolderID;
+char * _PREHASH_RegionHandleRequest;
+char * _PREHASH_GestureRequest;
+char * _PREHASH_ScriptDataRequest;
+char * _PREHASH_GroupRoleDataRequest;
+char * _PREHASH_GroupTitlesRequest;
+char * _PREHASH_AgentWearablesRequest;
+char * _PREHASH_MapBlockRequest;
+char * _PREHASH_LureID;
+char * _PREHASH_CopyCenters;
+char * _PREHASH_ParamList;
+char * _PREHASH_InventorySerial;
+char * _PREHASH_EdgeDataPacket;
+char * _PREHASH_AvatarPickerReply;
+char * _PREHASH_ParcelDwellReply;
+char * _PREHASH_IsForSale;
+char * _PREHASH_MuteID;
+char * _PREHASH_MeanCollisionAlert;
+char * _PREHASH_CanAcceptTasks;
+char * _PREHASH_ItemData;
+char * _PREHASH_AnimationList;
+char * _PREHASH_PassObject;
+char * _PREHASH_Reputation;
+char * _PREHASH_IntValue;
+char * _PREHASH_TargetType;
+char * _PREHASH_Amount;
+char * _PREHASH_HasAttachment;
+char * _PREHASH_UpdateAttachment;
+char * _PREHASH_RemoveAttachment;
+char * _PREHASH_HeightWidthBlock;
+char * _PREHASH_RequestObjectPropertiesFamily;
+char * _PREHASH_ObjectPropertiesFamily;
+char * _PREHASH_UserData;
+char * _PREHASH_IsReadable;
+char * _PREHASH_PathCurve;
+char * _PREHASH_Status;
+char * _PREHASH_FromGroup;
+char * _PREHASH_AlreadyVoted;
+char * _PREHASH_PlacesReply;
+char * _PREHASH_DirPlacesReply;
+char * _PREHASH_ParcelBuy;
+char * _PREHASH_DirFindQueryBackend;
+char * _PREHASH_DirPlacesQueryBackend;
+char * _PREHASH_DirClassifiedQueryBackend;
+char * _PREHASH_DirPicksQueryBackend;
+char * _PREHASH_DirLandQueryBackend;
+char * _PREHASH_DirPopularQueryBackend;
+char * _PREHASH_LogoutDemand;
+char * _PREHASH_HistoryData;
+char * _PREHASH_SnapshotID;
+char * _PREHASH_Aspect;
+char * _PREHASH_ParamSize;
+char * _PREHASH_VoteCast;
+char * _PREHASH_CastsShadows;
+char * _PREHASH_EveryoneMask;
+char * _PREHASH_SetSunPhase;
+char * _PREHASH_ObjectSpinUpdate;
+char * _PREHASH_MaturePublish;
+char * _PREHASH_UseExistingAsset;
+char * _PREHASH_Powers;
+char * _PREHASH_ParcelLocalID;
+char * _PREHASH_TeleportCancel;
+char * _PREHASH_UnixTime;
+char * _PREHASH_QueryFlags;
+char * _PREHASH_LastExecFroze;
+char * _PREHASH_AlwaysRun;
+char * _PREHASH_Bottom;
+char * _PREHASH_ButtonData;
+char * _PREHASH_SoundData;
+char * _PREHASH_ViewerStats;
+char * _PREHASH_RegionHandshake;
+char * _PREHASH_ObjectDescription;
+char * _PREHASH_Description;
+char * _PREHASH_ParamType;
+char * _PREHASH_UUIDNameReply;
+char * _PREHASH_UUIDGroupNameReply;
+char * _PREHASH_SaveAssetIntoInventory;
+char * _PREHASH_UserInfo;
+char * _PREHASH_AnimSequenceID;
+char * _PREHASH_NVPairs;
+char * _PREHASH_GroupNoticesListRequest;
+char * _PREHASH_ParcelAccessListRequest;
+char * _PREHASH_MuteListRequest;
+char * _PREHASH_StartPeriod;
+char * _PREHASH_RpcChannelRequest;
+char * _PREHASH_LandStatRequest;
+char * _PREHASH_PlacesQuery;
+char * _PREHASH_DirPlacesQuery;
+char * _PREHASH_SortOrder;
+char * _PREHASH_Hunter;
+char * _PREHASH_SunAngVelocity;
+char * _PREHASH_BinaryBucket;
+char * _PREHASH_ImagePacket;
+char * _PREHASH_StartGroupProposal;
+char * _PREHASH_EnergyLevel;
+char * _PREHASH_PriceForListing;
+char * _PREHASH_Scale;
+char * _PREHASH_EstateCovenantReply;
+char * _PREHASH_ParentEstateID;
+char * _PREHASH_Extra2;
+char * _PREHASH_Throttle;
+char * _PREHASH_SimIP;
+char * _PREHASH_GodID;
+char * _PREHASH_TeleportMinPrice;
+char * _PREHASH_VoteItem;
+char * _PREHASH_ObjectRotation;
+char * _PREHASH_SitRotation;
+char * _PREHASH_SnapSelection;
+char * _PREHASH_SoundTrigger;
+char * _PREHASH_TerrainRaiseLimit;
+char * _PREHASH_Quorum;
+char * _PREHASH_TokenBlock;
+char * _PREHASH_AgentBlock;
+char * _PREHASH_CommandBlock;
+char * _PREHASH_PricePublicObjectDecay;
+char * _PREHASH_SpawnPointPos;
+char * _PREHASH_AttachedSoundCutoffRadius;
+char * _PREHASH_VolumeDetail;
+char * _PREHASH_FromAgentName;
+char * _PREHASH_Range;
+char * _PREHASH_DirectoryVisibility;
+char * _PREHASH_PublicIP;
+char * _PREHASH_TeleportFailed;
+char * _PREHASH_OnlineStatusReply;
+char * _PREHASH_RequestAvatarInfo;
+char * _PREHASH_PreloadSound;
+char * _PREHASH_ScreenshotID;
+char * _PREHASH_CovenantTimestamp;
+char * _PREHASH_OldestUnacked;
+char * _PREHASH_SimulatorIP;
+char * _PREHASH_ObjectImport;
+char * _PREHASH_Value;
+char * _PREHASH_JointAxisOrAnchor;
+char * _PREHASH_Test0;
+char * _PREHASH_Test1;
+char * _PREHASH_Test2;
+char * _PREHASH_SunPhase;
+char * _PREHASH_Place;
+char * _PREHASH_Phase;
+char * _PREHASH_ParcelDivide;
+char * _PREHASH_PriceObjectClaim;
+char * _PREHASH_Field;
+char * _PREHASH_Ratio;
+char * _PREHASH_JoinGroupReply;
+char * _PREHASH_LiveHelpGroupReply;
+char * _PREHASH_Score;
+char * _PREHASH_ExpungeData;
+char * _PREHASH_Image;
+char * _PREHASH_ObjectClickAction;
+char * _PREHASH_Delta;
+char * _PREHASH_InitiateUpload;
+char * _PREHASH_Parameter;
+char * _PREHASH_Flags;
+char * _PREHASH_Plane;
+char * _PREHASH_Width;
+char * _PREHASH_Right;
+char * _PREHASH_DirFindQuery;
+char * _PREHASH_Textures;
+char * _PREHASH_EventData;
+char * _PREHASH_Final;
+char * _PREHASH_TelehubPos;
+char * _PREHASH_ReportAutosaveCrash;
+char * _PREHASH_Reset;
+char * _PREHASH_CreateTrustedCircuit;
+char * _PREHASH_DenyTrustedCircuit;
+char * _PREHASH_RequestTrustedCircuit;
+char * _PREHASH_Codec;
+char * _PREHASH_Level;
+char * _PREHASH_Modal;
+char * _PREHASH_ChildAgentUnknown;
+char * _PREHASH_LandingType;
+char * _PREHASH_ScriptRunningReply;
+char * _PREHASH_MoneyDetailsReply;
+char * _PREHASH_Reply;
+char * _PREHASH_TelehubRot;
+char * _PREHASH_RequestFriendship;
+char * _PREHASH_AcceptFriendship;
+char * _PREHASH_GroupAccountDetailsReply;
+char * _PREHASH_DwellInfo;
+char * _PREHASH_AgentResume;
+char * _PREHASH_ItemType;
+char * _PREHASH_MailFilter;
+char * _PREHASH_Disconnect;
+char * _PREHASH_SimPosition;
+char * _PREHASH_SimWideTotalPrims;
+char * _PREHASH_Index;
+char * _PREHASH_BaseFilename;
+char * _PREHASH_SimFilename;
+char * _PREHASH_LastOwnerID;
+char * _PREHASH_GroupNoticeRequest;
+char * _PREHASH_EmailMessageRequest;
+char * _PREHASH_MapItemRequest;
+char * _PREHASH_AgentCount;
+char * _PREHASH_MessageBlock;
+char * _PREHASH_FuseBlock;
+char * _PREHASH_AgentGroupData;
+char * _PREHASH_ClassifiedInfoUpdate;
+char * _PREHASH_RegionPos;
+char * _PREHASH_ParcelMediaUpdate;
+char * _PREHASH_NoticeID;
+char * _PREHASH_GridX;
+char * _PREHASH_GridY;
+char * _PREHASH_Title;
+char * _PREHASH_AuctionID;
+char * _PREHASH_VoteType;
+char * _PREHASH_CategoryID;
+char * _PREHASH_Token;
+char * _PREHASH_AggregatePerms;
+char * _PREHASH_StartParcelRemoveAck;
+char * _PREHASH_ObjectSelect;
+char * _PREHASH_ForceObjectSelect;
+char * _PREHASH_Price;
+char * _PREHASH_SunDirection;
+char * _PREHASH_FromName;
+char * _PREHASH_ChangeInventoryItemFlags;
+char * _PREHASH_Force;
+char * _PREHASH_TransactionBlock;
+char * _PREHASH_PowersMask;
+char * _PREHASH_Stamp;
+char * _PREHASH_TotalCredits;
+char * _PREHASH_State;
+char * _PREHASH_TextureIndex;
+char * _PREHASH_InviteeID;
+char * _PREHASH_ParcelReclaim;
+char * _PREHASH_Money;
+char * _PREHASH_PathTwist;
+char * _PREHASH_AuthBuyerID;
+char * _PREHASH_Color;
+char * _PREHASH_SourceType;
+char * _PREHASH_World;
+char * _PREHASH_QueryData;
+char * _PREHASH_Users;
+char * _PREHASH_SysOS;
+char * _PREHASH_Notes;
+char * _PREHASH_AvatarID;
+char * _PREHASH_FounderID;
+char * _PREHASH_EndPointID;
+char * _PREHASH_StipendEstimate;
+char * _PREHASH_LocationLookAt;
+char * _PREHASH_Sound;
+char * _PREHASH_Cover;
+char * _PREHASH_TotalObjectCount;
+char * _PREHASH_TextureEntry;
+char * _PREHASH_SquareMetersCommitted;
+char * _PREHASH_ChannelID;
+char * _PREHASH_Dwell;
+char * _PREHASH_North;
+char * _PREHASH_AgentUpdate;
+char * _PREHASH_PickGodDelete;
+char * _PREHASH_HostName;
+char * _PREHASH_PriceParcelClaim;
+char * _PREHASH_ParcelClaim;
+char * _PREHASH_AgentPowers;
+char * _PREHASH_ProfileHollow;
+char * _PREHASH_GroupRoleChanges;
+char * _PREHASH_Count;
+char * _PREHASH_South;
+char * _PREHASH_Entry;
+char * _PREHASH_ObjectUpdateCompressed;
+char * _PREHASH_MuteFlags;
+char * _PREHASH_Group;
+char * _PREHASH_AgentPause;
+char * _PREHASH_LanguagesText;
+char * _PREHASH_InternalScriptMail;
+char * _PREHASH_FindAgent;
+char * _PREHASH_AgentData;
+char * _PREHASH_FolderData;
+char * _PREHASH_AssetBlock;
+char * _PREHASH_AcceptNotices;
+char * _PREHASH_SetGroupAcceptNotices;
+char * _PREHASH_CloseCircuit;
+char * _PREHASH_LogControl;
+char * _PREHASH_TeleportFinish;
+char * _PREHASH_PathRevolutions;
+char * _PREHASH_ClassifiedInfoReply;
+char * _PREHASH_ParcelInfoReply;
+char * _PREHASH_AutosaveData;
+char * _PREHASH_SetStartLocation;
+char * _PREHASH_PassHours;
+char * _PREHASH_AttachmentPt;
+char * _PREHASH_ParcelFlags;
+char * _PREHASH_NumVotes;
+char * _PREHASH_AvatarPickerRequest;
+char * _PREHASH_TeleportLocationRequest;
+char * _PREHASH_DataHomeLocationRequest;
+char * _PREHASH_EventNotificationAddRequest;
+char * _PREHASH_ParcelDwellRequest;
+char * _PREHASH_EventLocationRequest;
+char * _PREHASH_EndPeriod;
+char * _PREHASH_SetStartLocationRequest;
+char * _PREHASH_QueryStart;
+char * _PREHASH_EjectData;
+char * _PREHASH_AvatarTextureUpdate;
+char * _PREHASH_RPCServerPort;
+char * _PREHASH_Bytes;
+char * _PREHASH_Extra;
+char * _PREHASH_ForceScriptControlRelease;
+char * _PREHASH_ParcelRelease;
+char * _PREHASH_VFileType;
+char * _PREHASH_EjectGroupMemberReply;
+char * _PREHASH_ImageData;
+char * _PREHASH_SpaceServerSimulatorTimeMessage;
+char * _PREHASH_SimulatorViewerTimeMessage;
+char * _PREHASH_Rotation;
+char * _PREHASH_Selection;
+char * _PREHASH_TransactionData;
+char * _PREHASH_OperationData;
+char * _PREHASH_ExpirationDate;
+char * _PREHASH_ParcelDeedToGroup;
+char * _PREHASH_DirPicksReply;
+char * _PREHASH_AvatarPicksReply;
+char * _PREHASH_GroupTitlesReply;
+char * _PREHASH_AgentInfo;
+char * _PREHASH_MoneyTransferBackend;
+char * _PREHASH_NextOwnerMask;
+char * _PREHASH_MuteData;
+char * _PREHASH_PassPrice;
+char * _PREHASH_SourceID;
+char * _PREHASH_ChangeUserRights;
+char * _PREHASH_TeleportFlags;
+char * _PREHASH_AssetData;
+char * _PREHASH_SlaveParcelData;
+char * _PREHASH_MultipleObjectUpdate;
+char * _PREHASH_ObjectUpdate;
+char * _PREHASH_ImprovedTerseObjectUpdate;
+char * _PREHASH_ConfirmXferPacket;
+char * _PREHASH_StartPingCheck;
+char * _PREHASH_SimWideDeletes;
+char * _PREHASH_LandStatReply;
+char * _PREHASH_IsPhantom;
+char * _PREHASH_AgentList;
+char * _PREHASH_SimApproved;
+char * _PREHASH_RezObject;
+char * _PREHASH_TaskLocalID;
+char * _PREHASH_ClaimDate;
+char * _PREHASH_MergeParcel;
+char * _PREHASH_Priority;
+char * _PREHASH_Building;
+char * _PREHASH_QueryText;
+char * _PREHASH_GroupNoticeAdd;
+char * _PREHASH_ReturnType;
+char * _PREHASH_FetchFolders;
+char * _PREHASH_SimulatorPublicHostBlock;
+char * _PREHASH_HeaderData;
+char * _PREHASH_RequestMultipleObjects;
+char * _PREHASH_RetrieveInstantMessages;
+char * _PREHASH_DequeueInstantMessages;
+char * _PREHASH_OpenCircuit;
+char * _PREHASH_SecureSessionID;
+char * _PREHASH_CrossedRegion;
+char * _PREHASH_DirGroupsReply;
+char * _PREHASH_AvatarGroupsReply;
+char * _PREHASH_EmailMessageReply;
+char * _PREHASH_GroupVoteHistoryItemReply;
+char * _PREHASH_ViewerPosition;
+char * _PREHASH_Position;
+char * _PREHASH_ParentEstate;
+char * _PREHASH_EstateName;
+char * _PREHASH_MuteName;
+char * _PREHASH_StartParcelRename;
+char * _PREHASH_BulkParcelRename;
+char * _PREHASH_ParcelRename;
+char * _PREHASH_ViewerFilename;
+char * _PREHASH_Positive;
+char * _PREHASH_UserReportInternal;
+char * _PREHASH_AvatarPropertiesRequest;
+char * _PREHASH_ParcelPropertiesRequest;
+char * _PREHASH_GroupProfileRequest;
+char * _PREHASH_AgentDataUpdateRequest;
+char * _PREHASH_PriceObjectScaleFactor;
+char * _PREHASH_DirPicksQuery;
+char * _PREHASH_OpenEnrollment;
+char * _PREHASH_GroupData;
+char * _PREHASH_RequestGodlikePowers;
+char * _PREHASH_GrantGodlikePowers;
+char * _PREHASH_TransactionID;
+char * _PREHASH_DestinationID;
+char * _PREHASH_Controls;
+char * _PREHASH_FirstDetachAll;
+char * _PREHASH_EstateID;
+char * _PREHASH_ImprovedInstantMessage;
+char * _PREHASH_AgentQuit;
+char * _PREHASH_CheckParcelSales;
+char * _PREHASH_ParcelSales;
+char * _PREHASH_CurrentInterval;
+char * _PREHASH_PriceRentLight;
+char * _PREHASH_MediaAutoScale;
+char * _PREHASH_NeighborBlock;
+char * _PREHASH_LayerData;
+char * _PREHASH_NVPairData;
+char * _PREHASH_TeleportLocal;
+char * _PREHASH_EjecteeID;
+char * _PREHASH_VoteInitiator;
+char * _PREHASH_TypeData;
+char * _PREHASH_OwnerIDs;
+char * _PREHASH_SystemKickUser;
+char * _PREHASH_TransactionTime;
+char * _PREHASH_TimeToLive;
+char * _PREHASH_StartParcelRemove;
+char * _PREHASH_BulkParcelRemove;
+char * _PREHASH_OldAgentID;
+char * _PREHASH_BonusEstimate;
+char * _PREHASH_MusicURL;
+char * _PREHASH_CompleteLure;
+char * _PREHASH_ParcelPrimBonus;
+char * _PREHASH_EjectUser;
+char * _PREHASH_CoarseLocationUpdate;
+char * _PREHASH_ChildAgentPositionUpdate;
+char * _PREHASH_StoreLocal;
+char * _PREHASH_GroupName;
+char * _PREHASH_PriceParcelRent;
+char * _PREHASH_SimStatus;
+char * _PREHASH_TransactionSuccess;
+char * _PREHASH_LureType;
+char * _PREHASH_GroupMask;
+char * _PREHASH_SitObject;
+char * _PREHASH_Override;
+char * _PREHASH_LocomotionState;
+char * _PREHASH_PriceUpload;
+char * _PREHASH_RemoveParcel;
+char * _PREHASH_ConfirmAuctionStart;
+char * _PREHASH_RpcScriptRequestInbound;
+char * _PREHASH_ActiveGroupID;
+char * _PREHASH_ParcelReturnObjects;
+char * _PREHASH_TotalObjects;
+char * _PREHASH_ObjectExtraParams;
+char * _PREHASH_Questions;
+char * _PREHASH_TransferAbort;
+char * _PREHASH_TransferInventory;
+char * _PREHASH_RayTargetID;
+char * _PREHASH_ClaimPrice;
+char * _PREHASH_ObjectProperties;
+char * _PREHASH_ParcelProperties;
+char * _PREHASH_EstateOwnerID;
+char * _PREHASH_LogoutRequest;
+char * _PREHASH_AssetUploadRequest;
+char * _PREHASH_ReputationIndividualRequest;
+char * _PREHASH_MajorVersion;
+char * _PREHASH_MinorVersion;
+char * _PREHASH_SimulatorAssign;
+char * _PREHASH_TransactionType;
+char * _PREHASH_AvatarPropertiesUpdate;
+char * _PREHASH_ParcelPropertiesUpdate;
+char * _PREHASH_FetchItems;
+char * _PREHASH_AbortXfer;
+char * _PREHASH_DeRezAck;
+char * _PREHASH_TakeControls;
+char * _PREHASH_DirLandReply;
+char * _PREHASH_SpaceLocationTeleportReply;
+char * _PREHASH_MuteType;
+char * _PREHASH_IMViaEMail;
+char * _PREHASH_StartExpungeProcessAck;
+char * _PREHASH_RentPrice;
+char * _PREHASH_GenericMessage;
+char * _PREHASH_ChildAgentAlive;
+char * _PREHASH_AssetType;
+char * _PREHASH_SpawnPointBlock;
+char * _PREHASH_AttachmentBlock;
+char * _PREHASH_ObjectMaterial;
+char * _PREHASH_OwnerName;
+char * _PREHASH_AvatarNotesReply;
+char * _PREHASH_CacheID;
+char * _PREHASH_OwnerMask;
+char * _PREHASH_TransferInventoryAck;
+
+void init_prehash_data()
+{
+ _PREHASH_X = gMessageStringTable.getString("X");
+ _PREHASH_Y = gMessageStringTable.getString("Y");
+ _PREHASH_Z = gMessageStringTable.getString("Z");
+ _PREHASH_AddFlags = gMessageStringTable.getString("AddFlags");
+ _PREHASH_ReservedNewbie = gMessageStringTable.getString("ReservedNewbie");
+ _PREHASH_FailureInfo = gMessageStringTable.getString("FailureInfo");
+ _PREHASH_MapData = gMessageStringTable.getString("MapData");
+ _PREHASH_AddItem = gMessageStringTable.getString("AddItem");
+ _PREHASH_MeanCollision = gMessageStringTable.getString("MeanCollision");
+ _PREHASH_RezScript = gMessageStringTable.getString("RezScript");
+ _PREHASH_AvatarSitResponse = gMessageStringTable.getString("AvatarSitResponse");
+ _PREHASH_InventoryAssetResponse = gMessageStringTable.getString("InventoryAssetResponse");
+ _PREHASH_KillObject = gMessageStringTable.getString("KillObject");
+ _PREHASH_ProposalID = gMessageStringTable.getString("ProposalID");
+ _PREHASH_SerialNum = gMessageStringTable.getString("SerialNum");
+ _PREHASH_Duration = gMessageStringTable.getString("Duration");
+ _PREHASH_ScriptQuestion = gMessageStringTable.getString("ScriptQuestion");
+ _PREHASH_AddCircuitCode = gMessageStringTable.getString("AddCircuitCode");
+ _PREHASH_UseCircuitCode = gMessageStringTable.getString("UseCircuitCode");
+ _PREHASH_ViewerCircuitCode = gMessageStringTable.getString("ViewerCircuitCode");
+ _PREHASH_ScriptAnswerYes = gMessageStringTable.getString("ScriptAnswerYes");
+ _PREHASH_PartnerID = gMessageStringTable.getString("PartnerID");
+ _PREHASH_DirLandQuery = gMessageStringTable.getString("DirLandQuery");
+ _PREHASH_TeleportStart = gMessageStringTable.getString("TeleportStart");
+ _PREHASH_LogMessages = gMessageStringTable.getString("LogMessages");
+ _PREHASH_AboutText = gMessageStringTable.getString("AboutText");
+ _PREHASH_VisualParam = gMessageStringTable.getString("VisualParam");
+ _PREHASH_GroupPrims = gMessageStringTable.getString("GroupPrims");
+ _PREHASH_SelectedPrims = gMessageStringTable.getString("SelectedPrims");
+ _PREHASH_ID = gMessageStringTable.getString("ID");
+ _PREHASH_UUIDNameRequest = gMessageStringTable.getString("UUIDNameRequest");
+ _PREHASH_UUIDGroupNameRequest = gMessageStringTable.getString("UUIDGroupNameRequest");
+ _PREHASH_MoneyTransactionsRequest = gMessageStringTable.getString("MoneyTransactionsRequest");
+ _PREHASH_GroupAccountTransactionsRequest = gMessageStringTable.getString("GroupAccountTransactionsRequest");
+ _PREHASH_MapNameRequest = gMessageStringTable.getString("MapNameRequest");
+ _PREHASH_MailTaskSimRequest = gMessageStringTable.getString("MailTaskSimRequest");
+ _PREHASH_UpdateSimulator = gMessageStringTable.getString("UpdateSimulator");
+ _PREHASH_BillableFactor = gMessageStringTable.getString("BillableFactor");
+ _PREHASH_ObjectBonusFactor = gMessageStringTable.getString("ObjectBonusFactor");
+ _PREHASH_EnableSimulator = gMessageStringTable.getString("EnableSimulator");
+ _PREHASH_DisableSimulator = gMessageStringTable.getString("DisableSimulator");
+ _PREHASH_ConfirmEnableSimulator = gMessageStringTable.getString("ConfirmEnableSimulator");
+ _PREHASH_LayerType = gMessageStringTable.getString("LayerType");
+ _PREHASH_OwnerRole = gMessageStringTable.getString("OwnerRole");
+ _PREHASH_ParcelOverlay = gMessageStringTable.getString("ParcelOverlay");
+ _PREHASH_AdjustBalance = gMessageStringTable.getString("AdjustBalance");
+ _PREHASH_GroupOwned = gMessageStringTable.getString("GroupOwned");
+ _PREHASH_IP = gMessageStringTable.getString("IP");
+ _PREHASH_ChatFromViewer = gMessageStringTable.getString("ChatFromViewer");
+ _PREHASH_AvgAgentsInView = gMessageStringTable.getString("AvgAgentsInView");
+ _PREHASH_AgentsInView = gMessageStringTable.getString("AgentsInView");
+ _PREHASH_GroupTitle = gMessageStringTable.getString("GroupTitle");
+ _PREHASH_MapLayerReply = gMessageStringTable.getString("MapLayerReply");
+ _PREHASH_CompoundMsgID = gMessageStringTable.getString("CompoundMsgID");
+ _PREHASH_CameraConstraint = gMessageStringTable.getString("CameraConstraint");
+ _PREHASH_DownloadTotals = gMessageStringTable.getString("DownloadTotals");
+ _PREHASH_GenCounter = gMessageStringTable.getString("GenCounter");
+ _PREHASH_FrozenData = gMessageStringTable.getString("FrozenData");
+ _PREHASH_ChildAgentDying = gMessageStringTable.getString("ChildAgentDying");
+ _PREHASH_To = gMessageStringTable.getString("To");
+ _PREHASH_CopyInventoryFromNotecard = gMessageStringTable.getString("CopyInventoryFromNotecard");
+ _PREHASH_RezObjectFromNotecard = gMessageStringTable.getString("RezObjectFromNotecard");
+ _PREHASH_ParcelDirFeeCurrent = gMessageStringTable.getString("ParcelDirFeeCurrent");
+ _PREHASH_SeedCapability = gMessageStringTable.getString("SeedCapability");
+ _PREHASH_ObjectDuplicate = gMessageStringTable.getString("ObjectDuplicate");
+ _PREHASH_InventoryData = gMessageStringTable.getString("InventoryData");
+ _PREHASH_ReplyData = gMessageStringTable.getString("ReplyData");
+ _PREHASH_ResetList = gMessageStringTable.getString("ResetList");
+ _PREHASH_MediaID = gMessageStringTable.getString("MediaID");
+ _PREHASH_RelatedRights = gMessageStringTable.getString("RelatedRights");
+ _PREHASH_RedirectGridX = gMessageStringTable.getString("RedirectGridX");
+ _PREHASH_RedirectGridY = gMessageStringTable.getString("RedirectGridY");
+ _PREHASH_TransferID = gMessageStringTable.getString("TransferID");
+ _PREHASH_Transacted = gMessageStringTable.getString("Transacted");
+ _PREHASH_TexturesChanged = gMessageStringTable.getString("TexturesChanged");
+ _PREHASH_UserLookAt = gMessageStringTable.getString("UserLookAt");
+ _PREHASH_TestBlock1 = gMessageStringTable.getString("TestBlock1");
+ _PREHASH_SensedData = gMessageStringTable.getString("SensedData");
+ _PREHASH_UpdateBlock = gMessageStringTable.getString("UpdateBlock");
+ _PREHASH_ClassifiedGodDelete = gMessageStringTable.getString("ClassifiedGodDelete");
+ _PREHASH_ObjectGrabUpdate = gMessageStringTable.getString("ObjectGrabUpdate");
+ _PREHASH_TaxDate = gMessageStringTable.getString("TaxDate");
+ _PREHASH_LocationPos = gMessageStringTable.getString("LocationPos");
+ _PREHASH_StartDateTime = gMessageStringTable.getString("StartDateTime");
+ _PREHASH_ObjectUpdateCached = gMessageStringTable.getString("ObjectUpdateCached");
+ _PREHASH_Packets = gMessageStringTable.getString("Packets");
+ _PREHASH_FailureType = gMessageStringTable.getString("FailureType");
+ _PREHASH_UpdateGroupInfo = gMessageStringTable.getString("UpdateGroupInfo");
+ _PREHASH_ObjectPermissions = gMessageStringTable.getString("ObjectPermissions");
+ _PREHASH_RevokePermissions = gMessageStringTable.getString("RevokePermissions");
+ _PREHASH_UpdateFlags = gMessageStringTable.getString("UpdateFlags");
+ _PREHASH_ObjectExportSelected = gMessageStringTable.getString("ObjectExportSelected");
+ _PREHASH_RezSelected = gMessageStringTable.getString("RezSelected");
+ _PREHASH_AutoPilot = gMessageStringTable.getString("AutoPilot");
+ _PREHASH_UpdateMuteListEntry = gMessageStringTable.getString("UpdateMuteListEntry");
+ _PREHASH_RemoveMuteListEntry = gMessageStringTable.getString("RemoveMuteListEntry");
+ _PREHASH_SetSimStatusInDatabase = gMessageStringTable.getString("SetSimStatusInDatabase");
+ _PREHASH_SetSimPresenceInDatabase = gMessageStringTable.getString("SetSimPresenceInDatabase");
+ _PREHASH_CameraProperty = gMessageStringTable.getString("CameraProperty");
+ _PREHASH_BrushSize = gMessageStringTable.getString("BrushSize");
+ _PREHASH_StartExpungeProcess = gMessageStringTable.getString("StartExpungeProcess");
+ _PREHASH_SimulatorSetMap = gMessageStringTable.getString("SimulatorSetMap");
+ _PREHASH_RegionPresenceRequestByRegionID = gMessageStringTable.getString("RegionPresenceRequestByRegionID");
+ _PREHASH_ParcelObjectOwnersReply = gMessageStringTable.getString("ParcelObjectOwnersReply");
+ _PREHASH_GroupMembersReply = gMessageStringTable.getString("GroupMembersReply");
+ _PREHASH_GroupRoleMembersReply = gMessageStringTable.getString("GroupRoleMembersReply");
+ _PREHASH_RequestRegionInfo = gMessageStringTable.getString("RequestRegionInfo");
+ _PREHASH_AABBMax = gMessageStringTable.getString("AABBMax");
+ _PREHASH_RequestPayPrice = gMessageStringTable.getString("RequestPayPrice");
+ _PREHASH_SimulatorPresentAtLocation = gMessageStringTable.getString("SimulatorPresentAtLocation");
+ _PREHASH_AgentRequestSit = gMessageStringTable.getString("AgentRequestSit");
+ _PREHASH_AABBMin = gMessageStringTable.getString("AABBMin");
+ _PREHASH_ClassifiedFlags = gMessageStringTable.getString("ClassifiedFlags");
+ _PREHASH_ControlFlags = gMessageStringTable.getString("ControlFlags");
+ _PREHASH_TeleportRequest = gMessageStringTable.getString("TeleportRequest");
+ _PREHASH_SpaceLocationTeleportRequest = gMessageStringTable.getString("SpaceLocationTeleportRequest");
+ _PREHASH_LeaderBoardRequest = gMessageStringTable.getString("LeaderBoardRequest");
+ _PREHASH_ScriptTeleportRequest = gMessageStringTable.getString("ScriptTeleportRequest");
+ _PREHASH_DateUTC = gMessageStringTable.getString("DateUTC");
+ _PREHASH_TaskIDs = gMessageStringTable.getString("TaskIDs");
+ _PREHASH_EstateCovenantRequest = gMessageStringTable.getString("EstateCovenantRequest");
+ _PREHASH_RequestResult = gMessageStringTable.getString("RequestResult");
+ _PREHASH_ReputationAgentAssign = gMessageStringTable.getString("ReputationAgentAssign");
+ _PREHASH_CanAcceptAgents = gMessageStringTable.getString("CanAcceptAgents");
+ _PREHASH_ObjectSaleInfo = gMessageStringTable.getString("ObjectSaleInfo");
+ _PREHASH_KillChildAgents = gMessageStringTable.getString("KillChildAgents");
+ _PREHASH_Balance = gMessageStringTable.getString("Balance");
+ _PREHASH_DerezContainer = gMessageStringTable.getString("DerezContainer");
+ _PREHASH_ObjectData = gMessageStringTable.getString("ObjectData");
+ _PREHASH_CameraAtAxis = gMessageStringTable.getString("CameraAtAxis");
+ _PREHASH_InfoBlock = gMessageStringTable.getString("InfoBlock");
+ _PREHASH_OwnershipCost = gMessageStringTable.getString("OwnershipCost");
+ _PREHASH_AvatarNotesUpdate = gMessageStringTable.getString("AvatarNotesUpdate");
+ _PREHASH_PID = gMessageStringTable.getString("PID");
+ _PREHASH_TimeString = gMessageStringTable.getString("TimeString");
+ _PREHASH_DirPopularReply = gMessageStringTable.getString("DirPopularReply");
+ _PREHASH_TerrainHeightRange00 = gMessageStringTable.getString("TerrainHeightRange00");
+ _PREHASH_SimData = gMessageStringTable.getString("SimData");
+ _PREHASH_TerrainHeightRange01 = gMessageStringTable.getString("TerrainHeightRange01");
+ _PREHASH_TerrainHeightRange10 = gMessageStringTable.getString("TerrainHeightRange10");
+ _PREHASH_TerrainHeightRange11 = gMessageStringTable.getString("TerrainHeightRange11");
+ _PREHASH_UpdateInventoryItem = gMessageStringTable.getString("UpdateInventoryItem");
+ _PREHASH_UpdateCreateInventoryItem = gMessageStringTable.getString("UpdateCreateInventoryItem");
+ _PREHASH_MoveInventoryItem = gMessageStringTable.getString("MoveInventoryItem");
+ _PREHASH_CopyInventoryItem = gMessageStringTable.getString("CopyInventoryItem");
+ _PREHASH_RemoveInventoryItem = gMessageStringTable.getString("RemoveInventoryItem");
+ _PREHASH_CreateInventoryItem = gMessageStringTable.getString("CreateInventoryItem");
+ _PREHASH_PathTwistBegin = gMessageStringTable.getString("PathTwistBegin");
+ _PREHASH_CRC = gMessageStringTable.getString("CRC");
+ _PREHASH_AttachmentPoint = gMessageStringTable.getString("AttachmentPoint");
+ _PREHASH_TelehubBlock = gMessageStringTable.getString("TelehubBlock");
+ _PREHASH_FOVBlock = gMessageStringTable.getString("FOVBlock");
+ _PREHASH_StartLocationData = gMessageStringTable.getString("StartLocationData");
+ _PREHASH_PositionData = gMessageStringTable.getString("PositionData");
+ _PREHASH_TimeSinceLast = gMessageStringTable.getString("TimeSinceLast");
+ _PREHASH_MapImage = gMessageStringTable.getString("MapImage");
+ _PREHASH_Objects = gMessageStringTable.getString("Objects");
+ _PREHASH_URL = gMessageStringTable.getString("URL");
+ _PREHASH_CreationDate = gMessageStringTable.getString("CreationDate");
+ _PREHASH_JointPivot = gMessageStringTable.getString("JointPivot");
+ _PREHASH_RateeID = gMessageStringTable.getString("RateeID");
+ _PREHASH_FPS = gMessageStringTable.getString("FPS");
+ _PREHASH_HasTelehub = gMessageStringTable.getString("HasTelehub");
+ _PREHASH_PathEnd = gMessageStringTable.getString("PathEnd");
+ _PREHASH_ScriptDataReply = gMessageStringTable.getString("ScriptDataReply");
+ _PREHASH_MapBlockReply = gMessageStringTable.getString("MapBlockReply");
+ _PREHASH_PropertiesData = gMessageStringTable.getString("PropertiesData");
+ _PREHASH_ViewerEffect = gMessageStringTable.getString("ViewerEffect");
+ _PREHASH_FreezeUser = gMessageStringTable.getString("FreezeUser");
+ _PREHASH_OwnerPrims = gMessageStringTable.getString("OwnerPrims");
+ _PREHASH_ObjectGrab = gMessageStringTable.getString("ObjectGrab");
+ _PREHASH_ToAgentID = gMessageStringTable.getString("ToAgentID");
+ _PREHASH_SimulatorMapUpdate = gMessageStringTable.getString("SimulatorMapUpdate");
+ _PREHASH_TransferPacket = gMessageStringTable.getString("TransferPacket");
+ _PREHASH_ObjectName = gMessageStringTable.getString("ObjectName");
+ _PREHASH_GroupPowers = gMessageStringTable.getString("GroupPowers");
+ _PREHASH_OriginalName = gMessageStringTable.getString("OriginalName");
+ _PREHASH_CompletePingCheck = gMessageStringTable.getString("CompletePingCheck");
+ _PREHASH_OnlineStatus = gMessageStringTable.getString("OnlineStatus");
+ _PREHASH_ObjectDrop = gMessageStringTable.getString("ObjectDrop");
+ _PREHASH_UseBigPackets = gMessageStringTable.getString("UseBigPackets");
+ _PREHASH_GroupNoticesListReply = gMessageStringTable.getString("GroupNoticesListReply");
+ _PREHASH_ParcelAccessListReply = gMessageStringTable.getString("ParcelAccessListReply");
+ _PREHASH_RpcChannelReply = gMessageStringTable.getString("RpcChannelReply");
+ _PREHASH_RegionPresenceResponse = gMessageStringTable.getString("RegionPresenceResponse");
+ _PREHASH_AgentPresenceResponse = gMessageStringTable.getString("AgentPresenceResponse");
+ _PREHASH_CharterMember = gMessageStringTable.getString("CharterMember");
+ _PREHASH_EdgeData = gMessageStringTable.getString("EdgeData");
+ _PREHASH_NameData = gMessageStringTable.getString("NameData");
+ _PREHASH_RegionPushOverride = gMessageStringTable.getString("RegionPushOverride");
+ _PREHASH_SimName = gMessageStringTable.getString("SimName");
+ _PREHASH_UserReport = gMessageStringTable.getString("UserReport");
+ _PREHASH_DownloadPriority = gMessageStringTable.getString("DownloadPriority");
+ _PREHASH_ToAgentId = gMessageStringTable.getString("ToAgentId");
+ _PREHASH_Mag = gMessageStringTable.getString("Mag");
+ _PREHASH_DirPopularQuery = gMessageStringTable.getString("DirPopularQuery");
+ _PREHASH_ParcelPropertiesRequestByID = gMessageStringTable.getString("ParcelPropertiesRequestByID");
+ _PREHASH_ObjectLink = gMessageStringTable.getString("ObjectLink");
+ _PREHASH_RpcScriptReplyInbound = gMessageStringTable.getString("RpcScriptReplyInbound");
+ _PREHASH_BoardData = gMessageStringTable.getString("BoardData");
+ _PREHASH_RezData = gMessageStringTable.getString("RezData");
+ _PREHASH_RemoveInventoryObjects = gMessageStringTable.getString("RemoveInventoryObjects");
+ _PREHASH_GroupProposalBallot = gMessageStringTable.getString("GroupProposalBallot");
+ _PREHASH_RPCServerIP = gMessageStringTable.getString("RPCServerIP");
+ _PREHASH_Far = gMessageStringTable.getString("Far");
+ _PREHASH_GodSessionID = gMessageStringTable.getString("GodSessionID");
+ _PREHASH_ViewerDigest = gMessageStringTable.getString("ViewerDigest");
+ _PREHASH_FLAboutText = gMessageStringTable.getString("FLAboutText");
+ _PREHASH_RegionHandshakeReply = gMessageStringTable.getString("RegionHandshakeReply");
+ _PREHASH_GroupActiveProposalItemReply = gMessageStringTable.getString("GroupActiveProposalItemReply");
+ _PREHASH_MapItemReply = gMessageStringTable.getString("MapItemReply");
+ _PREHASH_Seconds = gMessageStringTable.getString("Seconds");
+ _PREHASH_UpdateUserInfo = gMessageStringTable.getString("UpdateUserInfo");
+ _PREHASH_AggregatePermTexturesOwner = gMessageStringTable.getString("AggregatePermTexturesOwner");
+ _PREHASH_Set = gMessageStringTable.getString("Set");
+ _PREHASH_NewName = gMessageStringTable.getString("NewName");
+ _PREHASH_Key = gMessageStringTable.getString("Key");
+ _PREHASH_AgentID = gMessageStringTable.getString("AgentID");
+ _PREHASH_OnlineStatusRequest = gMessageStringTable.getString("OnlineStatusRequest");
+ _PREHASH_EventNotificationRemoveRequest = gMessageStringTable.getString("EventNotificationRemoveRequest");
+ _PREHASH_NewFolderID = gMessageStringTable.getString("NewFolderID");
+ _PREHASH_Arc = gMessageStringTable.getString("Arc");
+ _PREHASH_RegionX = gMessageStringTable.getString("RegionX");
+ _PREHASH_RegionY = gMessageStringTable.getString("RegionY");
+ _PREHASH_RequestData = gMessageStringTable.getString("RequestData");
+ _PREHASH_Msg = gMessageStringTable.getString("Msg");
+ _PREHASH_Top = gMessageStringTable.getString("Top");
+ _PREHASH_MiscStats = gMessageStringTable.getString("MiscStats");
+ _PREHASH_ImageID = gMessageStringTable.getString("ImageID");
+ _PREHASH_DataPacket = gMessageStringTable.getString("DataPacket");
+ _PREHASH_ObjectDehinge = gMessageStringTable.getString("ObjectDehinge");
+ _PREHASH_You = gMessageStringTable.getString("You");
+ _PREHASH_ScriptControlChange = gMessageStringTable.getString("ScriptControlChange");
+ _PREHASH_LoadURL = gMessageStringTable.getString("LoadURL");
+ _PREHASH_SetCPURatio = gMessageStringTable.getString("SetCPURatio");
+ _PREHASH_NameValueData = gMessageStringTable.getString("NameValueData");
+ _PREHASH_AtomicPassObject = gMessageStringTable.getString("AtomicPassObject");
+ _PREHASH_ErrorMessage = gMessageStringTable.getString("ErrorMessage");
+ _PREHASH_ViewerFrozenMessage = gMessageStringTable.getString("ViewerFrozenMessage");
+ _PREHASH_HealthMessage = gMessageStringTable.getString("HealthMessage");
+ _PREHASH_LogTextMessage = gMessageStringTable.getString("LogTextMessage");
+ _PREHASH_TimeDilation = gMessageStringTable.getString("TimeDilation");
+ _PREHASH_RemoveContribution = gMessageStringTable.getString("RemoveContribution");
+ _PREHASH_Contribution = gMessageStringTable.getString("Contribution");
+ _PREHASH_SetGroupContribution = gMessageStringTable.getString("SetGroupContribution");
+ _PREHASH_Offline = gMessageStringTable.getString("Offline");
+ _PREHASH_AgentIsNowWearing = gMessageStringTable.getString("AgentIsNowWearing");
+ _PREHASH_SecPerDay = gMessageStringTable.getString("SecPerDay");
+ _PREHASH_Members = gMessageStringTable.getString("Members");
+ _PREHASH_FailedResends = gMessageStringTable.getString("FailedResends");
+ _PREHASH_CameraCenter = gMessageStringTable.getString("CameraCenter");
+ _PREHASH_CameraLeftAxis = gMessageStringTable.getString("CameraLeftAxis");
+ _PREHASH_ExBlock = gMessageStringTable.getString("ExBlock");
+ _PREHASH_Channel = gMessageStringTable.getString("Channel");
+ _PREHASH_NetTest = gMessageStringTable.getString("NetTest");
+ _PREHASH_DiscardLevel = gMessageStringTable.getString("DiscardLevel");
+ _PREHASH_LayerID = gMessageStringTable.getString("LayerID");
+ _PREHASH_RatorID = gMessageStringTable.getString("RatorID");
+ _PREHASH_GrabOffset = gMessageStringTable.getString("GrabOffset");
+ _PREHASH_SimPort = gMessageStringTable.getString("SimPort");
+ _PREHASH_PricePerMeter = gMessageStringTable.getString("PricePerMeter");
+ _PREHASH_RegionFlags = gMessageStringTable.getString("RegionFlags");
+ _PREHASH_VoteResult = gMessageStringTable.getString("VoteResult");
+ _PREHASH_ParcelDirFeeEstimate = gMessageStringTable.getString("ParcelDirFeeEstimate");
+ _PREHASH_ModifyBlock = gMessageStringTable.getString("ModifyBlock");
+ _PREHASH_InventoryBlock = gMessageStringTable.getString("InventoryBlock");
+ _PREHASH_ReplyBlock = gMessageStringTable.getString("ReplyBlock");
+ _PREHASH_ValidUntil = gMessageStringTable.getString("ValidUntil");
+ _PREHASH_VelocityInterpolateOn = gMessageStringTable.getString("VelocityInterpolateOn");
+ _PREHASH_ClassifiedDelete = gMessageStringTable.getString("ClassifiedDelete");
+ _PREHASH_RegionDenyAnonymous = gMessageStringTable.getString("RegionDenyAnonymous");
+ _PREHASH_FLImageID = gMessageStringTable.getString("FLImageID");
+ _PREHASH_AllowPublish = gMessageStringTable.getString("AllowPublish");
+ _PREHASH_SitName = gMessageStringTable.getString("SitName");
+ _PREHASH_RegionsVisited = gMessageStringTable.getString("RegionsVisited");
+ _PREHASH_DirClassifiedReply = gMessageStringTable.getString("DirClassifiedReply");
+ _PREHASH_AvatarClassifiedReply = gMessageStringTable.getString("AvatarClassifiedReply");
+ _PREHASH_ReputationIndividualReply = gMessageStringTable.getString("ReputationIndividualReply");
+ _PREHASH_MediaURL = gMessageStringTable.getString("MediaURL");
+ _PREHASH_CompleteAgentMovement = gMessageStringTable.getString("CompleteAgentMovement");
+ _PREHASH_SpaceIP = gMessageStringTable.getString("SpaceIP");
+ _PREHASH_ClassifiedID = gMessageStringTable.getString("ClassifiedID");
+ _PREHASH_LocalID = gMessageStringTable.getString("LocalID");
+ _PREHASH_RemoveItem = gMessageStringTable.getString("RemoveItem");
+ _PREHASH_LogFailedMoneyTransaction = gMessageStringTable.getString("LogFailedMoneyTransaction");
+ _PREHASH_ViewerStartAuction = gMessageStringTable.getString("ViewerStartAuction");
+ _PREHASH_StartAuction = gMessageStringTable.getString("StartAuction");
+ _PREHASH_NameValueName = gMessageStringTable.getString("NameValueName");
+ _PREHASH_AngVelX = gMessageStringTable.getString("AngVelX");
+ _PREHASH_DuplicateFlags = gMessageStringTable.getString("DuplicateFlags");
+ _PREHASH_AngVelY = gMessageStringTable.getString("AngVelY");
+ _PREHASH_AngVelZ = gMessageStringTable.getString("AngVelZ");
+ _PREHASH_TextColor = gMessageStringTable.getString("TextColor");
+ _PREHASH_SlaveID = gMessageStringTable.getString("SlaveID");
+ _PREHASH_Charter = gMessageStringTable.getString("Charter");
+ _PREHASH_AlertData = gMessageStringTable.getString("AlertData");
+ _PREHASH_TargetBlock = gMessageStringTable.getString("TargetBlock");
+ _PREHASH_CheckParcelAuctions = gMessageStringTable.getString("CheckParcelAuctions");
+ _PREHASH_ParcelAuctions = gMessageStringTable.getString("ParcelAuctions");
+ _PREHASH_OwnerIsGroup = gMessageStringTable.getString("OwnerIsGroup");
+ _PREHASH_NameValuePair = gMessageStringTable.getString("NameValuePair");
+ _PREHASH_RemoveNameValuePair = gMessageStringTable.getString("RemoveNameValuePair");
+ _PREHASH_GetNameValuePair = gMessageStringTable.getString("GetNameValuePair");
+ _PREHASH_BulkUpdateInventory = gMessageStringTable.getString("BulkUpdateInventory");
+ _PREHASH_UpdateTaskInventory = gMessageStringTable.getString("UpdateTaskInventory");
+ _PREHASH_RemoveTaskInventory = gMessageStringTable.getString("RemoveTaskInventory");
+ _PREHASH_MoveTaskInventory = gMessageStringTable.getString("MoveTaskInventory");
+ _PREHASH_RequestTaskInventory = gMessageStringTable.getString("RequestTaskInventory");
+ _PREHASH_ReplyTaskInventory = gMessageStringTable.getString("ReplyTaskInventory");
+ _PREHASH_DeclineInventory = gMessageStringTable.getString("DeclineInventory");
+ _PREHASH_AggregatePermInventory = gMessageStringTable.getString("AggregatePermInventory");
+ _PREHASH_SimulatorInfo = gMessageStringTable.getString("SimulatorInfo");
+ _PREHASH_MoneyTransactionsReply = gMessageStringTable.getString("MoneyTransactionsReply");
+ _PREHASH_GroupAccountTransactionsReply = gMessageStringTable.getString("GroupAccountTransactionsReply");
+ _PREHASH_MailTaskSimReply = gMessageStringTable.getString("MailTaskSimReply");
+ _PREHASH_WearableData = gMessageStringTable.getString("WearableData");
+ _PREHASH_StatisticsData = gMessageStringTable.getString("StatisticsData");
+ _PREHASH_Enabled = gMessageStringTable.getString("Enabled");
+ _PREHASH_Savings = gMessageStringTable.getString("Savings");
+ _PREHASH_SimulatorLoad = gMessageStringTable.getString("SimulatorLoad");
+ _PREHASH_InternalRegionIP = gMessageStringTable.getString("InternalRegionIP");
+ _PREHASH_ExternalRegionIP = gMessageStringTable.getString("ExternalRegionIP");
+ _PREHASH_TotalPairs = gMessageStringTable.getString("TotalPairs");
+ _PREHASH_CreateGroupRequest = gMessageStringTable.getString("CreateGroupRequest");
+ _PREHASH_JoinGroupRequest = gMessageStringTable.getString("JoinGroupRequest");
+ _PREHASH_LeaveGroupRequest = gMessageStringTable.getString("LeaveGroupRequest");
+ _PREHASH_InviteGroupRequest = gMessageStringTable.getString("InviteGroupRequest");
+ _PREHASH_LiveHelpGroupRequest = gMessageStringTable.getString("LiveHelpGroupRequest");
+ _PREHASH_ServerVersion = gMessageStringTable.getString("ServerVersion");
+ _PREHASH_PriceParcelClaimFactor = gMessageStringTable.getString("PriceParcelClaimFactor");
+ _PREHASH_BillableArea = gMessageStringTable.getString("BillableArea");
+ _PREHASH_ObjectID = gMessageStringTable.getString("ObjectID");
+ _PREHASH_ObjectFlagUpdate = gMessageStringTable.getString("ObjectFlagUpdate");
+ _PREHASH_GroupRoleUpdate = gMessageStringTable.getString("GroupRoleUpdate");
+ _PREHASH_RequestInventoryAsset = gMessageStringTable.getString("RequestInventoryAsset");
+ _PREHASH_RedoLand = gMessageStringTable.getString("RedoLand");
+ _PREHASH_TravelAccess = gMessageStringTable.getString("TravelAccess");
+ _PREHASH_ChangedGrid = gMessageStringTable.getString("ChangedGrid");
+ _PREHASH_AgentDropGroup = gMessageStringTable.getString("AgentDropGroup");
+ _PREHASH_Details = gMessageStringTable.getString("Details");
+ _PREHASH_LocationX = gMessageStringTable.getString("LocationX");
+ _PREHASH_SaleType = gMessageStringTable.getString("SaleType");
+ _PREHASH_LocationY = gMessageStringTable.getString("LocationY");
+ _PREHASH_LocationZ = gMessageStringTable.getString("LocationZ");
+ _PREHASH_EconomyData = gMessageStringTable.getString("EconomyData");
+ _PREHASH_HeadRotation = gMessageStringTable.getString("HeadRotation");
+ _PREHASH_DeleteOnCompletion = gMessageStringTable.getString("DeleteOnCompletion");
+ _PREHASH_PublicPort = gMessageStringTable.getString("PublicPort");
+ _PREHASH_DirClassifiedQuery = gMessageStringTable.getString("DirClassifiedQuery");
+ _PREHASH_CallbackID = gMessageStringTable.getString("CallbackID");
+ _PREHASH_RequestParcelTransfer = gMessageStringTable.getString("RequestParcelTransfer");
+ _PREHASH_RoleCount = gMessageStringTable.getString("RoleCount");
+ _PREHASH_ObjectCapacity = gMessageStringTable.getString("ObjectCapacity");
+ _PREHASH_RequestID = gMessageStringTable.getString("RequestID");
+ _PREHASH_RequestXfer = gMessageStringTable.getString("RequestXfer");
+ _PREHASH_ObjectTaxCurrent = gMessageStringTable.getString("ObjectTaxCurrent");
+ _PREHASH_LightTaxCurrent = gMessageStringTable.getString("LightTaxCurrent");
+ _PREHASH_LandTaxCurrent = gMessageStringTable.getString("LandTaxCurrent");
+ _PREHASH_GroupTaxCurrent = gMessageStringTable.getString("GroupTaxCurrent");
+ _PREHASH_FetchInventoryDescendents = gMessageStringTable.getString("FetchInventoryDescendents");
+ _PREHASH_InventoryDescendents = gMessageStringTable.getString("InventoryDescendents");
+ _PREHASH_Descendents = gMessageStringTable.getString("Descendents");
+ _PREHASH_PurgeInventoryDescendents = gMessageStringTable.getString("PurgeInventoryDescendents");
+ _PREHASH_ShowDir = gMessageStringTable.getString("ShowDir");
+ _PREHASH_IsOwner = gMessageStringTable.getString("IsOwner");
+ _PREHASH_Timestamp = gMessageStringTable.getString("Timestamp");
+ _PREHASH_GlobalPos = gMessageStringTable.getString("GlobalPos");
+ _PREHASH_GrabOffsetInitial = gMessageStringTable.getString("GrabOffsetInitial");
+ _PREHASH_IsTrial = gMessageStringTable.getString("IsTrial");
+ _PREHASH_FinalizeLogout = gMessageStringTable.getString("FinalizeLogout");
+ _PREHASH_ObjectDuplicateOnRay = gMessageStringTable.getString("ObjectDuplicateOnRay");
+ _PREHASH_GroupMembershipCount = gMessageStringTable.getString("GroupMembershipCount");
+ _PREHASH_MethodData = gMessageStringTable.getString("MethodData");
+ _PREHASH_ActivateGestures = gMessageStringTable.getString("ActivateGestures");
+ _PREHASH_DeactivateGestures = gMessageStringTable.getString("DeactivateGestures");
+ _PREHASH_ProposalData = gMessageStringTable.getString("ProposalData");
+ _PREHASH_PosGlobal = gMessageStringTable.getString("PosGlobal");
+ _PREHASH_SearchID = gMessageStringTable.getString("SearchID");
+ _PREHASH_RezMultipleAttachmentsFromInv = gMessageStringTable.getString("RezMultipleAttachmentsFromInv");
+ _PREHASH_SearchName = gMessageStringTable.getString("SearchName");
+ _PREHASH_VersionString = gMessageStringTable.getString("VersionString");
+ _PREHASH_CreateGroupReply = gMessageStringTable.getString("CreateGroupReply");
+ _PREHASH_LeaveGroupReply = gMessageStringTable.getString("LeaveGroupReply");
+ _PREHASH_ActualArea = gMessageStringTable.getString("ActualArea");
+ _PREHASH_Message = gMessageStringTable.getString("Message");
+ _PREHASH_ClickAction = gMessageStringTable.getString("ClickAction");
+ _PREHASH_AssetUploadComplete = gMessageStringTable.getString("AssetUploadComplete");
+ _PREHASH_RequestType = gMessageStringTable.getString("RequestType");
+ _PREHASH_UUID = gMessageStringTable.getString("UUID");
+ _PREHASH_BaseMask = gMessageStringTable.getString("BaseMask");
+ _PREHASH_NetBlock = gMessageStringTable.getString("NetBlock");
+ _PREHASH_GlobalX = gMessageStringTable.getString("GlobalX");
+ _PREHASH_GlobalY = gMessageStringTable.getString("GlobalY");
+ _PREHASH_CopyRotates = gMessageStringTable.getString("CopyRotates");
+ _PREHASH_KickUserAck = gMessageStringTable.getString("KickUserAck");
+ _PREHASH_TopPick = gMessageStringTable.getString("TopPick");
+ _PREHASH_SessionID = gMessageStringTable.getString("SessionID");
+ _PREHASH_GlobalZ = gMessageStringTable.getString("GlobalZ");
+ _PREHASH_DeclineFriendship = gMessageStringTable.getString("DeclineFriendship");
+ _PREHASH_FormFriendship = gMessageStringTable.getString("FormFriendship");
+ _PREHASH_TerminateFriendship = gMessageStringTable.getString("TerminateFriendship");
+ _PREHASH_TaskData = gMessageStringTable.getString("TaskData");
+ _PREHASH_SimWideMaxPrims = gMessageStringTable.getString("SimWideMaxPrims");
+ _PREHASH_TotalPrims = gMessageStringTable.getString("TotalPrims");
+ _PREHASH_SourceFilename = gMessageStringTable.getString("SourceFilename");
+ _PREHASH_ProfileBegin = gMessageStringTable.getString("ProfileBegin");
+ _PREHASH_MoneyDetailsRequest = gMessageStringTable.getString("MoneyDetailsRequest");
+ _PREHASH_Request = gMessageStringTable.getString("Request");
+ _PREHASH_GroupAccountDetailsRequest = gMessageStringTable.getString("GroupAccountDetailsRequest");
+ _PREHASH_GroupActiveProposalsRequest = gMessageStringTable.getString("GroupActiveProposalsRequest");
+ _PREHASH_StringValue = gMessageStringTable.getString("StringValue");
+ _PREHASH_ClosestSimulator = gMessageStringTable.getString("ClosestSimulator");
+ _PREHASH_Version = gMessageStringTable.getString("Version");
+ _PREHASH_OtherCount = gMessageStringTable.getString("OtherCount");
+ _PREHASH_MemberCount = gMessageStringTable.getString("MemberCount");
+ _PREHASH_ChatData = gMessageStringTable.getString("ChatData");
+ _PREHASH_IsGroupOwned = gMessageStringTable.getString("IsGroupOwned");
+ _PREHASH_EnergyEfficiency = gMessageStringTable.getString("EnergyEfficiency");
+ _PREHASH_MaxPlace = gMessageStringTable.getString("MaxPlace");
+ _PREHASH_PickInfoUpdate = gMessageStringTable.getString("PickInfoUpdate");
+ _PREHASH_PickDelete = gMessageStringTable.getString("PickDelete");
+ _PREHASH_ScriptReset = gMessageStringTable.getString("ScriptReset");
+ _PREHASH_Requester = gMessageStringTable.getString("Requester");
+ _PREHASH_ForSale = gMessageStringTable.getString("ForSale");
+ _PREHASH_NearestLandingRegionReply = gMessageStringTable.getString("NearestLandingRegionReply");
+ _PREHASH_RecordAgentPresence = gMessageStringTable.getString("RecordAgentPresence");
+ _PREHASH_EraseAgentPresence = gMessageStringTable.getString("EraseAgentPresence");
+ _PREHASH_ParcelID = gMessageStringTable.getString("ParcelID");
+ _PREHASH_Godlike = gMessageStringTable.getString("Godlike");
+ _PREHASH_TotalDebits = gMessageStringTable.getString("TotalDebits");
+ _PREHASH_Direction = gMessageStringTable.getString("Direction");
+ _PREHASH_Appearance = gMessageStringTable.getString("Appearance");
+ _PREHASH_HealthData = gMessageStringTable.getString("HealthData");
+ _PREHASH_LeftAxis = gMessageStringTable.getString("LeftAxis");
+ _PREHASH_LocationBlock = gMessageStringTable.getString("LocationBlock");
+ _PREHASH_ObjectImage = gMessageStringTable.getString("ObjectImage");
+ _PREHASH_TerrainStartHeight00 = gMessageStringTable.getString("TerrainStartHeight00");
+ _PREHASH_TerrainStartHeight01 = gMessageStringTable.getString("TerrainStartHeight01");
+ _PREHASH_TerrainStartHeight10 = gMessageStringTable.getString("TerrainStartHeight10");
+ _PREHASH_ObjectHinge = gMessageStringTable.getString("ObjectHinge");
+ _PREHASH_TerrainStartHeight11 = gMessageStringTable.getString("TerrainStartHeight11");
+ _PREHASH_MetersPerGrid = gMessageStringTable.getString("MetersPerGrid");
+ _PREHASH_WaterHeight = gMessageStringTable.getString("WaterHeight");
+ _PREHASH_FetchInventoryReply = gMessageStringTable.getString("FetchInventoryReply");
+ _PREHASH_MoneySummaryReply = gMessageStringTable.getString("MoneySummaryReply");
+ _PREHASH_GroupAccountSummaryReply = gMessageStringTable.getString("GroupAccountSummaryReply");
+ _PREHASH_AttachedSound = gMessageStringTable.getString("AttachedSound");
+ _PREHASH_ParamInUse = gMessageStringTable.getString("ParamInUse");
+ _PREHASH_GodKickUser = gMessageStringTable.getString("GodKickUser");
+ _PREHASH_PickName = gMessageStringTable.getString("PickName");
+ _PREHASH_TaskName = gMessageStringTable.getString("TaskName");
+ _PREHASH_ParcelGodReserveForNewbie = gMessageStringTable.getString("ParcelGodReserveForNewbie");
+ _PREHASH_SubType = gMessageStringTable.getString("SubType");
+ _PREHASH_ObjectCount = gMessageStringTable.getString("ObjectCount");
+ _PREHASH_RegionPresenceRequestByHandle = gMessageStringTable.getString("RegionPresenceRequestByHandle");
+ _PREHASH_RezSingleAttachmentFromInv = gMessageStringTable.getString("RezSingleAttachmentFromInv");
+ _PREHASH_ChildAgentUpdate = gMessageStringTable.getString("ChildAgentUpdate");
+ _PREHASH_ToID = gMessageStringTable.getString("ToID");
+ _PREHASH_ViewerPort = gMessageStringTable.getString("ViewerPort");
+ _PREHASH_IsOwnerGroup = gMessageStringTable.getString("IsOwnerGroup");
+ _PREHASH_AgentHeightWidth = gMessageStringTable.getString("AgentHeightWidth");
+ _PREHASH_VerticalAngle = gMessageStringTable.getString("VerticalAngle");
+ _PREHASH_WearableType = gMessageStringTable.getString("WearableType");
+ _PREHASH_AggregatePermNextOwner = gMessageStringTable.getString("AggregatePermNextOwner");
+ _PREHASH_ShowInList = gMessageStringTable.getString("ShowInList");
+ _PREHASH_PositionSuggestion = gMessageStringTable.getString("PositionSuggestion");
+ _PREHASH_UpdateParcel = gMessageStringTable.getString("UpdateParcel");
+ _PREHASH_ClearAgentSessions = gMessageStringTable.getString("ClearAgentSessions");
+ _PREHASH_SetAlwaysRun = gMessageStringTable.getString("SetAlwaysRun");
+ _PREHASH_NVPair = gMessageStringTable.getString("NVPair");
+ _PREHASH_ObjectSpinStart = gMessageStringTable.getString("ObjectSpinStart");
+ _PREHASH_UseEstateSun = gMessageStringTable.getString("UseEstateSun");
+ _PREHASH_LogoutBlock = gMessageStringTable.getString("LogoutBlock");
+ _PREHASH_RelayLogControl = gMessageStringTable.getString("RelayLogControl");
+ _PREHASH_RegionID = gMessageStringTable.getString("RegionID");
+ _PREHASH_Creator = gMessageStringTable.getString("Creator");
+ _PREHASH_ProposalText = gMessageStringTable.getString("ProposalText");
+ _PREHASH_DirEventsReply = gMessageStringTable.getString("DirEventsReply");
+ _PREHASH_EventInfoReply = gMessageStringTable.getString("EventInfoReply");
+ _PREHASH_UserInfoReply = gMessageStringTable.getString("UserInfoReply");
+ _PREHASH_PathRadiusOffset = gMessageStringTable.getString("PathRadiusOffset");
+ _PREHASH_SessionInfo = gMessageStringTable.getString("SessionInfo");
+ _PREHASH_TextureData = gMessageStringTable.getString("TextureData");
+ _PREHASH_ChatPass = gMessageStringTable.getString("ChatPass");
+ _PREHASH_TargetID = gMessageStringTable.getString("TargetID");
+ _PREHASH_DefaultPayPrice = gMessageStringTable.getString("DefaultPayPrice");
+ _PREHASH_UserLocation = gMessageStringTable.getString("UserLocation");
+ _PREHASH_MaxPrims = gMessageStringTable.getString("MaxPrims");
+ _PREHASH_RegionIP = gMessageStringTable.getString("RegionIP");
+ _PREHASH_LandmarkID = gMessageStringTable.getString("LandmarkID");
+ _PREHASH_InitiateDownload = gMessageStringTable.getString("InitiateDownload");
+ _PREHASH_Name = gMessageStringTable.getString("Name");
+ _PREHASH_OtherCleanTime = gMessageStringTable.getString("OtherCleanTime");
+ _PREHASH_ParcelSetOtherCleanTime = gMessageStringTable.getString("ParcelSetOtherCleanTime");
+ _PREHASH_TeleportPriceExponent = gMessageStringTable.getString("TeleportPriceExponent");
+ _PREHASH_Gain = gMessageStringTable.getString("Gain");
+ _PREHASH_VelX = gMessageStringTable.getString("VelX");
+ _PREHASH_PacketAck = gMessageStringTable.getString("PacketAck");
+ _PREHASH_PathSkew = gMessageStringTable.getString("PathSkew");
+ _PREHASH_Negative = gMessageStringTable.getString("Negative");
+ _PREHASH_VelY = gMessageStringTable.getString("VelY");
+ _PREHASH_SimulatorShutdownRequest = gMessageStringTable.getString("SimulatorShutdownRequest");
+ _PREHASH_NearestLandingRegionRequest = gMessageStringTable.getString("NearestLandingRegionRequest");
+ _PREHASH_VelZ = gMessageStringTable.getString("VelZ");
+ _PREHASH_OtherID = gMessageStringTable.getString("OtherID");
+ _PREHASH_MemberID = gMessageStringTable.getString("MemberID");
+ _PREHASH_MapLayerRequest = gMessageStringTable.getString("MapLayerRequest");
+ _PREHASH_PatchVersion = gMessageStringTable.getString("PatchVersion");
+ _PREHASH_ObjectScale = gMessageStringTable.getString("ObjectScale");
+ _PREHASH_TargetIP = gMessageStringTable.getString("TargetIP");
+ _PREHASH_Redo = gMessageStringTable.getString("Redo");
+ _PREHASH_MoneyBalance = gMessageStringTable.getString("MoneyBalance");
+ _PREHASH_TrackAgent = gMessageStringTable.getString("TrackAgent");
+ _PREHASH_MaxX = gMessageStringTable.getString("MaxX");
+ _PREHASH_Data = gMessageStringTable.getString("Data");
+ _PREHASH_MaxY = gMessageStringTable.getString("MaxY");
+ _PREHASH_TextureAnim = gMessageStringTable.getString("TextureAnim");
+ _PREHASH_ReturnIDs = gMessageStringTable.getString("ReturnIDs");
+ _PREHASH_Date = gMessageStringTable.getString("Date");
+ _PREHASH_GestureUpdate = gMessageStringTable.getString("GestureUpdate");
+ _PREHASH_AgentWearablesUpdate = gMessageStringTable.getString("AgentWearablesUpdate");
+ _PREHASH_AgentDataUpdate = gMessageStringTable.getString("AgentDataUpdate");
+ _PREHASH_Hash = gMessageStringTable.getString("Hash");
+ _PREHASH_GroupDataUpdate = gMessageStringTable.getString("GroupDataUpdate");
+ _PREHASH_AgentGroupDataUpdate = gMessageStringTable.getString("AgentGroupDataUpdate");
+ _PREHASH_Left = gMessageStringTable.getString("Left");
+ _PREHASH_Mask = gMessageStringTable.getString("Mask");
+ _PREHASH_ForceMouselook = gMessageStringTable.getString("ForceMouselook");
+ _PREHASH_Success = gMessageStringTable.getString("Success");
+ _PREHASH_ObjectGroup = gMessageStringTable.getString("ObjectGroup");
+ _PREHASH_SunHour = gMessageStringTable.getString("SunHour");
+ _PREHASH_MinX = gMessageStringTable.getString("MinX");
+ _PREHASH_ScriptSensorReply = gMessageStringTable.getString("ScriptSensorReply");
+ _PREHASH_MinY = gMessageStringTable.getString("MinY");
+ _PREHASH_Command = gMessageStringTable.getString("Command");
+ _PREHASH_Desc = gMessageStringTable.getString("Desc");
+ _PREHASH_AttachmentNeedsSave = gMessageStringTable.getString("AttachmentNeedsSave");
+ _PREHASH_HistoryItemData = gMessageStringTable.getString("HistoryItemData");
+ _PREHASH_AgentCachedTexture = gMessageStringTable.getString("AgentCachedTexture");
+ _PREHASH_Subject = gMessageStringTable.getString("Subject");
+ _PREHASH_East = gMessageStringTable.getString("East");
+ _PREHASH_GodExpungeUser = gMessageStringTable.getString("GodExpungeUser");
+ _PREHASH_QueryReplies = gMessageStringTable.getString("QueryReplies");
+ _PREHASH_ObjectCategory = gMessageStringTable.getString("ObjectCategory");
+ _PREHASH_Time = gMessageStringTable.getString("Time");
+ _PREHASH_CreateLandmarkForEvent = gMessageStringTable.getString("CreateLandmarkForEvent");
+ _PREHASH_ParentID = gMessageStringTable.getString("ParentID");
+ _PREHASH_Ping = gMessageStringTable.getString("Ping");
+ _PREHASH_Perp = gMessageStringTable.getString("Perp");
+ _PREHASH_Code = gMessageStringTable.getString("Code");
+ _PREHASH_InvType = gMessageStringTable.getString("InvType");
+ _PREHASH_AgentFOV = gMessageStringTable.getString("AgentFOV");
+ _PREHASH_BulkMoneyTransfer = gMessageStringTable.getString("BulkMoneyTransfer");
+ _PREHASH_Audible = gMessageStringTable.getString("Audible");
+ _PREHASH_AuctionData = gMessageStringTable.getString("AuctionData");
+ _PREHASH_IDBlock = gMessageStringTable.getString("IDBlock");
+ _PREHASH_ReputationData = gMessageStringTable.getString("ReputationData");
+ _PREHASH_West = gMessageStringTable.getString("West");
+ _PREHASH_Undo = gMessageStringTable.getString("Undo");
+ _PREHASH_TotalNumItems = gMessageStringTable.getString("TotalNumItems");
+ _PREHASH_Info = gMessageStringTable.getString("Info");
+ _PREHASH_Area = gMessageStringTable.getString("Area");
+ _PREHASH_Behavior = gMessageStringTable.getString("Behavior");
+ _PREHASH_SimCrashed = gMessageStringTable.getString("SimCrashed");
+ _PREHASH_Text = gMessageStringTable.getString("Text");
+ _PREHASH_AgentToNewRegion = gMessageStringTable.getString("AgentToNewRegion");
+ _PREHASH_PriceGroupCreate = gMessageStringTable.getString("PriceGroupCreate");
+ _PREHASH_ObjectShape = gMessageStringTable.getString("ObjectShape");
+ _PREHASH_GroupRoleDataReply = gMessageStringTable.getString("GroupRoleDataReply");
+ _PREHASH_PosX = gMessageStringTable.getString("PosX");
+ _PREHASH_PosY = gMessageStringTable.getString("PosY");
+ _PREHASH_MuteCRC = gMessageStringTable.getString("MuteCRC");
+ _PREHASH_PosZ = gMessageStringTable.getString("PosZ");
+ _PREHASH_Size = gMessageStringTable.getString("Size");
+ _PREHASH_FromAddress = gMessageStringTable.getString("FromAddress");
+ _PREHASH_Body = gMessageStringTable.getString("Body");
+ _PREHASH_FileData = gMessageStringTable.getString("FileData");
+ _PREHASH_List = gMessageStringTable.getString("List");
+ _PREHASH_KickUser = gMessageStringTable.getString("KickUser");
+ _PREHASH_OtherPrims = gMessageStringTable.getString("OtherPrims");
+ _PREHASH_RunTime = gMessageStringTable.getString("RunTime");
+ _PREHASH_GrantUserRights = gMessageStringTable.getString("GrantUserRights");
+ _PREHASH_RpcScriptRequestInboundForward = gMessageStringTable.getString("RpcScriptRequestInboundForward");
+ _PREHASH_More = gMessageStringTable.getString("More");
+ _PREHASH_Majority = gMessageStringTable.getString("Majority");
+ _PREHASH_MetersTraveled = gMessageStringTable.getString("MetersTraveled");
+ _PREHASH_Stat = gMessageStringTable.getString("Stat");
+ _PREHASH_SoundID = gMessageStringTable.getString("SoundID");
+ _PREHASH_Item = gMessageStringTable.getString("Item");
+ _PREHASH_User = gMessageStringTable.getString("User");
+ _PREHASH_RemoteInfos = gMessageStringTable.getString("RemoteInfos");
+ _PREHASH_Prey = gMessageStringTable.getString("Prey");
+ _PREHASH_UsecSinceStart = gMessageStringTable.getString("UsecSinceStart");
+ _PREHASH_RayStart = gMessageStringTable.getString("RayStart");
+ _PREHASH_ParcelData = gMessageStringTable.getString("ParcelData");
+ _PREHASH_CameraUpAxis = gMessageStringTable.getString("CameraUpAxis");
+ _PREHASH_ScriptDialog = gMessageStringTable.getString("ScriptDialog");
+ _PREHASH_MasterParcelData = gMessageStringTable.getString("MasterParcelData");
+ _PREHASH_Invalid = gMessageStringTable.getString("Invalid");
+ _PREHASH_MinPlace = gMessageStringTable.getString("MinPlace");
+ _PREHASH_ProfileCurve = gMessageStringTable.getString("ProfileCurve");
+ _PREHASH_ParcelAccessListUpdate = gMessageStringTable.getString("ParcelAccessListUpdate");
+ _PREHASH_MuteListUpdate = gMessageStringTable.getString("MuteListUpdate");
+ _PREHASH_SendPacket = gMessageStringTable.getString("SendPacket");
+ _PREHASH_SendXferPacket = gMessageStringTable.getString("SendXferPacket");
+ _PREHASH_RegionDenyIdentified = gMessageStringTable.getString("RegionDenyIdentified");
+ _PREHASH_NotecardItemID = gMessageStringTable.getString("NotecardItemID");
+ _PREHASH_LastName = gMessageStringTable.getString("LastName");
+ _PREHASH_From = gMessageStringTable.getString("From");
+ _PREHASH_RoleChange = gMessageStringTable.getString("RoleChange");
+ _PREHASH_Port = gMessageStringTable.getString("Port");
+ _PREHASH_MemberTitle = gMessageStringTable.getString("MemberTitle");
+ _PREHASH_LogParcelChanges = gMessageStringTable.getString("LogParcelChanges");
+ _PREHASH_AgentCachedTextureResponse = gMessageStringTable.getString("AgentCachedTextureResponse");
+ _PREHASH_DeRezObject = gMessageStringTable.getString("DeRezObject");
+ _PREHASH_IsTemporary = gMessageStringTable.getString("IsTemporary");
+ _PREHASH_InsigniaID = gMessageStringTable.getString("InsigniaID");
+ _PREHASH_CheckFlags = gMessageStringTable.getString("CheckFlags");
+ _PREHASH_TransferPriority = gMessageStringTable.getString("TransferPriority");
+ _PREHASH_EventID = gMessageStringTable.getString("EventID");
+ _PREHASH_Selected = gMessageStringTable.getString("Selected");
+ _PREHASH_FromAgentId = gMessageStringTable.getString("FromAgentId");
+ _PREHASH_Type = gMessageStringTable.getString("Type");
+ _PREHASH_ChatType = gMessageStringTable.getString("ChatType");
+ _PREHASH_ReportData = gMessageStringTable.getString("ReportData");
+ _PREHASH_LeaderBoardData = gMessageStringTable.getString("LeaderBoardData");
+ _PREHASH_RequestBlock = gMessageStringTable.getString("RequestBlock");
+ _PREHASH_GrantData = gMessageStringTable.getString("GrantData");
+ _PREHASH_DetachAttachmentIntoInv = gMessageStringTable.getString("DetachAttachmentIntoInv");
+ _PREHASH_ParcelDisableObjects = gMessageStringTable.getString("ParcelDisableObjects");
+ _PREHASH_Sections = gMessageStringTable.getString("Sections");
+ _PREHASH_GodLevel = gMessageStringTable.getString("GodLevel");
+ _PREHASH_PayPriceReply = gMessageStringTable.getString("PayPriceReply");
+ _PREHASH_QueryID = gMessageStringTable.getString("QueryID");
+ _PREHASH_CameraEyeOffset = gMessageStringTable.getString("CameraEyeOffset");
+ _PREHASH_AgentPosition = gMessageStringTable.getString("AgentPosition");
+ _PREHASH_GrabPosition = gMessageStringTable.getString("GrabPosition");
+ _PREHASH_OnlineNotification = gMessageStringTable.getString("OnlineNotification");
+ _PREHASH_OfflineNotification = gMessageStringTable.getString("OfflineNotification");
+ _PREHASH_SendPostcard = gMessageStringTable.getString("SendPostcard");
+ _PREHASH_RequestFlags = gMessageStringTable.getString("RequestFlags");
+ _PREHASH_MoneyHistoryRequest = gMessageStringTable.getString("MoneyHistoryRequest");
+ _PREHASH_MoneySummaryRequest = gMessageStringTable.getString("MoneySummaryRequest");
+ _PREHASH_GroupAccountSummaryRequest = gMessageStringTable.getString("GroupAccountSummaryRequest");
+ _PREHASH_GroupVoteHistoryRequest = gMessageStringTable.getString("GroupVoteHistoryRequest");
+ _PREHASH_ParamValue = gMessageStringTable.getString("ParamValue");
+ _PREHASH_Checksum = gMessageStringTable.getString("Checksum");
+ _PREHASH_MaxAgents = gMessageStringTable.getString("MaxAgents");
+ _PREHASH_CreateNewOutfitAttachments = gMessageStringTable.getString("CreateNewOutfitAttachments");
+ _PREHASH_RegionHandle = gMessageStringTable.getString("RegionHandle");
+ _PREHASH_TeleportProgress = gMessageStringTable.getString("TeleportProgress");
+ _PREHASH_AgentQuitCopy = gMessageStringTable.getString("AgentQuitCopy");
+ _PREHASH_ToViewer = gMessageStringTable.getString("ToViewer");
+ _PREHASH_AvatarInterestsUpdate = gMessageStringTable.getString("AvatarInterestsUpdate");
+ _PREHASH_GroupNoticeID = gMessageStringTable.getString("GroupNoticeID");
+ _PREHASH_ParcelName = gMessageStringTable.getString("ParcelName");
+ _PREHASH_PriceObjectRent = gMessageStringTable.getString("PriceObjectRent");
+ _PREHASH_ConnectAgentToUserserver = gMessageStringTable.getString("ConnectAgentToUserserver");
+ _PREHASH_ConnectToUserserver = gMessageStringTable.getString("ConnectToUserserver");
+ _PREHASH_OfferCallingCard = gMessageStringTable.getString("OfferCallingCard");
+ _PREHASH_AgentAccess = gMessageStringTable.getString("AgentAccess");
+ _PREHASH_AcceptCallingCard = gMessageStringTable.getString("AcceptCallingCard");
+ _PREHASH_DeclineCallingCard = gMessageStringTable.getString("DeclineCallingCard");
+ _PREHASH_DataHomeLocationReply = gMessageStringTable.getString("DataHomeLocationReply");
+ _PREHASH_EventLocationReply = gMessageStringTable.getString("EventLocationReply");
+ _PREHASH_TerseDateID = gMessageStringTable.getString("TerseDateID");
+ _PREHASH_ObjectOwner = gMessageStringTable.getString("ObjectOwner");
+ _PREHASH_AssetID = gMessageStringTable.getString("AssetID");
+ _PREHASH_AlertMessage = gMessageStringTable.getString("AlertMessage");
+ _PREHASH_AgentAlertMessage = gMessageStringTable.getString("AgentAlertMessage");
+ _PREHASH_EstateOwnerMessage = gMessageStringTable.getString("EstateOwnerMessage");
+ _PREHASH_ParcelMediaCommandMessage = gMessageStringTable.getString("ParcelMediaCommandMessage");
+ _PREHASH_Auction = gMessageStringTable.getString("Auction");
+ _PREHASH_Category = gMessageStringTable.getString("Category");
+ _PREHASH_FilePath = gMessageStringTable.getString("FilePath");
+ _PREHASH_ItemFlags = gMessageStringTable.getString("ItemFlags");
+ _PREHASH_Invoice = gMessageStringTable.getString("Invoice");
+ _PREHASH_IntervalDays = gMessageStringTable.getString("IntervalDays");
+ _PREHASH_PathScaleX = gMessageStringTable.getString("PathScaleX");
+ _PREHASH_FromTaskID = gMessageStringTable.getString("FromTaskID");
+ _PREHASH_TimeInfo = gMessageStringTable.getString("TimeInfo");
+ _PREHASH_PathScaleY = gMessageStringTable.getString("PathScaleY");
+ _PREHASH_PublicCount = gMessageStringTable.getString("PublicCount");
+ _PREHASH_ParcelJoin = gMessageStringTable.getString("ParcelJoin");
+ _PREHASH_GroupRolesCount = gMessageStringTable.getString("GroupRolesCount");
+ _PREHASH_SimulatorBlock = gMessageStringTable.getString("SimulatorBlock");
+ _PREHASH_GroupID = gMessageStringTable.getString("GroupID");
+ _PREHASH_AgentVel = gMessageStringTable.getString("AgentVel");
+ _PREHASH_RequestImage = gMessageStringTable.getString("RequestImage");
+ _PREHASH_NetStats = gMessageStringTable.getString("NetStats");
+ _PREHASH_AgentPos = gMessageStringTable.getString("AgentPos");
+ _PREHASH_AgentSit = gMessageStringTable.getString("AgentSit");
+ _PREHASH_Material = gMessageStringTable.getString("Material");
+ _PREHASH_ObjectDeGrab = gMessageStringTable.getString("ObjectDeGrab");
+ _PREHASH_VelocityInterpolateOff = gMessageStringTable.getString("VelocityInterpolateOff");
+ _PREHASH_AuthorizedBuyerID = gMessageStringTable.getString("AuthorizedBuyerID");
+ _PREHASH_AvatarPropertiesReply = gMessageStringTable.getString("AvatarPropertiesReply");
+ _PREHASH_GroupProfileReply = gMessageStringTable.getString("GroupProfileReply");
+ _PREHASH_SimOwner = gMessageStringTable.getString("SimOwner");
+ _PREHASH_SalePrice = gMessageStringTable.getString("SalePrice");
+ _PREHASH_Animation = gMessageStringTable.getString("Animation");
+ _PREHASH_OwnerID = gMessageStringTable.getString("OwnerID");
+ _PREHASH_NearestLandingRegionUpdated = gMessageStringTable.getString("NearestLandingRegionUpdated");
+ _PREHASH_PassToAgent = gMessageStringTable.getString("PassToAgent");
+ _PREHASH_PreyAgent = gMessageStringTable.getString("PreyAgent");
+ _PREHASH_SimStats = gMessageStringTable.getString("SimStats");
+ _PREHASH_Options = gMessageStringTable.getString("Options");
+ _PREHASH_LogoutReply = gMessageStringTable.getString("LogoutReply");
+ _PREHASH_FeatureDisabled = gMessageStringTable.getString("FeatureDisabled");
+ _PREHASH_ObjectLocalID = gMessageStringTable.getString("ObjectLocalID");
+ _PREHASH_Dropped = gMessageStringTable.getString("Dropped");
+ _PREHASH_WebProfilesDisabled = gMessageStringTable.getString("WebProfilesDisabled");
+ _PREHASH_Destination = gMessageStringTable.getString("Destination");
+ _PREHASH_MasterID = gMessageStringTable.getString("MasterID");
+ _PREHASH_TransferData = gMessageStringTable.getString("TransferData");
+ _PREHASH_WantToMask = gMessageStringTable.getString("WantToMask");
+ _PREHASH_AvatarData = gMessageStringTable.getString("AvatarData");
+ _PREHASH_ParcelSelectObjects = gMessageStringTable.getString("ParcelSelectObjects");
+ _PREHASH_ExtraParams = gMessageStringTable.getString("ExtraParams");
+ _PREHASH_LogLogin = gMessageStringTable.getString("LogLogin");
+ _PREHASH_CreatorID = gMessageStringTable.getString("CreatorID");
+ _PREHASH_Summary = gMessageStringTable.getString("Summary");
+ _PREHASH_BuyObjectInventory = gMessageStringTable.getString("BuyObjectInventory");
+ _PREHASH_FetchInventory = gMessageStringTable.getString("FetchInventory");
+ _PREHASH_InventoryID = gMessageStringTable.getString("InventoryID");
+ _PREHASH_PacketNumber = gMessageStringTable.getString("PacketNumber");
+ _PREHASH_SetFollowCamProperties = gMessageStringTable.getString("SetFollowCamProperties");
+ _PREHASH_ClearFollowCamProperties = gMessageStringTable.getString("ClearFollowCamProperties");
+ _PREHASH_SequenceID = gMessageStringTable.getString("SequenceID");
+ _PREHASH_DataServerLogout = gMessageStringTable.getString("DataServerLogout");
+ _PREHASH_NameValue = gMessageStringTable.getString("NameValue");
+ _PREHASH_PathShearX = gMessageStringTable.getString("PathShearX");
+ _PREHASH_PathShearY = gMessageStringTable.getString("PathShearY");
+ _PREHASH_Velocity = gMessageStringTable.getString("Velocity");
+ _PREHASH_SecPerYear = gMessageStringTable.getString("SecPerYear");
+ _PREHASH_FirstName = gMessageStringTable.getString("FirstName");
+ _PREHASH_AttachedSoundGainChange = gMessageStringTable.getString("AttachedSoundGainChange");
+ _PREHASH_LocationID = gMessageStringTable.getString("LocationID");
+ _PREHASH_Running = gMessageStringTable.getString("Running");
+ _PREHASH_AgentThrottle = gMessageStringTable.getString("AgentThrottle");
+ _PREHASH_NeighborList = gMessageStringTable.getString("NeighborList");
+ _PREHASH_PathTaperX = gMessageStringTable.getString("PathTaperX");
+ _PREHASH_PathTaperY = gMessageStringTable.getString("PathTaperY");
+ _PREHASH_AgentRelated = gMessageStringTable.getString("AgentRelated");
+ _PREHASH_GranterBlock = gMessageStringTable.getString("GranterBlock");
+ _PREHASH_UseCachedMuteList = gMessageStringTable.getString("UseCachedMuteList");
+ _PREHASH_FailStats = gMessageStringTable.getString("FailStats");
+ _PREHASH_Tempfile = gMessageStringTable.getString("Tempfile");
+ _PREHASH_BuyerID = gMessageStringTable.getString("BuyerID");
+ _PREHASH_DirPeopleReply = gMessageStringTable.getString("DirPeopleReply");
+ _PREHASH_TransferInfo = gMessageStringTable.getString("TransferInfo");
+ _PREHASH_AvatarPickerRequestBackend = gMessageStringTable.getString("AvatarPickerRequestBackend");
+ _PREHASH_AvatarPropertiesRequestBackend = gMessageStringTable.getString("AvatarPropertiesRequestBackend");
+ _PREHASH_UpdateData = gMessageStringTable.getString("UpdateData");
+ _PREHASH_SimFPS = gMessageStringTable.getString("SimFPS");
+ _PREHASH_ReporterID = gMessageStringTable.getString("ReporterID");
+ _PREHASH_ButtonLabel = gMessageStringTable.getString("ButtonLabel");
+ _PREHASH_GranterID = gMessageStringTable.getString("GranterID");
+ _PREHASH_WantToText = gMessageStringTable.getString("WantToText");
+ _PREHASH_ReportType = gMessageStringTable.getString("ReportType");
+ _PREHASH_DataBlock = gMessageStringTable.getString("DataBlock");
+ _PREHASH_SimulatorReady = gMessageStringTable.getString("SimulatorReady");
+ _PREHASH_AnimationSourceList = gMessageStringTable.getString("AnimationSourceList");
+ _PREHASH_SubscribeLoad = gMessageStringTable.getString("SubscribeLoad");
+ _PREHASH_UnsubscribeLoad = gMessageStringTable.getString("UnsubscribeLoad");
+ _PREHASH_Packet = gMessageStringTable.getString("Packet");
+ _PREHASH_UndoLand = gMessageStringTable.getString("UndoLand");
+ _PREHASH_SimAccess = gMessageStringTable.getString("SimAccess");
+ _PREHASH_MembershipFee = gMessageStringTable.getString("MembershipFee");
+ _PREHASH_InviteGroupResponse = gMessageStringTable.getString("InviteGroupResponse");
+ _PREHASH_CreateInventoryFolder = gMessageStringTable.getString("CreateInventoryFolder");
+ _PREHASH_UpdateInventoryFolder = gMessageStringTable.getString("UpdateInventoryFolder");
+ _PREHASH_MoveInventoryFolder = gMessageStringTable.getString("MoveInventoryFolder");
+ _PREHASH_RemoveInventoryFolder = gMessageStringTable.getString("RemoveInventoryFolder");
+ _PREHASH_MoneyData = gMessageStringTable.getString("MoneyData");
+ _PREHASH_ObjectDeselect = gMessageStringTable.getString("ObjectDeselect");
+ _PREHASH_NewAssetID = gMessageStringTable.getString("NewAssetID");
+ _PREHASH_ObjectAdd = gMessageStringTable.getString("ObjectAdd");
+ _PREHASH_RayEndIsIntersection = gMessageStringTable.getString("RayEndIsIntersection");
+ _PREHASH_CompleteAuction = gMessageStringTable.getString("CompleteAuction");
+ _PREHASH_CircuitCode = gMessageStringTable.getString("CircuitCode");
+ _PREHASH_AgentMovementComplete = gMessageStringTable.getString("AgentMovementComplete");
+ _PREHASH_ViewerIP = gMessageStringTable.getString("ViewerIP");
+ _PREHASH_Header = gMessageStringTable.getString("Header");
+ _PREHASH_GestureFlags = gMessageStringTable.getString("GestureFlags");
+ _PREHASH_XferID = gMessageStringTable.getString("XferID");
+ _PREHASH_StatValue = gMessageStringTable.getString("StatValue");
+ _PREHASH_PickID = gMessageStringTable.getString("PickID");
+ _PREHASH_TaskID = gMessageStringTable.getString("TaskID");
+ _PREHASH_GridsPerEdge = gMessageStringTable.getString("GridsPerEdge");
+ _PREHASH_RayEnd = gMessageStringTable.getString("RayEnd");
+ _PREHASH_Throttles = gMessageStringTable.getString("Throttles");
+ _PREHASH_RebakeAvatarTextures = gMessageStringTable.getString("RebakeAvatarTextures");
+ _PREHASH_UpAxis = gMessageStringTable.getString("UpAxis");
+ _PREHASH_AgentTextures = gMessageStringTable.getString("AgentTextures");
+ _PREHASH_NotecardData = gMessageStringTable.getString("NotecardData");
+ _PREHASH_Radius = gMessageStringTable.getString("Radius");
+ _PREHASH_OffCircuit = gMessageStringTable.getString("OffCircuit");
+ _PREHASH_Access = gMessageStringTable.getString("Access");
+ _PREHASH_TitleRoleID = gMessageStringTable.getString("TitleRoleID");
+ _PREHASH_SquareMetersCredit = gMessageStringTable.getString("SquareMetersCredit");
+ _PREHASH_Filename = gMessageStringTable.getString("Filename");
+ _PREHASH_SecuredTemplateChecksumRequest = gMessageStringTable.getString("SecuredTemplateChecksumRequest");
+ _PREHASH_TemplateChecksumRequest = gMessageStringTable.getString("TemplateChecksumRequest");
+ _PREHASH_AgentPresenceRequest = gMessageStringTable.getString("AgentPresenceRequest");
+ _PREHASH_ClassifiedInfoRequest = gMessageStringTable.getString("ClassifiedInfoRequest");
+ _PREHASH_ParcelInfoRequest = gMessageStringTable.getString("ParcelInfoRequest");
+ _PREHASH_ParcelObjectOwnersRequest = gMessageStringTable.getString("ParcelObjectOwnersRequest");
+ _PREHASH_TeleportLandmarkRequest = gMessageStringTable.getString("TeleportLandmarkRequest");
+ _PREHASH_EventInfoRequest = gMessageStringTable.getString("EventInfoRequest");
+ _PREHASH_ChatFromSimulator = gMessageStringTable.getString("ChatFromSimulator");
+ _PREHASH_PickInfoRequest = gMessageStringTable.getString("PickInfoRequest");
+ _PREHASH_MoneyBalanceRequest = gMessageStringTable.getString("MoneyBalanceRequest");
+ _PREHASH_GroupMembersRequest = gMessageStringTable.getString("GroupMembersRequest");
+ _PREHASH_GroupRoleMembersRequest = gMessageStringTable.getString("GroupRoleMembersRequest");
+ _PREHASH_OldFolderID = gMessageStringTable.getString("OldFolderID");
+ _PREHASH_UserInfoRequest = gMessageStringTable.getString("UserInfoRequest");
+ _PREHASH_TextureID = gMessageStringTable.getString("TextureID");
+ _PREHASH_ProfileURL = gMessageStringTable.getString("ProfileURL");
+ _PREHASH_Handle = gMessageStringTable.getString("Handle");
+ _PREHASH_StartParcelRenameAck = gMessageStringTable.getString("StartParcelRenameAck");
+ _PREHASH_ButtonIndex = gMessageStringTable.getString("ButtonIndex");
+ _PREHASH_GetScriptRunning = gMessageStringTable.getString("GetScriptRunning");
+ _PREHASH_SetScriptRunning = gMessageStringTable.getString("SetScriptRunning");
+ _PREHASH_Health = gMessageStringTable.getString("Health");
+ _PREHASH_FileID = gMessageStringTable.getString("FileID");
+ _PREHASH_CircuitInfo = gMessageStringTable.getString("CircuitInfo");
+ _PREHASH_ObjectBuy = gMessageStringTable.getString("ObjectBuy");
+ _PREHASH_ProfileEnd = gMessageStringTable.getString("ProfileEnd");
+ _PREHASH_Effect = gMessageStringTable.getString("Effect");
+ _PREHASH_TestMessage = gMessageStringTable.getString("TestMessage");
+ _PREHASH_ScriptMailRegistration = gMessageStringTable.getString("ScriptMailRegistration");
+ _PREHASH_AgentSetAppearance = gMessageStringTable.getString("AgentSetAppearance");
+ _PREHASH_AvatarAppearance = gMessageStringTable.getString("AvatarAppearance");
+ _PREHASH_RegionData = gMessageStringTable.getString("RegionData");
+ _PREHASH_RequestingRegionData = gMessageStringTable.getString("RequestingRegionData");
+ _PREHASH_LandingRegionData = gMessageStringTable.getString("LandingRegionData");
+ _PREHASH_SitTransform = gMessageStringTable.getString("SitTransform");
+ _PREHASH_TerrainBase0 = gMessageStringTable.getString("TerrainBase0");
+ _PREHASH_SkillsMask = gMessageStringTable.getString("SkillsMask");
+ _PREHASH_AtAxis = gMessageStringTable.getString("AtAxis");
+ _PREHASH_TerrainBase1 = gMessageStringTable.getString("TerrainBase1");
+ _PREHASH_Reason = gMessageStringTable.getString("Reason");
+ _PREHASH_TerrainBase2 = gMessageStringTable.getString("TerrainBase2");
+ _PREHASH_TerrainBase3 = gMessageStringTable.getString("TerrainBase3");
+ _PREHASH_Params = gMessageStringTable.getString("Params");
+ _PREHASH_PingID = gMessageStringTable.getString("PingID");
+ _PREHASH_Change = gMessageStringTable.getString("Change");
+ _PREHASH_Height = gMessageStringTable.getString("Height");
+ _PREHASH_Region = gMessageStringTable.getString("Region");
+ _PREHASH_MoneyHistoryReply = gMessageStringTable.getString("MoneyHistoryReply");
+ _PREHASH_TelehubInfo = gMessageStringTable.getString("TelehubInfo");
+ _PREHASH_StateSave = gMessageStringTable.getString("StateSave");
+ _PREHASH_RoleData = gMessageStringTable.getString("RoleData");
+ _PREHASH_AgentAnimation = gMessageStringTable.getString("AgentAnimation");
+ _PREHASH_AvatarAnimation = gMessageStringTable.getString("AvatarAnimation");
+ _PREHASH_LogDwellTime = gMessageStringTable.getString("LogDwellTime");
+ _PREHASH_ParcelGodMarkAsContent = gMessageStringTable.getString("ParcelGodMarkAsContent");
+ _PREHASH_UsePhysics = gMessageStringTable.getString("UsePhysics");
+ _PREHASH_RegionDenyTransacted = gMessageStringTable.getString("RegionDenyTransacted");
+ _PREHASH_JointType = gMessageStringTable.getString("JointType");
+ _PREHASH_TaxEstimate = gMessageStringTable.getString("TaxEstimate");
+ _PREHASH_ObjectTaxEstimate = gMessageStringTable.getString("ObjectTaxEstimate");
+ _PREHASH_LightTaxEstimate = gMessageStringTable.getString("LightTaxEstimate");
+ _PREHASH_TeleportLandingStatusChanged = gMessageStringTable.getString("TeleportLandingStatusChanged");
+ _PREHASH_LandTaxEstimate = gMessageStringTable.getString("LandTaxEstimate");
+ _PREHASH_GroupTaxEstimate = gMessageStringTable.getString("GroupTaxEstimate");
+ _PREHASH_AvgViewerFPS = gMessageStringTable.getString("AvgViewerFPS");
+ _PREHASH_Buttons = gMessageStringTable.getString("Buttons");
+ _PREHASH_Sender = gMessageStringTable.getString("Sender");
+ _PREHASH_Dialog = gMessageStringTable.getString("Dialog");
+ _PREHASH_TargetData = gMessageStringTable.getString("TargetData");
+ _PREHASH_DestID = gMessageStringTable.getString("DestID");
+ _PREHASH_PricePublicObjectDelete = gMessageStringTable.getString("PricePublicObjectDelete");
+ _PREHASH_ObjectDelete = gMessageStringTable.getString("ObjectDelete");
+ _PREHASH_Delete = gMessageStringTable.getString("Delete");
+ _PREHASH_EventGodDelete = gMessageStringTable.getString("EventGodDelete");
+ _PREHASH_LastTaxDate = gMessageStringTable.getString("LastTaxDate");
+ _PREHASH_MapImageID = gMessageStringTable.getString("MapImageID");
+ _PREHASH_EndDateTime = gMessageStringTable.getString("EndDateTime");
+ _PREHASH_TerrainDetail0 = gMessageStringTable.getString("TerrainDetail0");
+ _PREHASH_TerrainDetail1 = gMessageStringTable.getString("TerrainDetail1");
+ _PREHASH_TerrainDetail2 = gMessageStringTable.getString("TerrainDetail2");
+ _PREHASH_TerrainDetail3 = gMessageStringTable.getString("TerrainDetail3");
+ _PREHASH_Offset = gMessageStringTable.getString("Offset");
+ _PREHASH_ObjectDelink = gMessageStringTable.getString("ObjectDelink");
+ _PREHASH_TargetObject = gMessageStringTable.getString("TargetObject");
+ _PREHASH_IsEstateManager = gMessageStringTable.getString("IsEstateManager");
+ _PREHASH_CancelAuction = gMessageStringTable.getString("CancelAuction");
+ _PREHASH_ObjectDetach = gMessageStringTable.getString("ObjectDetach");
+ _PREHASH_Compressed = gMessageStringTable.getString("Compressed");
+ _PREHASH_PathBegin = gMessageStringTable.getString("PathBegin");
+ _PREHASH_BypassRaycast = gMessageStringTable.getString("BypassRaycast");
+ _PREHASH_WinnerID = gMessageStringTable.getString("WinnerID");
+ _PREHASH_ChannelType = gMessageStringTable.getString("ChannelType");
+ _PREHASH_NonExemptMembers = gMessageStringTable.getString("NonExemptMembers");
+ _PREHASH_Agents = gMessageStringTable.getString("Agents");
+ _PREHASH_SimulatorStart = gMessageStringTable.getString("SimulatorStart");
+ _PREHASH_Enable = gMessageStringTable.getString("Enable");
+ _PREHASH_MemberData = gMessageStringTable.getString("MemberData");
+ _PREHASH_ToGroupID = gMessageStringTable.getString("ToGroupID");
+ _PREHASH_ImageNotInDatabase = gMessageStringTable.getString("ImageNotInDatabase");
+ _PREHASH_StartDate = gMessageStringTable.getString("StartDate");
+ _PREHASH_AnimID = gMessageStringTable.getString("AnimID");
+ _PREHASH_Serial = gMessageStringTable.getString("Serial");
+ _PREHASH_ControlPort = gMessageStringTable.getString("ControlPort");
+ _PREHASH_ModifyLand = gMessageStringTable.getString("ModifyLand");
+ _PREHASH_Digest = gMessageStringTable.getString("Digest");
+ _PREHASH_Victim = gMessageStringTable.getString("Victim");
+ _PREHASH_Script = gMessageStringTable.getString("Script");
+ _PREHASH_TemplateChecksumReply = gMessageStringTable.getString("TemplateChecksumReply");
+ _PREHASH_PickInfoReply = gMessageStringTable.getString("PickInfoReply");
+ _PREHASH_MoneyBalanceReply = gMessageStringTable.getString("MoneyBalanceReply");
+ _PREHASH_RoutedMoneyBalanceReply = gMessageStringTable.getString("RoutedMoneyBalanceReply");
+ _PREHASH_RoleID = gMessageStringTable.getString("RoleID");
+ _PREHASH_RegionInfo = gMessageStringTable.getString("RegionInfo");
+ _PREHASH_Sequence = gMessageStringTable.getString("Sequence");
+ _PREHASH_GodUpdateRegionInfo = gMessageStringTable.getString("GodUpdateRegionInfo");
+ _PREHASH_LocalX = gMessageStringTable.getString("LocalX");
+ _PREHASH_LocalY = gMessageStringTable.getString("LocalY");
+ _PREHASH_StartAnim = gMessageStringTable.getString("StartAnim");
+ _PREHASH_Location = gMessageStringTable.getString("Location");
+ _PREHASH_Action = gMessageStringTable.getString("Action");
+ _PREHASH_Rights = gMessageStringTable.getString("Rights");
+ _PREHASH_SearchDir = gMessageStringTable.getString("SearchDir");
+ _PREHASH_Active = gMessageStringTable.getString("Active");
+ _PREHASH_TransferRequest = gMessageStringTable.getString("TransferRequest");
+ _PREHASH_ScriptSensorRequest = gMessageStringTable.getString("ScriptSensorRequest");
+ _PREHASH_MoneyTransferRequest = gMessageStringTable.getString("MoneyTransferRequest");
+ _PREHASH_EjectGroupMemberRequest = gMessageStringTable.getString("EjectGroupMemberRequest");
+ _PREHASH_SkillsText = gMessageStringTable.getString("SkillsText");
+ _PREHASH_Resent = gMessageStringTable.getString("Resent");
+ _PREHASH_Center = gMessageStringTable.getString("Center");
+ _PREHASH_SharedData = gMessageStringTable.getString("SharedData");
+ _PREHASH_PSBlock = gMessageStringTable.getString("PSBlock");
+ _PREHASH_UUIDNameBlock = gMessageStringTable.getString("UUIDNameBlock");
+ _PREHASH_Viewer = gMessageStringTable.getString("Viewer");
+ _PREHASH_GroupNoticeDelete = gMessageStringTable.getString("GroupNoticeDelete");
+ _PREHASH_GroupTitleUpdate = gMessageStringTable.getString("GroupTitleUpdate");
+ _PREHASH_Method = gMessageStringTable.getString("Method");
+ _PREHASH_TouchName = gMessageStringTable.getString("TouchName");
+ _PREHASH_UpdateType = gMessageStringTable.getString("UpdateType");
+ _PREHASH_KickedFromEstateID = gMessageStringTable.getString("KickedFromEstateID");
+ _PREHASH_CandidateID = gMessageStringTable.getString("CandidateID");
+ _PREHASH_ParamData = gMessageStringTable.getString("ParamData");
+ _PREHASH_GodlikeMessage = gMessageStringTable.getString("GodlikeMessage");
+ _PREHASH_SystemMessage = gMessageStringTable.getString("SystemMessage");
+ _PREHASH_BodyRotation = gMessageStringTable.getString("BodyRotation");
+ _PREHASH_SearchRegions = gMessageStringTable.getString("SearchRegions");
+ _PREHASH_Ignore = gMessageStringTable.getString("Ignore");
+ _PREHASH_AnimationData = gMessageStringTable.getString("AnimationData");
+ _PREHASH_StatID = gMessageStringTable.getString("StatID");
+ _PREHASH_ItemID = gMessageStringTable.getString("ItemID");
+ _PREHASH_AvatarStatisticsReply = gMessageStringTable.getString("AvatarStatisticsReply");
+ _PREHASH_ScriptDialogReply = gMessageStringTable.getString("ScriptDialogReply");
+ _PREHASH_RegionIDAndHandleReply = gMessageStringTable.getString("RegionIDAndHandleReply");
+ _PREHASH_CameraAtOffset = gMessageStringTable.getString("CameraAtOffset");
+ _PREHASH_VoteID = gMessageStringTable.getString("VoteID");
+ _PREHASH_ParcelGodForceOwner = gMessageStringTable.getString("ParcelGodForceOwner");
+ _PREHASH_Filter = gMessageStringTable.getString("Filter");
+ _PREHASH_InviteData = gMessageStringTable.getString("InviteData");
+ _PREHASH_PCode = gMessageStringTable.getString("PCode");
+ _PREHASH_SearchPos = gMessageStringTable.getString("SearchPos");
+ _PREHASH_PreyID = gMessageStringTable.getString("PreyID");
+ _PREHASH_TerrainLowerLimit = gMessageStringTable.getString("TerrainLowerLimit");
+ _PREHASH_EventFlags = gMessageStringTable.getString("EventFlags");
+ _PREHASH_TallyVotes = gMessageStringTable.getString("TallyVotes");
+ _PREHASH_Result = gMessageStringTable.getString("Result");
+ _PREHASH_LookAt = gMessageStringTable.getString("LookAt");
+ _PREHASH_PayButton = gMessageStringTable.getString("PayButton");
+ _PREHASH_SelfCount = gMessageStringTable.getString("SelfCount");
+ _PREHASH_PacketCount = gMessageStringTable.getString("PacketCount");
+ _PREHASH_ParcelBuyPass = gMessageStringTable.getString("ParcelBuyPass");
+ _PREHASH_Identified = gMessageStringTable.getString("Identified");
+ _PREHASH_OldItemID = gMessageStringTable.getString("OldItemID");
+ _PREHASH_RegionPort = gMessageStringTable.getString("RegionPort");
+ _PREHASH_PriceEnergyUnit = gMessageStringTable.getString("PriceEnergyUnit");
+ _PREHASH_Bitmap = gMessageStringTable.getString("Bitmap");
+ _PREHASH_TrackAgentSession = gMessageStringTable.getString("TrackAgentSession");
+ _PREHASH_CacheMissType = gMessageStringTable.getString("CacheMissType");
+ _PREHASH_VFileID = gMessageStringTable.getString("VFileID");
+ _PREHASH_GroupInsigniaID = gMessageStringTable.getString("GroupInsigniaID");
+ _PREHASH_FromID = gMessageStringTable.getString("FromID");
+ _PREHASH_Online = gMessageStringTable.getString("Online");
+ _PREHASH_KickFlags = gMessageStringTable.getString("KickFlags");
+ _PREHASH_CovenantID = gMessageStringTable.getString("CovenantID");
+ _PREHASH_SysCPU = gMessageStringTable.getString("SysCPU");
+ _PREHASH_EMail = gMessageStringTable.getString("EMail");
+ _PREHASH_AggregatePermTextures = gMessageStringTable.getString("AggregatePermTextures");
+ _PREHASH_ChatChannel = gMessageStringTable.getString("ChatChannel");
+ _PREHASH_ReturnID = gMessageStringTable.getString("ReturnID");
+ _PREHASH_ObjectAttach = gMessageStringTable.getString("ObjectAttach");
+ _PREHASH_TargetPort = gMessageStringTable.getString("TargetPort");
+ _PREHASH_ObjectSpinStop = gMessageStringTable.getString("ObjectSpinStop");
+ _PREHASH_FullID = gMessageStringTable.getString("FullID");
+ _PREHASH_ActivateGroup = gMessageStringTable.getString("ActivateGroup");
+ _PREHASH_SysGPU = gMessageStringTable.getString("SysGPU");
+ _PREHASH_AvatarInterestsReply = gMessageStringTable.getString("AvatarInterestsReply");
+ _PREHASH_StartLure = gMessageStringTable.getString("StartLure");
+ _PREHASH_SysRAM = gMessageStringTable.getString("SysRAM");
+ _PREHASH_ObjectPosition = gMessageStringTable.getString("ObjectPosition");
+ _PREHASH_SitPosition = gMessageStringTable.getString("SitPosition");
+ _PREHASH_StartTime = gMessageStringTable.getString("StartTime");
+ _PREHASH_BornOn = gMessageStringTable.getString("BornOn");
+ _PREHASH_CameraCollidePlane = gMessageStringTable.getString("CameraCollidePlane");
+ _PREHASH_EconomyDataRequest = gMessageStringTable.getString("EconomyDataRequest");
+ _PREHASH_TeleportLureRequest = gMessageStringTable.getString("TeleportLureRequest");
+ _PREHASH_FolderID = gMessageStringTable.getString("FolderID");
+ _PREHASH_RegionHandleRequest = gMessageStringTable.getString("RegionHandleRequest");
+ _PREHASH_GestureRequest = gMessageStringTable.getString("GestureRequest");
+ _PREHASH_ScriptDataRequest = gMessageStringTable.getString("ScriptDataRequest");
+ _PREHASH_GroupRoleDataRequest = gMessageStringTable.getString("GroupRoleDataRequest");
+ _PREHASH_GroupTitlesRequest = gMessageStringTable.getString("GroupTitlesRequest");
+ _PREHASH_AgentWearablesRequest = gMessageStringTable.getString("AgentWearablesRequest");
+ _PREHASH_MapBlockRequest = gMessageStringTable.getString("MapBlockRequest");
+ _PREHASH_LureID = gMessageStringTable.getString("LureID");
+ _PREHASH_CopyCenters = gMessageStringTable.getString("CopyCenters");
+ _PREHASH_ParamList = gMessageStringTable.getString("ParamList");
+ _PREHASH_InventorySerial = gMessageStringTable.getString("InventorySerial");
+ _PREHASH_EdgeDataPacket = gMessageStringTable.getString("EdgeDataPacket");
+ _PREHASH_AvatarPickerReply = gMessageStringTable.getString("AvatarPickerReply");
+ _PREHASH_ParcelDwellReply = gMessageStringTable.getString("ParcelDwellReply");
+ _PREHASH_IsForSale = gMessageStringTable.getString("IsForSale");
+ _PREHASH_MuteID = gMessageStringTable.getString("MuteID");
+ _PREHASH_MeanCollisionAlert = gMessageStringTable.getString("MeanCollisionAlert");
+ _PREHASH_CanAcceptTasks = gMessageStringTable.getString("CanAcceptTasks");
+ _PREHASH_ItemData = gMessageStringTable.getString("ItemData");
+ _PREHASH_AnimationList = gMessageStringTable.getString("AnimationList");
+ _PREHASH_PassObject = gMessageStringTable.getString("PassObject");
+ _PREHASH_Reputation = gMessageStringTable.getString("Reputation");
+ _PREHASH_IntValue = gMessageStringTable.getString("IntValue");
+ _PREHASH_TargetType = gMessageStringTable.getString("TargetType");
+ _PREHASH_Amount = gMessageStringTable.getString("Amount");
+ _PREHASH_HasAttachment = gMessageStringTable.getString("HasAttachment");
+ _PREHASH_UpdateAttachment = gMessageStringTable.getString("UpdateAttachment");
+ _PREHASH_RemoveAttachment = gMessageStringTable.getString("RemoveAttachment");
+ _PREHASH_HeightWidthBlock = gMessageStringTable.getString("HeightWidthBlock");
+ _PREHASH_RequestObjectPropertiesFamily = gMessageStringTable.getString("RequestObjectPropertiesFamily");
+ _PREHASH_ObjectPropertiesFamily = gMessageStringTable.getString("ObjectPropertiesFamily");
+ _PREHASH_UserData = gMessageStringTable.getString("UserData");
+ _PREHASH_IsReadable = gMessageStringTable.getString("IsReadable");
+ _PREHASH_PathCurve = gMessageStringTable.getString("PathCurve");
+ _PREHASH_Status = gMessageStringTable.getString("Status");
+ _PREHASH_FromGroup = gMessageStringTable.getString("FromGroup");
+ _PREHASH_AlreadyVoted = gMessageStringTable.getString("AlreadyVoted");
+ _PREHASH_PlacesReply = gMessageStringTable.getString("PlacesReply");
+ _PREHASH_DirPlacesReply = gMessageStringTable.getString("DirPlacesReply");
+ _PREHASH_ParcelBuy = gMessageStringTable.getString("ParcelBuy");
+ _PREHASH_DirFindQueryBackend = gMessageStringTable.getString("DirFindQueryBackend");
+ _PREHASH_DirPlacesQueryBackend = gMessageStringTable.getString("DirPlacesQueryBackend");
+ _PREHASH_DirClassifiedQueryBackend = gMessageStringTable.getString("DirClassifiedQueryBackend");
+ _PREHASH_DirPicksQueryBackend = gMessageStringTable.getString("DirPicksQueryBackend");
+ _PREHASH_DirLandQueryBackend = gMessageStringTable.getString("DirLandQueryBackend");
+ _PREHASH_DirPopularQueryBackend = gMessageStringTable.getString("DirPopularQueryBackend");
+ _PREHASH_LogoutDemand = gMessageStringTable.getString("LogoutDemand");
+ _PREHASH_HistoryData = gMessageStringTable.getString("HistoryData");
+ _PREHASH_SnapshotID = gMessageStringTable.getString("SnapshotID");
+ _PREHASH_Aspect = gMessageStringTable.getString("Aspect");
+ _PREHASH_ParamSize = gMessageStringTable.getString("ParamSize");
+ _PREHASH_VoteCast = gMessageStringTable.getString("VoteCast");
+ _PREHASH_CastsShadows = gMessageStringTable.getString("CastsShadows");
+ _PREHASH_EveryoneMask = gMessageStringTable.getString("EveryoneMask");
+ _PREHASH_SetSunPhase = gMessageStringTable.getString("SetSunPhase");
+ _PREHASH_ObjectSpinUpdate = gMessageStringTable.getString("ObjectSpinUpdate");
+ _PREHASH_MaturePublish = gMessageStringTable.getString("MaturePublish");
+ _PREHASH_UseExistingAsset = gMessageStringTable.getString("UseExistingAsset");
+ _PREHASH_Powers = gMessageStringTable.getString("Powers");
+ _PREHASH_ParcelLocalID = gMessageStringTable.getString("ParcelLocalID");
+ _PREHASH_TeleportCancel = gMessageStringTable.getString("TeleportCancel");
+ _PREHASH_UnixTime = gMessageStringTable.getString("UnixTime");
+ _PREHASH_QueryFlags = gMessageStringTable.getString("QueryFlags");
+ _PREHASH_LastExecFroze = gMessageStringTable.getString("LastExecFroze");
+ _PREHASH_AlwaysRun = gMessageStringTable.getString("AlwaysRun");
+ _PREHASH_Bottom = gMessageStringTable.getString("Bottom");
+ _PREHASH_ButtonData = gMessageStringTable.getString("ButtonData");
+ _PREHASH_SoundData = gMessageStringTable.getString("SoundData");
+ _PREHASH_ViewerStats = gMessageStringTable.getString("ViewerStats");
+ _PREHASH_RegionHandshake = gMessageStringTable.getString("RegionHandshake");
+ _PREHASH_ObjectDescription = gMessageStringTable.getString("ObjectDescription");
+ _PREHASH_Description = gMessageStringTable.getString("Description");
+ _PREHASH_ParamType = gMessageStringTable.getString("ParamType");
+ _PREHASH_UUIDNameReply = gMessageStringTable.getString("UUIDNameReply");
+ _PREHASH_UUIDGroupNameReply = gMessageStringTable.getString("UUIDGroupNameReply");
+ _PREHASH_SaveAssetIntoInventory = gMessageStringTable.getString("SaveAssetIntoInventory");
+ _PREHASH_UserInfo = gMessageStringTable.getString("UserInfo");
+ _PREHASH_AnimSequenceID = gMessageStringTable.getString("AnimSequenceID");
+ _PREHASH_NVPairs = gMessageStringTable.getString("NVPairs");
+ _PREHASH_GroupNoticesListRequest = gMessageStringTable.getString("GroupNoticesListRequest");
+ _PREHASH_ParcelAccessListRequest = gMessageStringTable.getString("ParcelAccessListRequest");
+ _PREHASH_MuteListRequest = gMessageStringTable.getString("MuteListRequest");
+ _PREHASH_StartPeriod = gMessageStringTable.getString("StartPeriod");
+ _PREHASH_RpcChannelRequest = gMessageStringTable.getString("RpcChannelRequest");
+ _PREHASH_LandStatRequest = gMessageStringTable.getString("LandStatRequest");
+ _PREHASH_PlacesQuery = gMessageStringTable.getString("PlacesQuery");
+ _PREHASH_DirPlacesQuery = gMessageStringTable.getString("DirPlacesQuery");
+ _PREHASH_SortOrder = gMessageStringTable.getString("SortOrder");
+ _PREHASH_Hunter = gMessageStringTable.getString("Hunter");
+ _PREHASH_SunAngVelocity = gMessageStringTable.getString("SunAngVelocity");
+ _PREHASH_BinaryBucket = gMessageStringTable.getString("BinaryBucket");
+ _PREHASH_ImagePacket = gMessageStringTable.getString("ImagePacket");
+ _PREHASH_StartGroupProposal = gMessageStringTable.getString("StartGroupProposal");
+ _PREHASH_EnergyLevel = gMessageStringTable.getString("EnergyLevel");
+ _PREHASH_PriceForListing = gMessageStringTable.getString("PriceForListing");
+ _PREHASH_Scale = gMessageStringTable.getString("Scale");
+ _PREHASH_EstateCovenantReply = gMessageStringTable.getString("EstateCovenantReply");
+ _PREHASH_ParentEstateID = gMessageStringTable.getString("ParentEstateID");
+ _PREHASH_Extra2 = gMessageStringTable.getString("Extra2");
+ _PREHASH_Throttle = gMessageStringTable.getString("Throttle");
+ _PREHASH_SimIP = gMessageStringTable.getString("SimIP");
+ _PREHASH_GodID = gMessageStringTable.getString("GodID");
+ _PREHASH_TeleportMinPrice = gMessageStringTable.getString("TeleportMinPrice");
+ _PREHASH_VoteItem = gMessageStringTable.getString("VoteItem");
+ _PREHASH_ObjectRotation = gMessageStringTable.getString("ObjectRotation");
+ _PREHASH_SitRotation = gMessageStringTable.getString("SitRotation");
+ _PREHASH_SnapSelection = gMessageStringTable.getString("SnapSelection");
+ _PREHASH_SoundTrigger = gMessageStringTable.getString("SoundTrigger");
+ _PREHASH_TerrainRaiseLimit = gMessageStringTable.getString("TerrainRaiseLimit");
+ _PREHASH_Quorum = gMessageStringTable.getString("Quorum");
+ _PREHASH_TokenBlock = gMessageStringTable.getString("TokenBlock");
+ _PREHASH_AgentBlock = gMessageStringTable.getString("AgentBlock");
+ _PREHASH_CommandBlock = gMessageStringTable.getString("CommandBlock");
+ _PREHASH_PricePublicObjectDecay = gMessageStringTable.getString("PricePublicObjectDecay");
+ _PREHASH_SpawnPointPos = gMessageStringTable.getString("SpawnPointPos");
+ _PREHASH_AttachedSoundCutoffRadius = gMessageStringTable.getString("AttachedSoundCutoffRadius");
+ _PREHASH_VolumeDetail = gMessageStringTable.getString("VolumeDetail");
+ _PREHASH_FromAgentName = gMessageStringTable.getString("FromAgentName");
+ _PREHASH_Range = gMessageStringTable.getString("Range");
+ _PREHASH_DirectoryVisibility = gMessageStringTable.getString("DirectoryVisibility");
+ _PREHASH_PublicIP = gMessageStringTable.getString("PublicIP");
+ _PREHASH_TeleportFailed = gMessageStringTable.getString("TeleportFailed");
+ _PREHASH_OnlineStatusReply = gMessageStringTable.getString("OnlineStatusReply");
+ _PREHASH_RequestAvatarInfo = gMessageStringTable.getString("RequestAvatarInfo");
+ _PREHASH_PreloadSound = gMessageStringTable.getString("PreloadSound");
+ _PREHASH_ScreenshotID = gMessageStringTable.getString("ScreenshotID");
+ _PREHASH_CovenantTimestamp = gMessageStringTable.getString("CovenantTimestamp");
+ _PREHASH_OldestUnacked = gMessageStringTable.getString("OldestUnacked");
+ _PREHASH_SimulatorIP = gMessageStringTable.getString("SimulatorIP");
+ _PREHASH_ObjectImport = gMessageStringTable.getString("ObjectImport");
+ _PREHASH_Value = gMessageStringTable.getString("Value");
+ _PREHASH_JointAxisOrAnchor = gMessageStringTable.getString("JointAxisOrAnchor");
+ _PREHASH_Test0 = gMessageStringTable.getString("Test0");
+ _PREHASH_Test1 = gMessageStringTable.getString("Test1");
+ _PREHASH_Test2 = gMessageStringTable.getString("Test2");
+ _PREHASH_SunPhase = gMessageStringTable.getString("SunPhase");
+ _PREHASH_Place = gMessageStringTable.getString("Place");
+ _PREHASH_Phase = gMessageStringTable.getString("Phase");
+ _PREHASH_ParcelDivide = gMessageStringTable.getString("ParcelDivide");
+ _PREHASH_PriceObjectClaim = gMessageStringTable.getString("PriceObjectClaim");
+ _PREHASH_Field = gMessageStringTable.getString("Field");
+ _PREHASH_Ratio = gMessageStringTable.getString("Ratio");
+ _PREHASH_JoinGroupReply = gMessageStringTable.getString("JoinGroupReply");
+ _PREHASH_LiveHelpGroupReply = gMessageStringTable.getString("LiveHelpGroupReply");
+ _PREHASH_Score = gMessageStringTable.getString("Score");
+ _PREHASH_ExpungeData = gMessageStringTable.getString("ExpungeData");
+ _PREHASH_Image = gMessageStringTable.getString("Image");
+ _PREHASH_ObjectClickAction = gMessageStringTable.getString("ObjectClickAction");
+ _PREHASH_Delta = gMessageStringTable.getString("Delta");
+ _PREHASH_InitiateUpload = gMessageStringTable.getString("InitiateUpload");
+ _PREHASH_Parameter = gMessageStringTable.getString("Parameter");
+ _PREHASH_Flags = gMessageStringTable.getString("Flags");
+ _PREHASH_Plane = gMessageStringTable.getString("Plane");
+ _PREHASH_Width = gMessageStringTable.getString("Width");
+ _PREHASH_Right = gMessageStringTable.getString("Right");
+ _PREHASH_DirFindQuery = gMessageStringTable.getString("DirFindQuery");
+ _PREHASH_Textures = gMessageStringTable.getString("Textures");
+ _PREHASH_EventData = gMessageStringTable.getString("EventData");
+ _PREHASH_Final = gMessageStringTable.getString("Final");
+ _PREHASH_TelehubPos = gMessageStringTable.getString("TelehubPos");
+ _PREHASH_ReportAutosaveCrash = gMessageStringTable.getString("ReportAutosaveCrash");
+ _PREHASH_Reset = gMessageStringTable.getString("Reset");
+ _PREHASH_CreateTrustedCircuit = gMessageStringTable.getString("CreateTrustedCircuit");
+ _PREHASH_DenyTrustedCircuit = gMessageStringTable.getString("DenyTrustedCircuit");
+ _PREHASH_RequestTrustedCircuit = gMessageStringTable.getString("RequestTrustedCircuit");
+ _PREHASH_Codec = gMessageStringTable.getString("Codec");
+ _PREHASH_Level = gMessageStringTable.getString("Level");
+ _PREHASH_Modal = gMessageStringTable.getString("Modal");
+ _PREHASH_ChildAgentUnknown = gMessageStringTable.getString("ChildAgentUnknown");
+ _PREHASH_LandingType = gMessageStringTable.getString("LandingType");
+ _PREHASH_ScriptRunningReply = gMessageStringTable.getString("ScriptRunningReply");
+ _PREHASH_MoneyDetailsReply = gMessageStringTable.getString("MoneyDetailsReply");
+ _PREHASH_Reply = gMessageStringTable.getString("Reply");
+ _PREHASH_TelehubRot = gMessageStringTable.getString("TelehubRot");
+ _PREHASH_RequestFriendship = gMessageStringTable.getString("RequestFriendship");
+ _PREHASH_AcceptFriendship = gMessageStringTable.getString("AcceptFriendship");
+ _PREHASH_GroupAccountDetailsReply = gMessageStringTable.getString("GroupAccountDetailsReply");
+ _PREHASH_DwellInfo = gMessageStringTable.getString("DwellInfo");
+ _PREHASH_AgentResume = gMessageStringTable.getString("AgentResume");
+ _PREHASH_ItemType = gMessageStringTable.getString("ItemType");
+ _PREHASH_MailFilter = gMessageStringTable.getString("MailFilter");
+ _PREHASH_Disconnect = gMessageStringTable.getString("Disconnect");
+ _PREHASH_SimPosition = gMessageStringTable.getString("SimPosition");
+ _PREHASH_SimWideTotalPrims = gMessageStringTable.getString("SimWideTotalPrims");
+ _PREHASH_Index = gMessageStringTable.getString("Index");
+ _PREHASH_BaseFilename = gMessageStringTable.getString("BaseFilename");
+ _PREHASH_SimFilename = gMessageStringTable.getString("SimFilename");
+ _PREHASH_LastOwnerID = gMessageStringTable.getString("LastOwnerID");
+ _PREHASH_GroupNoticeRequest = gMessageStringTable.getString("GroupNoticeRequest");
+ _PREHASH_EmailMessageRequest = gMessageStringTable.getString("EmailMessageRequest");
+ _PREHASH_MapItemRequest = gMessageStringTable.getString("MapItemRequest");
+ _PREHASH_AgentCount = gMessageStringTable.getString("AgentCount");
+ _PREHASH_MessageBlock = gMessageStringTable.getString("MessageBlock");
+ _PREHASH_FuseBlock = gMessageStringTable.getString("FuseBlock");
+ _PREHASH_AgentGroupData = gMessageStringTable.getString("AgentGroupData");
+ _PREHASH_ClassifiedInfoUpdate = gMessageStringTable.getString("ClassifiedInfoUpdate");
+ _PREHASH_RegionPos = gMessageStringTable.getString("RegionPos");
+ _PREHASH_ParcelMediaUpdate = gMessageStringTable.getString("ParcelMediaUpdate");
+ _PREHASH_NoticeID = gMessageStringTable.getString("NoticeID");
+ _PREHASH_GridX = gMessageStringTable.getString("GridX");
+ _PREHASH_GridY = gMessageStringTable.getString("GridY");
+ _PREHASH_Title = gMessageStringTable.getString("Title");
+ _PREHASH_AuctionID = gMessageStringTable.getString("AuctionID");
+ _PREHASH_VoteType = gMessageStringTable.getString("VoteType");
+ _PREHASH_CategoryID = gMessageStringTable.getString("CategoryID");
+ _PREHASH_Token = gMessageStringTable.getString("Token");
+ _PREHASH_AggregatePerms = gMessageStringTable.getString("AggregatePerms");
+ _PREHASH_StartParcelRemoveAck = gMessageStringTable.getString("StartParcelRemoveAck");
+ _PREHASH_ObjectSelect = gMessageStringTable.getString("ObjectSelect");
+ _PREHASH_ForceObjectSelect = gMessageStringTable.getString("ForceObjectSelect");
+ _PREHASH_Price = gMessageStringTable.getString("Price");
+ _PREHASH_SunDirection = gMessageStringTable.getString("SunDirection");
+ _PREHASH_FromName = gMessageStringTable.getString("FromName");
+ _PREHASH_ChangeInventoryItemFlags = gMessageStringTable.getString("ChangeInventoryItemFlags");
+ _PREHASH_Force = gMessageStringTable.getString("Force");
+ _PREHASH_TransactionBlock = gMessageStringTable.getString("TransactionBlock");
+ _PREHASH_PowersMask = gMessageStringTable.getString("PowersMask");
+ _PREHASH_Stamp = gMessageStringTable.getString("Stamp");
+ _PREHASH_TotalCredits = gMessageStringTable.getString("TotalCredits");
+ _PREHASH_State = gMessageStringTable.getString("State");
+ _PREHASH_TextureIndex = gMessageStringTable.getString("TextureIndex");
+ _PREHASH_InviteeID = gMessageStringTable.getString("InviteeID");
+ _PREHASH_ParcelReclaim = gMessageStringTable.getString("ParcelReclaim");
+ _PREHASH_Money = gMessageStringTable.getString("Money");
+ _PREHASH_PathTwist = gMessageStringTable.getString("PathTwist");
+ _PREHASH_AuthBuyerID = gMessageStringTable.getString("AuthBuyerID");
+ _PREHASH_Color = gMessageStringTable.getString("Color");
+ _PREHASH_SourceType = gMessageStringTable.getString("SourceType");
+ _PREHASH_World = gMessageStringTable.getString("World");
+ _PREHASH_QueryData = gMessageStringTable.getString("QueryData");
+ _PREHASH_Users = gMessageStringTable.getString("Users");
+ _PREHASH_SysOS = gMessageStringTable.getString("SysOS");
+ _PREHASH_Notes = gMessageStringTable.getString("Notes");
+ _PREHASH_AvatarID = gMessageStringTable.getString("AvatarID");
+ _PREHASH_FounderID = gMessageStringTable.getString("FounderID");
+ _PREHASH_EndPointID = gMessageStringTable.getString("EndPointID");
+ _PREHASH_StipendEstimate = gMessageStringTable.getString("StipendEstimate");
+ _PREHASH_LocationLookAt = gMessageStringTable.getString("LocationLookAt");
+ _PREHASH_Sound = gMessageStringTable.getString("Sound");
+ _PREHASH_Cover = gMessageStringTable.getString("Cover");
+ _PREHASH_TotalObjectCount = gMessageStringTable.getString("TotalObjectCount");
+ _PREHASH_TextureEntry = gMessageStringTable.getString("TextureEntry");
+ _PREHASH_SquareMetersCommitted = gMessageStringTable.getString("SquareMetersCommitted");
+ _PREHASH_ChannelID = gMessageStringTable.getString("ChannelID");
+ _PREHASH_Dwell = gMessageStringTable.getString("Dwell");
+ _PREHASH_North = gMessageStringTable.getString("North");
+ _PREHASH_AgentUpdate = gMessageStringTable.getString("AgentUpdate");
+ _PREHASH_PickGodDelete = gMessageStringTable.getString("PickGodDelete");
+ _PREHASH_HostName = gMessageStringTable.getString("HostName");
+ _PREHASH_PriceParcelClaim = gMessageStringTable.getString("PriceParcelClaim");
+ _PREHASH_ParcelClaim = gMessageStringTable.getString("ParcelClaim");
+ _PREHASH_AgentPowers = gMessageStringTable.getString("AgentPowers");
+ _PREHASH_ProfileHollow = gMessageStringTable.getString("ProfileHollow");
+ _PREHASH_GroupRoleChanges = gMessageStringTable.getString("GroupRoleChanges");
+ _PREHASH_Count = gMessageStringTable.getString("Count");
+ _PREHASH_South = gMessageStringTable.getString("South");
+ _PREHASH_Entry = gMessageStringTable.getString("Entry");
+ _PREHASH_ObjectUpdateCompressed = gMessageStringTable.getString("ObjectUpdateCompressed");
+ _PREHASH_MuteFlags = gMessageStringTable.getString("MuteFlags");
+ _PREHASH_Group = gMessageStringTable.getString("Group");
+ _PREHASH_AgentPause = gMessageStringTable.getString("AgentPause");
+ _PREHASH_LanguagesText = gMessageStringTable.getString("LanguagesText");
+ _PREHASH_InternalScriptMail = gMessageStringTable.getString("InternalScriptMail");
+ _PREHASH_FindAgent = gMessageStringTable.getString("FindAgent");
+ _PREHASH_AgentData = gMessageStringTable.getString("AgentData");
+ _PREHASH_FolderData = gMessageStringTable.getString("FolderData");
+ _PREHASH_AssetBlock = gMessageStringTable.getString("AssetBlock");
+ _PREHASH_AcceptNotices = gMessageStringTable.getString("AcceptNotices");
+ _PREHASH_SetGroupAcceptNotices = gMessageStringTable.getString("SetGroupAcceptNotices");
+ _PREHASH_CloseCircuit = gMessageStringTable.getString("CloseCircuit");
+ _PREHASH_LogControl = gMessageStringTable.getString("LogControl");
+ _PREHASH_TeleportFinish = gMessageStringTable.getString("TeleportFinish");
+ _PREHASH_PathRevolutions = gMessageStringTable.getString("PathRevolutions");
+ _PREHASH_ClassifiedInfoReply = gMessageStringTable.getString("ClassifiedInfoReply");
+ _PREHASH_ParcelInfoReply = gMessageStringTable.getString("ParcelInfoReply");
+ _PREHASH_AutosaveData = gMessageStringTable.getString("AutosaveData");
+ _PREHASH_SetStartLocation = gMessageStringTable.getString("SetStartLocation");
+ _PREHASH_PassHours = gMessageStringTable.getString("PassHours");
+ _PREHASH_AttachmentPt = gMessageStringTable.getString("AttachmentPt");
+ _PREHASH_ParcelFlags = gMessageStringTable.getString("ParcelFlags");
+ _PREHASH_NumVotes = gMessageStringTable.getString("NumVotes");
+ _PREHASH_AvatarPickerRequest = gMessageStringTable.getString("AvatarPickerRequest");
+ _PREHASH_TeleportLocationRequest = gMessageStringTable.getString("TeleportLocationRequest");
+ _PREHASH_DataHomeLocationRequest = gMessageStringTable.getString("DataHomeLocationRequest");
+ _PREHASH_EventNotificationAddRequest = gMessageStringTable.getString("EventNotificationAddRequest");
+ _PREHASH_ParcelDwellRequest = gMessageStringTable.getString("ParcelDwellRequest");
+ _PREHASH_EventLocationRequest = gMessageStringTable.getString("EventLocationRequest");
+ _PREHASH_EndPeriod = gMessageStringTable.getString("EndPeriod");
+ _PREHASH_SetStartLocationRequest = gMessageStringTable.getString("SetStartLocationRequest");
+ _PREHASH_QueryStart = gMessageStringTable.getString("QueryStart");
+ _PREHASH_EjectData = gMessageStringTable.getString("EjectData");
+ _PREHASH_AvatarTextureUpdate = gMessageStringTable.getString("AvatarTextureUpdate");
+ _PREHASH_RPCServerPort = gMessageStringTable.getString("RPCServerPort");
+ _PREHASH_Bytes = gMessageStringTable.getString("Bytes");
+ _PREHASH_Extra = gMessageStringTable.getString("Extra");
+ _PREHASH_ForceScriptControlRelease = gMessageStringTable.getString("ForceScriptControlRelease");
+ _PREHASH_ParcelRelease = gMessageStringTable.getString("ParcelRelease");
+ _PREHASH_VFileType = gMessageStringTable.getString("VFileType");
+ _PREHASH_EjectGroupMemberReply = gMessageStringTable.getString("EjectGroupMemberReply");
+ _PREHASH_ImageData = gMessageStringTable.getString("ImageData");
+ _PREHASH_SpaceServerSimulatorTimeMessage = gMessageStringTable.getString("SpaceServerSimulatorTimeMessage");
+ _PREHASH_SimulatorViewerTimeMessage = gMessageStringTable.getString("SimulatorViewerTimeMessage");
+ _PREHASH_Rotation = gMessageStringTable.getString("Rotation");
+ _PREHASH_Selection = gMessageStringTable.getString("Selection");
+ _PREHASH_TransactionData = gMessageStringTable.getString("TransactionData");
+ _PREHASH_OperationData = gMessageStringTable.getString("OperationData");
+ _PREHASH_ExpirationDate = gMessageStringTable.getString("ExpirationDate");
+ _PREHASH_ParcelDeedToGroup = gMessageStringTable.getString("ParcelDeedToGroup");
+ _PREHASH_DirPicksReply = gMessageStringTable.getString("DirPicksReply");
+ _PREHASH_AvatarPicksReply = gMessageStringTable.getString("AvatarPicksReply");
+ _PREHASH_GroupTitlesReply = gMessageStringTable.getString("GroupTitlesReply");
+ _PREHASH_AgentInfo = gMessageStringTable.getString("AgentInfo");
+ _PREHASH_MoneyTransferBackend = gMessageStringTable.getString("MoneyTransferBackend");
+ _PREHASH_NextOwnerMask = gMessageStringTable.getString("NextOwnerMask");
+ _PREHASH_MuteData = gMessageStringTable.getString("MuteData");
+ _PREHASH_PassPrice = gMessageStringTable.getString("PassPrice");
+ _PREHASH_SourceID = gMessageStringTable.getString("SourceID");
+ _PREHASH_ChangeUserRights = gMessageStringTable.getString("ChangeUserRights");
+ _PREHASH_TeleportFlags = gMessageStringTable.getString("TeleportFlags");
+ _PREHASH_AssetData = gMessageStringTable.getString("AssetData");
+ _PREHASH_SlaveParcelData = gMessageStringTable.getString("SlaveParcelData");
+ _PREHASH_MultipleObjectUpdate = gMessageStringTable.getString("MultipleObjectUpdate");
+ _PREHASH_ObjectUpdate = gMessageStringTable.getString("ObjectUpdate");
+ _PREHASH_ImprovedTerseObjectUpdate = gMessageStringTable.getString("ImprovedTerseObjectUpdate");
+ _PREHASH_ConfirmXferPacket = gMessageStringTable.getString("ConfirmXferPacket");
+ _PREHASH_StartPingCheck = gMessageStringTable.getString("StartPingCheck");
+ _PREHASH_SimWideDeletes = gMessageStringTable.getString("SimWideDeletes");
+ _PREHASH_LandStatReply = gMessageStringTable.getString("LandStatReply");
+ _PREHASH_IsPhantom = gMessageStringTable.getString("IsPhantom");
+ _PREHASH_AgentList = gMessageStringTable.getString("AgentList");
+ _PREHASH_SimApproved = gMessageStringTable.getString("SimApproved");
+ _PREHASH_RezObject = gMessageStringTable.getString("RezObject");
+ _PREHASH_TaskLocalID = gMessageStringTable.getString("TaskLocalID");
+ _PREHASH_ClaimDate = gMessageStringTable.getString("ClaimDate");
+ _PREHASH_MergeParcel = gMessageStringTable.getString("MergeParcel");
+ _PREHASH_Priority = gMessageStringTable.getString("Priority");
+ _PREHASH_Building = gMessageStringTable.getString("Building");
+ _PREHASH_QueryText = gMessageStringTable.getString("QueryText");
+ _PREHASH_GroupNoticeAdd = gMessageStringTable.getString("GroupNoticeAdd");
+ _PREHASH_ReturnType = gMessageStringTable.getString("ReturnType");
+ _PREHASH_FetchFolders = gMessageStringTable.getString("FetchFolders");
+ _PREHASH_SimulatorPublicHostBlock = gMessageStringTable.getString("SimulatorPublicHostBlock");
+ _PREHASH_HeaderData = gMessageStringTable.getString("HeaderData");
+ _PREHASH_RequestMultipleObjects = gMessageStringTable.getString("RequestMultipleObjects");
+ _PREHASH_RetrieveInstantMessages = gMessageStringTable.getString("RetrieveInstantMessages");
+ _PREHASH_DequeueInstantMessages = gMessageStringTable.getString("DequeueInstantMessages");
+ _PREHASH_OpenCircuit = gMessageStringTable.getString("OpenCircuit");
+ _PREHASH_SecureSessionID = gMessageStringTable.getString("SecureSessionID");
+ _PREHASH_CrossedRegion = gMessageStringTable.getString("CrossedRegion");
+ _PREHASH_DirGroupsReply = gMessageStringTable.getString("DirGroupsReply");
+ _PREHASH_AvatarGroupsReply = gMessageStringTable.getString("AvatarGroupsReply");
+ _PREHASH_EmailMessageReply = gMessageStringTable.getString("EmailMessageReply");
+ _PREHASH_GroupVoteHistoryItemReply = gMessageStringTable.getString("GroupVoteHistoryItemReply");
+ _PREHASH_ViewerPosition = gMessageStringTable.getString("ViewerPosition");
+ _PREHASH_Position = gMessageStringTable.getString("Position");
+ _PREHASH_ParentEstate = gMessageStringTable.getString("ParentEstate");
+ _PREHASH_EstateName = gMessageStringTable.getString("EstateName");
+ _PREHASH_MuteName = gMessageStringTable.getString("MuteName");
+ _PREHASH_StartParcelRename = gMessageStringTable.getString("StartParcelRename");
+ _PREHASH_BulkParcelRename = gMessageStringTable.getString("BulkParcelRename");
+ _PREHASH_ParcelRename = gMessageStringTable.getString("ParcelRename");
+ _PREHASH_ViewerFilename = gMessageStringTable.getString("ViewerFilename");
+ _PREHASH_Positive = gMessageStringTable.getString("Positive");
+ _PREHASH_UserReportInternal = gMessageStringTable.getString("UserReportInternal");
+ _PREHASH_AvatarPropertiesRequest = gMessageStringTable.getString("AvatarPropertiesRequest");
+ _PREHASH_ParcelPropertiesRequest = gMessageStringTable.getString("ParcelPropertiesRequest");
+ _PREHASH_GroupProfileRequest = gMessageStringTable.getString("GroupProfileRequest");
+ _PREHASH_AgentDataUpdateRequest = gMessageStringTable.getString("AgentDataUpdateRequest");
+ _PREHASH_PriceObjectScaleFactor = gMessageStringTable.getString("PriceObjectScaleFactor");
+ _PREHASH_DirPicksQuery = gMessageStringTable.getString("DirPicksQuery");
+ _PREHASH_OpenEnrollment = gMessageStringTable.getString("OpenEnrollment");
+ _PREHASH_GroupData = gMessageStringTable.getString("GroupData");
+ _PREHASH_RequestGodlikePowers = gMessageStringTable.getString("RequestGodlikePowers");
+ _PREHASH_GrantGodlikePowers = gMessageStringTable.getString("GrantGodlikePowers");
+ _PREHASH_TransactionID = gMessageStringTable.getString("TransactionID");
+ _PREHASH_DestinationID = gMessageStringTable.getString("DestinationID");
+ _PREHASH_Controls = gMessageStringTable.getString("Controls");
+ _PREHASH_FirstDetachAll = gMessageStringTable.getString("FirstDetachAll");
+ _PREHASH_EstateID = gMessageStringTable.getString("EstateID");
+ _PREHASH_ImprovedInstantMessage = gMessageStringTable.getString("ImprovedInstantMessage");
+ _PREHASH_AgentQuit = gMessageStringTable.getString("AgentQuit");
+ _PREHASH_CheckParcelSales = gMessageStringTable.getString("CheckParcelSales");
+ _PREHASH_ParcelSales = gMessageStringTable.getString("ParcelSales");
+ _PREHASH_CurrentInterval = gMessageStringTable.getString("CurrentInterval");
+ _PREHASH_PriceRentLight = gMessageStringTable.getString("PriceRentLight");
+ _PREHASH_MediaAutoScale = gMessageStringTable.getString("MediaAutoScale");
+ _PREHASH_NeighborBlock = gMessageStringTable.getString("NeighborBlock");
+ _PREHASH_LayerData = gMessageStringTable.getString("LayerData");
+ _PREHASH_NVPairData = gMessageStringTable.getString("NVPairData");
+ _PREHASH_TeleportLocal = gMessageStringTable.getString("TeleportLocal");
+ _PREHASH_EjecteeID = gMessageStringTable.getString("EjecteeID");
+ _PREHASH_VoteInitiator = gMessageStringTable.getString("VoteInitiator");
+ _PREHASH_TypeData = gMessageStringTable.getString("TypeData");
+ _PREHASH_OwnerIDs = gMessageStringTable.getString("OwnerIDs");
+ _PREHASH_SystemKickUser = gMessageStringTable.getString("SystemKickUser");
+ _PREHASH_TransactionTime = gMessageStringTable.getString("TransactionTime");
+ _PREHASH_TimeToLive = gMessageStringTable.getString("TimeToLive");
+ _PREHASH_StartParcelRemove = gMessageStringTable.getString("StartParcelRemove");
+ _PREHASH_BulkParcelRemove = gMessageStringTable.getString("BulkParcelRemove");
+ _PREHASH_OldAgentID = gMessageStringTable.getString("OldAgentID");
+ _PREHASH_BonusEstimate = gMessageStringTable.getString("BonusEstimate");
+ _PREHASH_MusicURL = gMessageStringTable.getString("MusicURL");
+ _PREHASH_CompleteLure = gMessageStringTable.getString("CompleteLure");
+ _PREHASH_ParcelPrimBonus = gMessageStringTable.getString("ParcelPrimBonus");
+ _PREHASH_EjectUser = gMessageStringTable.getString("EjectUser");
+ _PREHASH_CoarseLocationUpdate = gMessageStringTable.getString("CoarseLocationUpdate");
+ _PREHASH_ChildAgentPositionUpdate = gMessageStringTable.getString("ChildAgentPositionUpdate");
+ _PREHASH_StoreLocal = gMessageStringTable.getString("StoreLocal");
+ _PREHASH_GroupName = gMessageStringTable.getString("GroupName");
+ _PREHASH_PriceParcelRent = gMessageStringTable.getString("PriceParcelRent");
+ _PREHASH_SimStatus = gMessageStringTable.getString("SimStatus");
+ _PREHASH_TransactionSuccess = gMessageStringTable.getString("TransactionSuccess");
+ _PREHASH_LureType = gMessageStringTable.getString("LureType");
+ _PREHASH_GroupMask = gMessageStringTable.getString("GroupMask");
+ _PREHASH_SitObject = gMessageStringTable.getString("SitObject");
+ _PREHASH_Override = gMessageStringTable.getString("Override");
+ _PREHASH_LocomotionState = gMessageStringTable.getString("LocomotionState");
+ _PREHASH_PriceUpload = gMessageStringTable.getString("PriceUpload");
+ _PREHASH_RemoveParcel = gMessageStringTable.getString("RemoveParcel");
+ _PREHASH_ConfirmAuctionStart = gMessageStringTable.getString("ConfirmAuctionStart");
+ _PREHASH_RpcScriptRequestInbound = gMessageStringTable.getString("RpcScriptRequestInbound");
+ _PREHASH_ActiveGroupID = gMessageStringTable.getString("ActiveGroupID");
+ _PREHASH_ParcelReturnObjects = gMessageStringTable.getString("ParcelReturnObjects");
+ _PREHASH_TotalObjects = gMessageStringTable.getString("TotalObjects");
+ _PREHASH_ObjectExtraParams = gMessageStringTable.getString("ObjectExtraParams");
+ _PREHASH_Questions = gMessageStringTable.getString("Questions");
+ _PREHASH_TransferAbort = gMessageStringTable.getString("TransferAbort");
+ _PREHASH_TransferInventory = gMessageStringTable.getString("TransferInventory");
+ _PREHASH_RayTargetID = gMessageStringTable.getString("RayTargetID");
+ _PREHASH_ClaimPrice = gMessageStringTable.getString("ClaimPrice");
+ _PREHASH_ObjectProperties = gMessageStringTable.getString("ObjectProperties");
+ _PREHASH_ParcelProperties = gMessageStringTable.getString("ParcelProperties");
+ _PREHASH_EstateOwnerID = gMessageStringTable.getString("EstateOwnerID");
+ _PREHASH_LogoutRequest = gMessageStringTable.getString("LogoutRequest");
+ _PREHASH_AssetUploadRequest = gMessageStringTable.getString("AssetUploadRequest");
+ _PREHASH_ReputationIndividualRequest = gMessageStringTable.getString("ReputationIndividualRequest");
+ _PREHASH_MajorVersion = gMessageStringTable.getString("MajorVersion");
+ _PREHASH_MinorVersion = gMessageStringTable.getString("MinorVersion");
+ _PREHASH_SimulatorAssign = gMessageStringTable.getString("SimulatorAssign");
+ _PREHASH_TransactionType = gMessageStringTable.getString("TransactionType");
+ _PREHASH_AvatarPropertiesUpdate = gMessageStringTable.getString("AvatarPropertiesUpdate");
+ _PREHASH_ParcelPropertiesUpdate = gMessageStringTable.getString("ParcelPropertiesUpdate");
+ _PREHASH_FetchItems = gMessageStringTable.getString("FetchItems");
+ _PREHASH_AbortXfer = gMessageStringTable.getString("AbortXfer");
+ _PREHASH_DeRezAck = gMessageStringTable.getString("DeRezAck");
+ _PREHASH_TakeControls = gMessageStringTable.getString("TakeControls");
+ _PREHASH_DirLandReply = gMessageStringTable.getString("DirLandReply");
+ _PREHASH_SpaceLocationTeleportReply = gMessageStringTable.getString("SpaceLocationTeleportReply");
+ _PREHASH_MuteType = gMessageStringTable.getString("MuteType");
+ _PREHASH_IMViaEMail = gMessageStringTable.getString("IMViaEMail");
+ _PREHASH_StartExpungeProcessAck = gMessageStringTable.getString("StartExpungeProcessAck");
+ _PREHASH_RentPrice = gMessageStringTable.getString("RentPrice");
+ _PREHASH_GenericMessage = gMessageStringTable.getString("GenericMessage");
+ _PREHASH_ChildAgentAlive = gMessageStringTable.getString("ChildAgentAlive");
+ _PREHASH_AssetType = gMessageStringTable.getString("AssetType");
+ _PREHASH_SpawnPointBlock = gMessageStringTable.getString("SpawnPointBlock");
+ _PREHASH_AttachmentBlock = gMessageStringTable.getString("AttachmentBlock");
+ _PREHASH_ObjectMaterial = gMessageStringTable.getString("ObjectMaterial");
+ _PREHASH_OwnerName = gMessageStringTable.getString("OwnerName");
+ _PREHASH_AvatarNotesReply = gMessageStringTable.getString("AvatarNotesReply");
+ _PREHASH_CacheID = gMessageStringTable.getString("CacheID");
+ _PREHASH_OwnerMask = gMessageStringTable.getString("OwnerMask");
+ _PREHASH_TransferInventoryAck = gMessageStringTable.getString("TransferInventoryAck");
+}
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
new file mode 100644
index 0000000000..0da2e02f83
--- /dev/null
+++ b/indra/llmessage/message_prehash.h
@@ -0,0 +1,1498 @@
+/**
+ * @file message_prehash.h
+ * @brief header file of externs of prehashed variables plus defines.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MESSAGE_PREHASH_H
+#define LL_MESSAGE_PREHASH_H
+
+/**
+ * Generated from message template version number 1.053
+ */
+
+
+extern F32 gPrehashVersionNumber;
+
+extern char * _PREHASH_X;
+extern char * _PREHASH_Y;
+extern char * _PREHASH_Z;
+extern char * _PREHASH_AddFlags;
+extern char * _PREHASH_ReservedNewbie;
+extern char * _PREHASH_FailureInfo;
+extern char * _PREHASH_MapData;
+extern char * _PREHASH_AddItem;
+extern char * _PREHASH_MeanCollision;
+extern char * _PREHASH_RezScript;
+extern char * _PREHASH_AvatarSitResponse;
+extern char * _PREHASH_InventoryAssetResponse;
+extern char * _PREHASH_KillObject;
+extern char * _PREHASH_ProposalID;
+extern char * _PREHASH_SerialNum;
+extern char * _PREHASH_Duration;
+extern char * _PREHASH_ScriptQuestion;
+extern char * _PREHASH_AddCircuitCode;
+extern char * _PREHASH_UseCircuitCode;
+extern char * _PREHASH_ViewerCircuitCode;
+extern char * _PREHASH_ScriptAnswerYes;
+extern char * _PREHASH_PartnerID;
+extern char * _PREHASH_DirLandQuery;
+extern char * _PREHASH_TeleportStart;
+extern char * _PREHASH_LogMessages;
+extern char * _PREHASH_AboutText;
+extern char * _PREHASH_VisualParam;
+extern char * _PREHASH_GroupPrims;
+extern char * _PREHASH_SelectedPrims;
+extern char * _PREHASH_ID;
+extern char * _PREHASH_UUIDNameRequest;
+extern char * _PREHASH_UUIDGroupNameRequest;
+extern char * _PREHASH_MoneyTransactionsRequest;
+extern char * _PREHASH_GroupAccountTransactionsRequest;
+extern char * _PREHASH_MapNameRequest;
+extern char * _PREHASH_MailTaskSimRequest;
+extern char * _PREHASH_UpdateSimulator;
+extern char * _PREHASH_BillableFactor;
+extern char * _PREHASH_ObjectBonusFactor;
+extern char * _PREHASH_EnableSimulator;
+extern char * _PREHASH_DisableSimulator;
+extern char * _PREHASH_ConfirmEnableSimulator;
+extern char * _PREHASH_LayerType;
+extern char * _PREHASH_OwnerRole;
+extern char * _PREHASH_ParcelOverlay;
+extern char * _PREHASH_AdjustBalance;
+extern char * _PREHASH_GroupOwned;
+extern char * _PREHASH_IP;
+extern char * _PREHASH_ChatFromViewer;
+extern char * _PREHASH_AvgAgentsInView;
+extern char * _PREHASH_AgentsInView;
+extern char * _PREHASH_GroupTitle;
+extern char * _PREHASH_MapLayerReply;
+extern char * _PREHASH_CompoundMsgID;
+extern char * _PREHASH_CameraConstraint;
+extern char * _PREHASH_DownloadTotals;
+extern char * _PREHASH_GenCounter;
+extern char * _PREHASH_FrozenData;
+extern char * _PREHASH_ChildAgentDying;
+extern char * _PREHASH_To;
+extern char * _PREHASH_CopyInventoryFromNotecard;
+extern char * _PREHASH_RezObjectFromNotecard;
+extern char * _PREHASH_ParcelDirFeeCurrent;
+extern char * _PREHASH_SeedCapability;
+extern char * _PREHASH_ObjectDuplicate;
+extern char * _PREHASH_InventoryData;
+extern char * _PREHASH_ReplyData;
+extern char * _PREHASH_ResetList;
+extern char * _PREHASH_MediaID;
+extern char * _PREHASH_RelatedRights;
+extern char * _PREHASH_RedirectGridX;
+extern char * _PREHASH_RedirectGridY;
+extern char * _PREHASH_TransferID;
+extern char * _PREHASH_Transacted;
+extern char * _PREHASH_TexturesChanged;
+extern char * _PREHASH_UserLookAt;
+extern char * _PREHASH_TestBlock1;
+extern char * _PREHASH_SensedData;
+extern char * _PREHASH_UpdateBlock;
+extern char * _PREHASH_ClassifiedGodDelete;
+extern char * _PREHASH_ObjectGrabUpdate;
+extern char * _PREHASH_TaxDate;
+extern char * _PREHASH_LocationPos;
+extern char * _PREHASH_StartDateTime;
+extern char * _PREHASH_ObjectUpdateCached;
+extern char * _PREHASH_Packets;
+extern char * _PREHASH_FailureType;
+extern char * _PREHASH_UpdateGroupInfo;
+extern char * _PREHASH_ObjectPermissions;
+extern char * _PREHASH_RevokePermissions;
+extern char * _PREHASH_UpdateFlags;
+extern char * _PREHASH_ObjectExportSelected;
+extern char * _PREHASH_RezSelected;
+extern char * _PREHASH_AutoPilot;
+extern char * _PREHASH_UpdateMuteListEntry;
+extern char * _PREHASH_RemoveMuteListEntry;
+extern char * _PREHASH_SetSimStatusInDatabase;
+extern char * _PREHASH_SetSimPresenceInDatabase;
+extern char * _PREHASH_CameraProperty;
+extern char * _PREHASH_BrushSize;
+extern char * _PREHASH_StartExpungeProcess;
+extern char * _PREHASH_SimulatorSetMap;
+extern char * _PREHASH_RegionPresenceRequestByRegionID;
+extern char * _PREHASH_ParcelObjectOwnersReply;
+extern char * _PREHASH_GroupMembersReply;
+extern char * _PREHASH_GroupRoleMembersReply;
+extern char * _PREHASH_RequestRegionInfo;
+extern char * _PREHASH_AABBMax;
+extern char * _PREHASH_RequestPayPrice;
+extern char * _PREHASH_SimulatorPresentAtLocation;
+extern char * _PREHASH_AgentRequestSit;
+extern char * _PREHASH_AABBMin;
+extern char * _PREHASH_ClassifiedFlags;
+extern char * _PREHASH_ControlFlags;
+extern char * _PREHASH_TeleportRequest;
+extern char * _PREHASH_SpaceLocationTeleportRequest;
+extern char * _PREHASH_LeaderBoardRequest;
+extern char * _PREHASH_ScriptTeleportRequest;
+extern char * _PREHASH_DateUTC;
+extern char * _PREHASH_TaskIDs;
+extern char * _PREHASH_EstateCovenantRequest;
+extern char * _PREHASH_RequestResult;
+extern char * _PREHASH_ReputationAgentAssign;
+extern char * _PREHASH_CanAcceptAgents;
+extern char * _PREHASH_ObjectSaleInfo;
+extern char * _PREHASH_KillChildAgents;
+extern char * _PREHASH_Balance;
+extern char * _PREHASH_DerezContainer;
+extern char * _PREHASH_ObjectData;
+extern char * _PREHASH_CameraAtAxis;
+extern char * _PREHASH_InfoBlock;
+extern char * _PREHASH_OwnershipCost;
+extern char * _PREHASH_AvatarNotesUpdate;
+extern char * _PREHASH_PID;
+extern char * _PREHASH_TimeString;
+extern char * _PREHASH_DirPopularReply;
+extern char * _PREHASH_TerrainHeightRange00;
+extern char * _PREHASH_SimData;
+extern char * _PREHASH_TerrainHeightRange01;
+extern char * _PREHASH_TerrainHeightRange10;
+extern char * _PREHASH_TerrainHeightRange11;
+extern char * _PREHASH_UpdateInventoryItem;
+extern char * _PREHASH_UpdateCreateInventoryItem;
+extern char * _PREHASH_MoveInventoryItem;
+extern char * _PREHASH_CopyInventoryItem;
+extern char * _PREHASH_RemoveInventoryItem;
+extern char * _PREHASH_CreateInventoryItem;
+extern char * _PREHASH_PathTwistBegin;
+extern char * _PREHASH_CRC;
+extern char * _PREHASH_AttachmentPoint;
+extern char * _PREHASH_TelehubBlock;
+extern char * _PREHASH_FOVBlock;
+extern char * _PREHASH_StartLocationData;
+extern char * _PREHASH_PositionData;
+extern char * _PREHASH_TimeSinceLast;
+extern char * _PREHASH_MapImage;
+extern char * _PREHASH_Objects;
+extern char * _PREHASH_URL;
+extern char * _PREHASH_CreationDate;
+extern char * _PREHASH_JointPivot;
+extern char * _PREHASH_RateeID;
+extern char * _PREHASH_FPS;
+extern char * _PREHASH_HasTelehub;
+extern char * _PREHASH_PathEnd;
+extern char * _PREHASH_ScriptDataReply;
+extern char * _PREHASH_MapBlockReply;
+extern char * _PREHASH_PropertiesData;
+extern char * _PREHASH_ViewerEffect;
+extern char * _PREHASH_FreezeUser;
+extern char * _PREHASH_OwnerPrims;
+extern char * _PREHASH_ObjectGrab;
+extern char * _PREHASH_ToAgentID;
+extern char * _PREHASH_SimulatorMapUpdate;
+extern char * _PREHASH_TransferPacket;
+extern char * _PREHASH_ObjectName;
+extern char * _PREHASH_GroupPowers;
+extern char * _PREHASH_OriginalName;
+extern char * _PREHASH_CompletePingCheck;
+extern char * _PREHASH_OnlineStatus;
+extern char * _PREHASH_ObjectDrop;
+extern char * _PREHASH_UseBigPackets;
+extern char * _PREHASH_GroupNoticesListReply;
+extern char * _PREHASH_ParcelAccessListReply;
+extern char * _PREHASH_RpcChannelReply;
+extern char * _PREHASH_RegionPresenceResponse;
+extern char * _PREHASH_AgentPresenceResponse;
+extern char * _PREHASH_CharterMember;
+extern char * _PREHASH_EdgeData;
+extern char * _PREHASH_NameData;
+extern char * _PREHASH_RegionPushOverride;
+extern char * _PREHASH_SimName;
+extern char * _PREHASH_UserReport;
+extern char * _PREHASH_DownloadPriority;
+extern char * _PREHASH_ToAgentId;
+extern char * _PREHASH_Mag;
+extern char * _PREHASH_DirPopularQuery;
+extern char * _PREHASH_ParcelPropertiesRequestByID;
+extern char * _PREHASH_ObjectLink;
+extern char * _PREHASH_RpcScriptReplyInbound;
+extern char * _PREHASH_BoardData;
+extern char * _PREHASH_RezData;
+extern char * _PREHASH_RemoveInventoryObjects;
+extern char * _PREHASH_GroupProposalBallot;
+extern char * _PREHASH_RPCServerIP;
+extern char * _PREHASH_Far;
+extern char * _PREHASH_GodSessionID;
+extern char * _PREHASH_ViewerDigest;
+extern char * _PREHASH_FLAboutText;
+extern char * _PREHASH_RegionHandshakeReply;
+extern char * _PREHASH_GroupActiveProposalItemReply;
+extern char * _PREHASH_MapItemReply;
+extern char * _PREHASH_Seconds;
+extern char * _PREHASH_UpdateUserInfo;
+extern char * _PREHASH_AggregatePermTexturesOwner;
+extern char * _PREHASH_Set;
+extern char * _PREHASH_NewName;
+extern char * _PREHASH_Key;
+extern char * _PREHASH_AgentID;
+extern char * _PREHASH_OnlineStatusRequest;
+extern char * _PREHASH_EventNotificationRemoveRequest;
+extern char * _PREHASH_NewFolderID;
+extern char * _PREHASH_Arc;
+extern char * _PREHASH_RegionX;
+extern char * _PREHASH_RegionY;
+extern char * _PREHASH_RequestData;
+extern char * _PREHASH_Msg;
+extern char * _PREHASH_Top;
+extern char * _PREHASH_MiscStats;
+extern char * _PREHASH_ImageID;
+extern char * _PREHASH_DataPacket;
+extern char * _PREHASH_ObjectDehinge;
+extern char * _PREHASH_You;
+extern char * _PREHASH_ScriptControlChange;
+extern char * _PREHASH_LoadURL;
+extern char * _PREHASH_SetCPURatio;
+extern char * _PREHASH_NameValueData;
+extern char * _PREHASH_AtomicPassObject;
+extern char * _PREHASH_ErrorMessage;
+extern char * _PREHASH_ViewerFrozenMessage;
+extern char * _PREHASH_HealthMessage;
+extern char * _PREHASH_LogTextMessage;
+extern char * _PREHASH_TimeDilation;
+extern char * _PREHASH_RemoveContribution;
+extern char * _PREHASH_Contribution;
+extern char * _PREHASH_SetGroupContribution;
+extern char * _PREHASH_Offline;
+extern char * _PREHASH_AgentIsNowWearing;
+extern char * _PREHASH_SecPerDay;
+extern char * _PREHASH_Members;
+extern char * _PREHASH_FailedResends;
+extern char * _PREHASH_CameraCenter;
+extern char * _PREHASH_CameraLeftAxis;
+extern char * _PREHASH_ExBlock;
+extern char * _PREHASH_Channel;
+extern char * _PREHASH_NetTest;
+extern char * _PREHASH_DiscardLevel;
+extern char * _PREHASH_LayerID;
+extern char * _PREHASH_RatorID;
+extern char * _PREHASH_GrabOffset;
+extern char * _PREHASH_SimPort;
+extern char * _PREHASH_PricePerMeter;
+extern char * _PREHASH_RegionFlags;
+extern char * _PREHASH_VoteResult;
+extern char * _PREHASH_ParcelDirFeeEstimate;
+extern char * _PREHASH_ModifyBlock;
+extern char * _PREHASH_InventoryBlock;
+extern char * _PREHASH_ReplyBlock;
+extern char * _PREHASH_ValidUntil;
+extern char * _PREHASH_VelocityInterpolateOn;
+extern char * _PREHASH_ClassifiedDelete;
+extern char * _PREHASH_RegionDenyAnonymous;
+extern char * _PREHASH_FLImageID;
+extern char * _PREHASH_AllowPublish;
+extern char * _PREHASH_SitName;
+extern char * _PREHASH_RegionsVisited;
+extern char * _PREHASH_DirClassifiedReply;
+extern char * _PREHASH_AvatarClassifiedReply;
+extern char * _PREHASH_ReputationIndividualReply;
+extern char * _PREHASH_MediaURL;
+extern char * _PREHASH_CompleteAgentMovement;
+extern char * _PREHASH_SpaceIP;
+extern char * _PREHASH_ClassifiedID;
+extern char * _PREHASH_LocalID;
+extern char * _PREHASH_RemoveItem;
+extern char * _PREHASH_LogFailedMoneyTransaction;
+extern char * _PREHASH_ViewerStartAuction;
+extern char * _PREHASH_StartAuction;
+extern char * _PREHASH_NameValueName;
+extern char * _PREHASH_AngVelX;
+extern char * _PREHASH_DuplicateFlags;
+extern char * _PREHASH_AngVelY;
+extern char * _PREHASH_AngVelZ;
+extern char * _PREHASH_TextColor;
+extern char * _PREHASH_SlaveID;
+extern char * _PREHASH_Charter;
+extern char * _PREHASH_AlertData;
+extern char * _PREHASH_TargetBlock;
+extern char * _PREHASH_CheckParcelAuctions;
+extern char * _PREHASH_ParcelAuctions;
+extern char * _PREHASH_OwnerIsGroup;
+extern char * _PREHASH_NameValuePair;
+extern char * _PREHASH_RemoveNameValuePair;
+extern char * _PREHASH_GetNameValuePair;
+extern char * _PREHASH_BulkUpdateInventory;
+extern char * _PREHASH_UpdateTaskInventory;
+extern char * _PREHASH_RemoveTaskInventory;
+extern char * _PREHASH_MoveTaskInventory;
+extern char * _PREHASH_RequestTaskInventory;
+extern char * _PREHASH_ReplyTaskInventory;
+extern char * _PREHASH_DeclineInventory;
+extern char * _PREHASH_AggregatePermInventory;
+extern char * _PREHASH_SimulatorInfo;
+extern char * _PREHASH_MoneyTransactionsReply;
+extern char * _PREHASH_GroupAccountTransactionsReply;
+extern char * _PREHASH_MailTaskSimReply;
+extern char * _PREHASH_WearableData;
+extern char * _PREHASH_StatisticsData;
+extern char * _PREHASH_Enabled;
+extern char * _PREHASH_Savings;
+extern char * _PREHASH_SimulatorLoad;
+extern char * _PREHASH_InternalRegionIP;
+extern char * _PREHASH_ExternalRegionIP;
+extern char * _PREHASH_TotalPairs;
+extern char * _PREHASH_CreateGroupRequest;
+extern char * _PREHASH_JoinGroupRequest;
+extern char * _PREHASH_LeaveGroupRequest;
+extern char * _PREHASH_InviteGroupRequest;
+extern char * _PREHASH_LiveHelpGroupRequest;
+extern char * _PREHASH_ServerVersion;
+extern char * _PREHASH_PriceParcelClaimFactor;
+extern char * _PREHASH_BillableArea;
+extern char * _PREHASH_ObjectID;
+extern char * _PREHASH_ObjectFlagUpdate;
+extern char * _PREHASH_GroupRoleUpdate;
+extern char * _PREHASH_RequestInventoryAsset;
+extern char * _PREHASH_RedoLand;
+extern char * _PREHASH_TravelAccess;
+extern char * _PREHASH_ChangedGrid;
+extern char * _PREHASH_AgentDropGroup;
+extern char * _PREHASH_Details;
+extern char * _PREHASH_LocationX;
+extern char * _PREHASH_SaleType;
+extern char * _PREHASH_LocationY;
+extern char * _PREHASH_LocationZ;
+extern char * _PREHASH_EconomyData;
+extern char * _PREHASH_HeadRotation;
+extern char * _PREHASH_DeleteOnCompletion;
+extern char * _PREHASH_PublicPort;
+extern char * _PREHASH_DirClassifiedQuery;
+extern char * _PREHASH_CallbackID;
+extern char * _PREHASH_RequestParcelTransfer;
+extern char * _PREHASH_RoleCount;
+extern char * _PREHASH_ObjectCapacity;
+extern char * _PREHASH_RequestID;
+extern char * _PREHASH_RequestXfer;
+extern char * _PREHASH_ObjectTaxCurrent;
+extern char * _PREHASH_LightTaxCurrent;
+extern char * _PREHASH_LandTaxCurrent;
+extern char * _PREHASH_GroupTaxCurrent;
+extern char * _PREHASH_FetchInventoryDescendents;
+extern char * _PREHASH_InventoryDescendents;
+extern char * _PREHASH_Descendents;
+extern char * _PREHASH_PurgeInventoryDescendents;
+extern char * _PREHASH_ShowDir;
+extern char * _PREHASH_IsOwner;
+extern char * _PREHASH_Timestamp;
+extern char * _PREHASH_GlobalPos;
+extern char * _PREHASH_GrabOffsetInitial;
+extern char * _PREHASH_IsTrial;
+extern char * _PREHASH_FinalizeLogout;
+extern char * _PREHASH_ObjectDuplicateOnRay;
+extern char * _PREHASH_GroupMembershipCount;
+extern char * _PREHASH_MethodData;
+extern char * _PREHASH_ActivateGestures;
+extern char * _PREHASH_DeactivateGestures;
+extern char * _PREHASH_ProposalData;
+extern char * _PREHASH_PosGlobal;
+extern char * _PREHASH_SearchID;
+extern char * _PREHASH_RezMultipleAttachmentsFromInv;
+extern char * _PREHASH_SearchName;
+extern char * _PREHASH_VersionString;
+extern char * _PREHASH_CreateGroupReply;
+extern char * _PREHASH_LeaveGroupReply;
+extern char * _PREHASH_ActualArea;
+extern char * _PREHASH_Message;
+extern char * _PREHASH_ClickAction;
+extern char * _PREHASH_AssetUploadComplete;
+extern char * _PREHASH_RequestType;
+extern char * _PREHASH_UUID;
+extern char * _PREHASH_BaseMask;
+extern char * _PREHASH_NetBlock;
+extern char * _PREHASH_GlobalX;
+extern char * _PREHASH_GlobalY;
+extern char * _PREHASH_CopyRotates;
+extern char * _PREHASH_KickUserAck;
+extern char * _PREHASH_TopPick;
+extern char * _PREHASH_SessionID;
+extern char * _PREHASH_GlobalZ;
+extern char * _PREHASH_DeclineFriendship;
+extern char * _PREHASH_FormFriendship;
+extern char * _PREHASH_TerminateFriendship;
+extern char * _PREHASH_TaskData;
+extern char * _PREHASH_SimWideMaxPrims;
+extern char * _PREHASH_TotalPrims;
+extern char * _PREHASH_SourceFilename;
+extern char * _PREHASH_ProfileBegin;
+extern char * _PREHASH_MoneyDetailsRequest;
+extern char * _PREHASH_Request;
+extern char * _PREHASH_GroupAccountDetailsRequest;
+extern char * _PREHASH_GroupActiveProposalsRequest;
+extern char * _PREHASH_StringValue;
+extern char * _PREHASH_ClosestSimulator;
+extern char * _PREHASH_Version;
+extern char * _PREHASH_OtherCount;
+extern char * _PREHASH_MemberCount;
+extern char * _PREHASH_ChatData;
+extern char * _PREHASH_IsGroupOwned;
+extern char * _PREHASH_EnergyEfficiency;
+extern char * _PREHASH_MaxPlace;
+extern char * _PREHASH_PickInfoUpdate;
+extern char * _PREHASH_PickDelete;
+extern char * _PREHASH_ScriptReset;
+extern char * _PREHASH_Requester;
+extern char * _PREHASH_ForSale;
+extern char * _PREHASH_NearestLandingRegionReply;
+extern char * _PREHASH_RecordAgentPresence;
+extern char * _PREHASH_EraseAgentPresence;
+extern char * _PREHASH_ParcelID;
+extern char * _PREHASH_Godlike;
+extern char * _PREHASH_TotalDebits;
+extern char * _PREHASH_Direction;
+extern char * _PREHASH_Appearance;
+extern char * _PREHASH_HealthData;
+extern char * _PREHASH_LeftAxis;
+extern char * _PREHASH_LocationBlock;
+extern char * _PREHASH_ObjectImage;
+extern char * _PREHASH_TerrainStartHeight00;
+extern char * _PREHASH_TerrainStartHeight01;
+extern char * _PREHASH_TerrainStartHeight10;
+extern char * _PREHASH_ObjectHinge;
+extern char * _PREHASH_TerrainStartHeight11;
+extern char * _PREHASH_MetersPerGrid;
+extern char * _PREHASH_WaterHeight;
+extern char * _PREHASH_FetchInventoryReply;
+extern char * _PREHASH_MoneySummaryReply;
+extern char * _PREHASH_GroupAccountSummaryReply;
+extern char * _PREHASH_AttachedSound;
+extern char * _PREHASH_ParamInUse;
+extern char * _PREHASH_GodKickUser;
+extern char * _PREHASH_PickName;
+extern char * _PREHASH_TaskName;
+extern char * _PREHASH_ParcelGodReserveForNewbie;
+extern char * _PREHASH_SubType;
+extern char * _PREHASH_ObjectCount;
+extern char * _PREHASH_RegionPresenceRequestByHandle;
+extern char * _PREHASH_RezSingleAttachmentFromInv;
+extern char * _PREHASH_ChildAgentUpdate;
+extern char * _PREHASH_ToID;
+extern char * _PREHASH_ViewerPort;
+extern char * _PREHASH_IsOwnerGroup;
+extern char * _PREHASH_AgentHeightWidth;
+extern char * _PREHASH_VerticalAngle;
+extern char * _PREHASH_WearableType;
+extern char * _PREHASH_AggregatePermNextOwner;
+extern char * _PREHASH_ShowInList;
+extern char * _PREHASH_PositionSuggestion;
+extern char * _PREHASH_UpdateParcel;
+extern char * _PREHASH_ClearAgentSessions;
+extern char * _PREHASH_SetAlwaysRun;
+extern char * _PREHASH_NVPair;
+extern char * _PREHASH_ObjectSpinStart;
+extern char * _PREHASH_UseEstateSun;
+extern char * _PREHASH_LogoutBlock;
+extern char * _PREHASH_RelayLogControl;
+extern char * _PREHASH_RegionID;
+extern char * _PREHASH_Creator;
+extern char * _PREHASH_ProposalText;
+extern char * _PREHASH_DirEventsReply;
+extern char * _PREHASH_EventInfoReply;
+extern char * _PREHASH_UserInfoReply;
+extern char * _PREHASH_PathRadiusOffset;
+extern char * _PREHASH_SessionInfo;
+extern char * _PREHASH_TextureData;
+extern char * _PREHASH_ChatPass;
+extern char * _PREHASH_TargetID;
+extern char * _PREHASH_DefaultPayPrice;
+extern char * _PREHASH_UserLocation;
+extern char * _PREHASH_MaxPrims;
+extern char * _PREHASH_RegionIP;
+extern char * _PREHASH_LandmarkID;
+extern char * _PREHASH_InitiateDownload;
+extern char * _PREHASH_Name;
+extern char * _PREHASH_OtherCleanTime;
+extern char * _PREHASH_ParcelSetOtherCleanTime;
+extern char * _PREHASH_TeleportPriceExponent;
+extern char * _PREHASH_Gain;
+extern char * _PREHASH_VelX;
+extern char * _PREHASH_PacketAck;
+extern char * _PREHASH_PathSkew;
+extern char * _PREHASH_Negative;
+extern char * _PREHASH_VelY;
+extern char * _PREHASH_SimulatorShutdownRequest;
+extern char * _PREHASH_NearestLandingRegionRequest;
+extern char * _PREHASH_VelZ;
+extern char * _PREHASH_OtherID;
+extern char * _PREHASH_MemberID;
+extern char * _PREHASH_MapLayerRequest;
+extern char * _PREHASH_PatchVersion;
+extern char * _PREHASH_ObjectScale;
+extern char * _PREHASH_TargetIP;
+extern char * _PREHASH_Redo;
+extern char * _PREHASH_MoneyBalance;
+extern char * _PREHASH_TrackAgent;
+extern char * _PREHASH_MaxX;
+extern char * _PREHASH_Data;
+extern char * _PREHASH_MaxY;
+extern char * _PREHASH_TextureAnim;
+extern char * _PREHASH_ReturnIDs;
+extern char * _PREHASH_Date;
+extern char * _PREHASH_GestureUpdate;
+extern char * _PREHASH_AgentWearablesUpdate;
+extern char * _PREHASH_AgentDataUpdate;
+extern char * _PREHASH_Hash;
+extern char * _PREHASH_GroupDataUpdate;
+extern char * _PREHASH_AgentGroupDataUpdate;
+extern char * _PREHASH_Left;
+extern char * _PREHASH_Mask;
+extern char * _PREHASH_ForceMouselook;
+extern char * _PREHASH_Success;
+extern char * _PREHASH_ObjectGroup;
+extern char * _PREHASH_SunHour;
+extern char * _PREHASH_MinX;
+extern char * _PREHASH_ScriptSensorReply;
+extern char * _PREHASH_MinY;
+extern char * _PREHASH_Command;
+extern char * _PREHASH_Desc;
+extern char * _PREHASH_AttachmentNeedsSave;
+extern char * _PREHASH_HistoryItemData;
+extern char * _PREHASH_AgentCachedTexture;
+extern char * _PREHASH_Subject;
+extern char * _PREHASH_East;
+extern char * _PREHASH_GodExpungeUser;
+extern char * _PREHASH_QueryReplies;
+extern char * _PREHASH_ObjectCategory;
+extern char * _PREHASH_Time;
+extern char * _PREHASH_CreateLandmarkForEvent;
+extern char * _PREHASH_ParentID;
+extern char * _PREHASH_Ping;
+extern char * _PREHASH_Perp;
+extern char * _PREHASH_Code;
+extern char * _PREHASH_InvType;
+extern char * _PREHASH_AgentFOV;
+extern char * _PREHASH_BulkMoneyTransfer;
+extern char * _PREHASH_Audible;
+extern char * _PREHASH_AuctionData;
+extern char * _PREHASH_IDBlock;
+extern char * _PREHASH_ReputationData;
+extern char * _PREHASH_West;
+extern char * _PREHASH_Undo;
+extern char * _PREHASH_TotalNumItems;
+extern char * _PREHASH_Info;
+extern char * _PREHASH_Area;
+extern char * _PREHASH_Behavior;
+extern char * _PREHASH_SimCrashed;
+extern char * _PREHASH_Text;
+extern char * _PREHASH_AgentToNewRegion;
+extern char * _PREHASH_PriceGroupCreate;
+extern char * _PREHASH_ObjectShape;
+extern char * _PREHASH_GroupRoleDataReply;
+extern char * _PREHASH_PosX;
+extern char * _PREHASH_PosY;
+extern char * _PREHASH_MuteCRC;
+extern char * _PREHASH_PosZ;
+extern char * _PREHASH_Size;
+extern char * _PREHASH_FromAddress;
+extern char * _PREHASH_Body;
+extern char * _PREHASH_FileData;
+extern char * _PREHASH_List;
+extern char * _PREHASH_KickUser;
+extern char * _PREHASH_OtherPrims;
+extern char * _PREHASH_RunTime;
+extern char * _PREHASH_GrantUserRights;
+extern char * _PREHASH_RpcScriptRequestInboundForward;
+extern char * _PREHASH_More;
+extern char * _PREHASH_Majority;
+extern char * _PREHASH_MetersTraveled;
+extern char * _PREHASH_Stat;
+extern char * _PREHASH_SoundID;
+extern char * _PREHASH_Item;
+extern char * _PREHASH_User;
+extern char * _PREHASH_RemoteInfos;
+extern char * _PREHASH_Prey;
+extern char * _PREHASH_UsecSinceStart;
+extern char * _PREHASH_RayStart;
+extern char * _PREHASH_ParcelData;
+extern char * _PREHASH_CameraUpAxis;
+extern char * _PREHASH_ScriptDialog;
+extern char * _PREHASH_MasterParcelData;
+extern char * _PREHASH_Invalid;
+extern char * _PREHASH_MinPlace;
+extern char * _PREHASH_ProfileCurve;
+extern char * _PREHASH_ParcelAccessListUpdate;
+extern char * _PREHASH_MuteListUpdate;
+extern char * _PREHASH_SendPacket;
+extern char * _PREHASH_SendXferPacket;
+extern char * _PREHASH_RegionDenyIdentified;
+extern char * _PREHASH_NotecardItemID;
+extern char * _PREHASH_LastName;
+extern char * _PREHASH_From;
+extern char * _PREHASH_RoleChange;
+extern char * _PREHASH_Port;
+extern char * _PREHASH_MemberTitle;
+extern char * _PREHASH_LogParcelChanges;
+extern char * _PREHASH_AgentCachedTextureResponse;
+extern char * _PREHASH_DeRezObject;
+extern char * _PREHASH_IsTemporary;
+extern char * _PREHASH_InsigniaID;
+extern char * _PREHASH_CheckFlags;
+extern char * _PREHASH_TransferPriority;
+extern char * _PREHASH_EventID;
+extern char * _PREHASH_Selected;
+extern char * _PREHASH_FromAgentId;
+extern char * _PREHASH_Type;
+extern char * _PREHASH_ChatType;
+extern char * _PREHASH_ReportData;
+extern char * _PREHASH_LeaderBoardData;
+extern char * _PREHASH_RequestBlock;
+extern char * _PREHASH_GrantData;
+extern char * _PREHASH_DetachAttachmentIntoInv;
+extern char * _PREHASH_ParcelDisableObjects;
+extern char * _PREHASH_Sections;
+extern char * _PREHASH_GodLevel;
+extern char * _PREHASH_PayPriceReply;
+extern char * _PREHASH_QueryID;
+extern char * _PREHASH_CameraEyeOffset;
+extern char * _PREHASH_AgentPosition;
+extern char * _PREHASH_GrabPosition;
+extern char * _PREHASH_OnlineNotification;
+extern char * _PREHASH_OfflineNotification;
+extern char * _PREHASH_SendPostcard;
+extern char * _PREHASH_RequestFlags;
+extern char * _PREHASH_MoneyHistoryRequest;
+extern char * _PREHASH_MoneySummaryRequest;
+extern char * _PREHASH_GroupAccountSummaryRequest;
+extern char * _PREHASH_GroupVoteHistoryRequest;
+extern char * _PREHASH_ParamValue;
+extern char * _PREHASH_Checksum;
+extern char * _PREHASH_MaxAgents;
+extern char * _PREHASH_CreateNewOutfitAttachments;
+extern char * _PREHASH_RegionHandle;
+extern char * _PREHASH_TeleportProgress;
+extern char * _PREHASH_AgentQuitCopy;
+extern char * _PREHASH_ToViewer;
+extern char * _PREHASH_AvatarInterestsUpdate;
+extern char * _PREHASH_GroupNoticeID;
+extern char * _PREHASH_ParcelName;
+extern char * _PREHASH_PriceObjectRent;
+extern char * _PREHASH_ConnectAgentToUserserver;
+extern char * _PREHASH_ConnectToUserserver;
+extern char * _PREHASH_OfferCallingCard;
+extern char * _PREHASH_AgentAccess;
+extern char * _PREHASH_AcceptCallingCard;
+extern char * _PREHASH_DeclineCallingCard;
+extern char * _PREHASH_DataHomeLocationReply;
+extern char * _PREHASH_EventLocationReply;
+extern char * _PREHASH_TerseDateID;
+extern char * _PREHASH_ObjectOwner;
+extern char * _PREHASH_AssetID;
+extern char * _PREHASH_AlertMessage;
+extern char * _PREHASH_AgentAlertMessage;
+extern char * _PREHASH_EstateOwnerMessage;
+extern char * _PREHASH_ParcelMediaCommandMessage;
+extern char * _PREHASH_Auction;
+extern char * _PREHASH_Category;
+extern char * _PREHASH_FilePath;
+extern char * _PREHASH_ItemFlags;
+extern char * _PREHASH_Invoice;
+extern char * _PREHASH_IntervalDays;
+extern char * _PREHASH_PathScaleX;
+extern char * _PREHASH_FromTaskID;
+extern char * _PREHASH_TimeInfo;
+extern char * _PREHASH_PathScaleY;
+extern char * _PREHASH_PublicCount;
+extern char * _PREHASH_ParcelJoin;
+extern char * _PREHASH_GroupRolesCount;
+extern char * _PREHASH_SimulatorBlock;
+extern char * _PREHASH_GroupID;
+extern char * _PREHASH_AgentVel;
+extern char * _PREHASH_RequestImage;
+extern char * _PREHASH_NetStats;
+extern char * _PREHASH_AgentPos;
+extern char * _PREHASH_AgentSit;
+extern char * _PREHASH_Material;
+extern char * _PREHASH_ObjectDeGrab;
+extern char * _PREHASH_VelocityInterpolateOff;
+extern char * _PREHASH_AuthorizedBuyerID;
+extern char * _PREHASH_AvatarPropertiesReply;
+extern char * _PREHASH_GroupProfileReply;
+extern char * _PREHASH_SimOwner;
+extern char * _PREHASH_SalePrice;
+extern char * _PREHASH_Animation;
+extern char * _PREHASH_OwnerID;
+extern char * _PREHASH_NearestLandingRegionUpdated;
+extern char * _PREHASH_PassToAgent;
+extern char * _PREHASH_PreyAgent;
+extern char * _PREHASH_SimStats;
+extern char * _PREHASH_Options;
+extern char * _PREHASH_LogoutReply;
+extern char * _PREHASH_FeatureDisabled;
+extern char * _PREHASH_ObjectLocalID;
+extern char * _PREHASH_Dropped;
+extern char * _PREHASH_WebProfilesDisabled;
+extern char * _PREHASH_Destination;
+extern char * _PREHASH_MasterID;
+extern char * _PREHASH_TransferData;
+extern char * _PREHASH_WantToMask;
+extern char * _PREHASH_AvatarData;
+extern char * _PREHASH_ParcelSelectObjects;
+extern char * _PREHASH_ExtraParams;
+extern char * _PREHASH_LogLogin;
+extern char * _PREHASH_CreatorID;
+extern char * _PREHASH_Summary;
+extern char * _PREHASH_BuyObjectInventory;
+extern char * _PREHASH_FetchInventory;
+extern char * _PREHASH_InventoryID;
+extern char * _PREHASH_PacketNumber;
+extern char * _PREHASH_SetFollowCamProperties;
+extern char * _PREHASH_ClearFollowCamProperties;
+extern char * _PREHASH_SequenceID;
+extern char * _PREHASH_DataServerLogout;
+extern char * _PREHASH_NameValue;
+extern char * _PREHASH_PathShearX;
+extern char * _PREHASH_PathShearY;
+extern char * _PREHASH_Velocity;
+extern char * _PREHASH_SecPerYear;
+extern char * _PREHASH_FirstName;
+extern char * _PREHASH_AttachedSoundGainChange;
+extern char * _PREHASH_LocationID;
+extern char * _PREHASH_Running;
+extern char * _PREHASH_AgentThrottle;
+extern char * _PREHASH_NeighborList;
+extern char * _PREHASH_PathTaperX;
+extern char * _PREHASH_PathTaperY;
+extern char * _PREHASH_AgentRelated;
+extern char * _PREHASH_GranterBlock;
+extern char * _PREHASH_UseCachedMuteList;
+extern char * _PREHASH_FailStats;
+extern char * _PREHASH_Tempfile;
+extern char * _PREHASH_BuyerID;
+extern char * _PREHASH_DirPeopleReply;
+extern char * _PREHASH_TransferInfo;
+extern char * _PREHASH_AvatarPickerRequestBackend;
+extern char * _PREHASH_AvatarPropertiesRequestBackend;
+extern char * _PREHASH_UpdateData;
+extern char * _PREHASH_SimFPS;
+extern char * _PREHASH_ReporterID;
+extern char * _PREHASH_ButtonLabel;
+extern char * _PREHASH_GranterID;
+extern char * _PREHASH_WantToText;
+extern char * _PREHASH_ReportType;
+extern char * _PREHASH_DataBlock;
+extern char * _PREHASH_SimulatorReady;
+extern char * _PREHASH_AnimationSourceList;
+extern char * _PREHASH_SubscribeLoad;
+extern char * _PREHASH_UnsubscribeLoad;
+extern char * _PREHASH_Packet;
+extern char * _PREHASH_UndoLand;
+extern char * _PREHASH_SimAccess;
+extern char * _PREHASH_MembershipFee;
+extern char * _PREHASH_InviteGroupResponse;
+extern char * _PREHASH_CreateInventoryFolder;
+extern char * _PREHASH_UpdateInventoryFolder;
+extern char * _PREHASH_MoveInventoryFolder;
+extern char * _PREHASH_RemoveInventoryFolder;
+extern char * _PREHASH_MoneyData;
+extern char * _PREHASH_ObjectDeselect;
+extern char * _PREHASH_NewAssetID;
+extern char * _PREHASH_ObjectAdd;
+extern char * _PREHASH_RayEndIsIntersection;
+extern char * _PREHASH_CompleteAuction;
+extern char * _PREHASH_CircuitCode;
+extern char * _PREHASH_AgentMovementComplete;
+extern char * _PREHASH_ViewerIP;
+extern char * _PREHASH_Header;
+extern char * _PREHASH_GestureFlags;
+extern char * _PREHASH_XferID;
+extern char * _PREHASH_StatValue;
+extern char * _PREHASH_PickID;
+extern char * _PREHASH_TaskID;
+extern char * _PREHASH_GridsPerEdge;
+extern char * _PREHASH_RayEnd;
+extern char * _PREHASH_Throttles;
+extern char * _PREHASH_RebakeAvatarTextures;
+extern char * _PREHASH_UpAxis;
+extern char * _PREHASH_AgentTextures;
+extern char * _PREHASH_NotecardData;
+extern char * _PREHASH_Radius;
+extern char * _PREHASH_OffCircuit;
+extern char * _PREHASH_Access;
+extern char * _PREHASH_TitleRoleID;
+extern char * _PREHASH_SquareMetersCredit;
+extern char * _PREHASH_Filename;
+extern char * _PREHASH_SecuredTemplateChecksumRequest;
+extern char * _PREHASH_TemplateChecksumRequest;
+extern char * _PREHASH_AgentPresenceRequest;
+extern char * _PREHASH_ClassifiedInfoRequest;
+extern char * _PREHASH_ParcelInfoRequest;
+extern char * _PREHASH_ParcelObjectOwnersRequest;
+extern char * _PREHASH_TeleportLandmarkRequest;
+extern char * _PREHASH_EventInfoRequest;
+extern char * _PREHASH_ChatFromSimulator;
+extern char * _PREHASH_PickInfoRequest;
+extern char * _PREHASH_MoneyBalanceRequest;
+extern char * _PREHASH_GroupMembersRequest;
+extern char * _PREHASH_GroupRoleMembersRequest;
+extern char * _PREHASH_OldFolderID;
+extern char * _PREHASH_UserInfoRequest;
+extern char * _PREHASH_TextureID;
+extern char * _PREHASH_ProfileURL;
+extern char * _PREHASH_Handle;
+extern char * _PREHASH_StartParcelRenameAck;
+extern char * _PREHASH_ButtonIndex;
+extern char * _PREHASH_GetScriptRunning;
+extern char * _PREHASH_SetScriptRunning;
+extern char * _PREHASH_Health;
+extern char * _PREHASH_FileID;
+extern char * _PREHASH_CircuitInfo;
+extern char * _PREHASH_ObjectBuy;
+extern char * _PREHASH_ProfileEnd;
+extern char * _PREHASH_Effect;
+extern char * _PREHASH_TestMessage;
+extern char * _PREHASH_ScriptMailRegistration;
+extern char * _PREHASH_AgentSetAppearance;
+extern char * _PREHASH_AvatarAppearance;
+extern char * _PREHASH_RegionData;
+extern char * _PREHASH_RequestingRegionData;
+extern char * _PREHASH_LandingRegionData;
+extern char * _PREHASH_SitTransform;
+extern char * _PREHASH_TerrainBase0;
+extern char * _PREHASH_SkillsMask;
+extern char * _PREHASH_AtAxis;
+extern char * _PREHASH_TerrainBase1;
+extern char * _PREHASH_Reason;
+extern char * _PREHASH_TerrainBase2;
+extern char * _PREHASH_TerrainBase3;
+extern char * _PREHASH_Params;
+extern char * _PREHASH_PingID;
+extern char * _PREHASH_Change;
+extern char * _PREHASH_Height;
+extern char * _PREHASH_Region;
+extern char * _PREHASH_MoneyHistoryReply;
+extern char * _PREHASH_TelehubInfo;
+extern char * _PREHASH_StateSave;
+extern char * _PREHASH_RoleData;
+extern char * _PREHASH_AgentAnimation;
+extern char * _PREHASH_AvatarAnimation;
+extern char * _PREHASH_LogDwellTime;
+extern char * _PREHASH_ParcelGodMarkAsContent;
+extern char * _PREHASH_UsePhysics;
+extern char * _PREHASH_RegionDenyTransacted;
+extern char * _PREHASH_JointType;
+extern char * _PREHASH_TaxEstimate;
+extern char * _PREHASH_ObjectTaxEstimate;
+extern char * _PREHASH_LightTaxEstimate;
+extern char * _PREHASH_TeleportLandingStatusChanged;
+extern char * _PREHASH_LandTaxEstimate;
+extern char * _PREHASH_GroupTaxEstimate;
+extern char * _PREHASH_AvgViewerFPS;
+extern char * _PREHASH_Buttons;
+extern char * _PREHASH_Sender;
+extern char * _PREHASH_Dialog;
+extern char * _PREHASH_TargetData;
+extern char * _PREHASH_DestID;
+extern char * _PREHASH_PricePublicObjectDelete;
+extern char * _PREHASH_ObjectDelete;
+extern char * _PREHASH_Delete;
+extern char * _PREHASH_EventGodDelete;
+extern char * _PREHASH_LastTaxDate;
+extern char * _PREHASH_MapImageID;
+extern char * _PREHASH_EndDateTime;
+extern char * _PREHASH_TerrainDetail0;
+extern char * _PREHASH_TerrainDetail1;
+extern char * _PREHASH_TerrainDetail2;
+extern char * _PREHASH_TerrainDetail3;
+extern char * _PREHASH_Offset;
+extern char * _PREHASH_ObjectDelink;
+extern char * _PREHASH_TargetObject;
+extern char * _PREHASH_IsEstateManager;
+extern char * _PREHASH_CancelAuction;
+extern char * _PREHASH_ObjectDetach;
+extern char * _PREHASH_Compressed;
+extern char * _PREHASH_PathBegin;
+extern char * _PREHASH_BypassRaycast;
+extern char * _PREHASH_WinnerID;
+extern char * _PREHASH_ChannelType;
+extern char * _PREHASH_NonExemptMembers;
+extern char * _PREHASH_Agents;
+extern char * _PREHASH_SimulatorStart;
+extern char * _PREHASH_Enable;
+extern char * _PREHASH_MemberData;
+extern char * _PREHASH_ToGroupID;
+extern char * _PREHASH_ImageNotInDatabase;
+extern char * _PREHASH_StartDate;
+extern char * _PREHASH_AnimID;
+extern char * _PREHASH_Serial;
+extern char * _PREHASH_ControlPort;
+extern char * _PREHASH_ModifyLand;
+extern char * _PREHASH_Digest;
+extern char * _PREHASH_Victim;
+extern char * _PREHASH_Script;
+extern char * _PREHASH_TemplateChecksumReply;
+extern char * _PREHASH_PickInfoReply;
+extern char * _PREHASH_MoneyBalanceReply;
+extern char * _PREHASH_RoutedMoneyBalanceReply;
+extern char * _PREHASH_RoleID;
+extern char * _PREHASH_RegionInfo;
+extern char * _PREHASH_Sequence;
+extern char * _PREHASH_GodUpdateRegionInfo;
+extern char * _PREHASH_LocalX;
+extern char * _PREHASH_LocalY;
+extern char * _PREHASH_StartAnim;
+extern char * _PREHASH_Location;
+extern char * _PREHASH_Action;
+extern char * _PREHASH_Rights;
+extern char * _PREHASH_SearchDir;
+extern char * _PREHASH_Active;
+extern char * _PREHASH_TransferRequest;
+extern char * _PREHASH_ScriptSensorRequest;
+extern char * _PREHASH_MoneyTransferRequest;
+extern char * _PREHASH_EjectGroupMemberRequest;
+extern char * _PREHASH_SkillsText;
+extern char * _PREHASH_Resent;
+extern char * _PREHASH_Center;
+extern char * _PREHASH_SharedData;
+extern char * _PREHASH_PSBlock;
+extern char * _PREHASH_UUIDNameBlock;
+extern char * _PREHASH_Viewer;
+extern char * _PREHASH_GroupNoticeDelete;
+extern char * _PREHASH_GroupTitleUpdate;
+extern char * _PREHASH_Method;
+extern char * _PREHASH_TouchName;
+extern char * _PREHASH_UpdateType;
+extern char * _PREHASH_KickedFromEstateID;
+extern char * _PREHASH_CandidateID;
+extern char * _PREHASH_ParamData;
+extern char * _PREHASH_GodlikeMessage;
+extern char * _PREHASH_SystemMessage;
+extern char * _PREHASH_BodyRotation;
+extern char * _PREHASH_SearchRegions;
+extern char * _PREHASH_Ignore;
+extern char * _PREHASH_AnimationData;
+extern char * _PREHASH_StatID;
+extern char * _PREHASH_ItemID;
+extern char * _PREHASH_AvatarStatisticsReply;
+extern char * _PREHASH_ScriptDialogReply;
+extern char * _PREHASH_RegionIDAndHandleReply;
+extern char * _PREHASH_CameraAtOffset;
+extern char * _PREHASH_VoteID;
+extern char * _PREHASH_ParcelGodForceOwner;
+extern char * _PREHASH_Filter;
+extern char * _PREHASH_InviteData;
+extern char * _PREHASH_PCode;
+extern char * _PREHASH_SearchPos;
+extern char * _PREHASH_PreyID;
+extern char * _PREHASH_TerrainLowerLimit;
+extern char * _PREHASH_EventFlags;
+extern char * _PREHASH_TallyVotes;
+extern char * _PREHASH_Result;
+extern char * _PREHASH_LookAt;
+extern char * _PREHASH_PayButton;
+extern char * _PREHASH_SelfCount;
+extern char * _PREHASH_PacketCount;
+extern char * _PREHASH_ParcelBuyPass;
+extern char * _PREHASH_Identified;
+extern char * _PREHASH_OldItemID;
+extern char * _PREHASH_RegionPort;
+extern char * _PREHASH_PriceEnergyUnit;
+extern char * _PREHASH_Bitmap;
+extern char * _PREHASH_TrackAgentSession;
+extern char * _PREHASH_CacheMissType;
+extern char * _PREHASH_VFileID;
+extern char * _PREHASH_GroupInsigniaID;
+extern char * _PREHASH_FromID;
+extern char * _PREHASH_Online;
+extern char * _PREHASH_KickFlags;
+extern char * _PREHASH_CovenantID;
+extern char * _PREHASH_SysCPU;
+extern char * _PREHASH_EMail;
+extern char * _PREHASH_AggregatePermTextures;
+extern char * _PREHASH_ChatChannel;
+extern char * _PREHASH_ReturnID;
+extern char * _PREHASH_ObjectAttach;
+extern char * _PREHASH_TargetPort;
+extern char * _PREHASH_ObjectSpinStop;
+extern char * _PREHASH_FullID;
+extern char * _PREHASH_ActivateGroup;
+extern char * _PREHASH_SysGPU;
+extern char * _PREHASH_AvatarInterestsReply;
+extern char * _PREHASH_StartLure;
+extern char * _PREHASH_SysRAM;
+extern char * _PREHASH_ObjectPosition;
+extern char * _PREHASH_SitPosition;
+extern char * _PREHASH_StartTime;
+extern char * _PREHASH_BornOn;
+extern char * _PREHASH_CameraCollidePlane;
+extern char * _PREHASH_EconomyDataRequest;
+extern char * _PREHASH_TeleportLureRequest;
+extern char * _PREHASH_FolderID;
+extern char * _PREHASH_RegionHandleRequest;
+extern char * _PREHASH_GestureRequest;
+extern char * _PREHASH_ScriptDataRequest;
+extern char * _PREHASH_GroupRoleDataRequest;
+extern char * _PREHASH_GroupTitlesRequest;
+extern char * _PREHASH_AgentWearablesRequest;
+extern char * _PREHASH_MapBlockRequest;
+extern char * _PREHASH_LureID;
+extern char * _PREHASH_CopyCenters;
+extern char * _PREHASH_ParamList;
+extern char * _PREHASH_InventorySerial;
+extern char * _PREHASH_EdgeDataPacket;
+extern char * _PREHASH_AvatarPickerReply;
+extern char * _PREHASH_ParcelDwellReply;
+extern char * _PREHASH_IsForSale;
+extern char * _PREHASH_MuteID;
+extern char * _PREHASH_MeanCollisionAlert;
+extern char * _PREHASH_CanAcceptTasks;
+extern char * _PREHASH_ItemData;
+extern char * _PREHASH_AnimationList;
+extern char * _PREHASH_PassObject;
+extern char * _PREHASH_Reputation;
+extern char * _PREHASH_IntValue;
+extern char * _PREHASH_TargetType;
+extern char * _PREHASH_Amount;
+extern char * _PREHASH_HasAttachment;
+extern char * _PREHASH_UpdateAttachment;
+extern char * _PREHASH_RemoveAttachment;
+extern char * _PREHASH_HeightWidthBlock;
+extern char * _PREHASH_RequestObjectPropertiesFamily;
+extern char * _PREHASH_ObjectPropertiesFamily;
+extern char * _PREHASH_UserData;
+extern char * _PREHASH_IsReadable;
+extern char * _PREHASH_PathCurve;
+extern char * _PREHASH_Status;
+extern char * _PREHASH_FromGroup;
+extern char * _PREHASH_AlreadyVoted;
+extern char * _PREHASH_PlacesReply;
+extern char * _PREHASH_DirPlacesReply;
+extern char * _PREHASH_ParcelBuy;
+extern char * _PREHASH_DirFindQueryBackend;
+extern char * _PREHASH_DirPlacesQueryBackend;
+extern char * _PREHASH_DirClassifiedQueryBackend;
+extern char * _PREHASH_DirPicksQueryBackend;
+extern char * _PREHASH_DirLandQueryBackend;
+extern char * _PREHASH_DirPopularQueryBackend;
+extern char * _PREHASH_LogoutDemand;
+extern char * _PREHASH_HistoryData;
+extern char * _PREHASH_SnapshotID;
+extern char * _PREHASH_Aspect;
+extern char * _PREHASH_ParamSize;
+extern char * _PREHASH_VoteCast;
+extern char * _PREHASH_CastsShadows;
+extern char * _PREHASH_EveryoneMask;
+extern char * _PREHASH_SetSunPhase;
+extern char * _PREHASH_ObjectSpinUpdate;
+extern char * _PREHASH_MaturePublish;
+extern char * _PREHASH_UseExistingAsset;
+extern char * _PREHASH_Powers;
+extern char * _PREHASH_ParcelLocalID;
+extern char * _PREHASH_TeleportCancel;
+extern char * _PREHASH_UnixTime;
+extern char * _PREHASH_QueryFlags;
+extern char * _PREHASH_LastExecFroze;
+extern char * _PREHASH_AlwaysRun;
+extern char * _PREHASH_Bottom;
+extern char * _PREHASH_ButtonData;
+extern char * _PREHASH_SoundData;
+extern char * _PREHASH_ViewerStats;
+extern char * _PREHASH_RegionHandshake;
+extern char * _PREHASH_ObjectDescription;
+extern char * _PREHASH_Description;
+extern char * _PREHASH_ParamType;
+extern char * _PREHASH_UUIDNameReply;
+extern char * _PREHASH_UUIDGroupNameReply;
+extern char * _PREHASH_SaveAssetIntoInventory;
+extern char * _PREHASH_UserInfo;
+extern char * _PREHASH_AnimSequenceID;
+extern char * _PREHASH_NVPairs;
+extern char * _PREHASH_GroupNoticesListRequest;
+extern char * _PREHASH_ParcelAccessListRequest;
+extern char * _PREHASH_MuteListRequest;
+extern char * _PREHASH_StartPeriod;
+extern char * _PREHASH_RpcChannelRequest;
+extern char * _PREHASH_LandStatRequest;
+extern char * _PREHASH_PlacesQuery;
+extern char * _PREHASH_DirPlacesQuery;
+extern char * _PREHASH_SortOrder;
+extern char * _PREHASH_Hunter;
+extern char * _PREHASH_SunAngVelocity;
+extern char * _PREHASH_BinaryBucket;
+extern char * _PREHASH_ImagePacket;
+extern char * _PREHASH_StartGroupProposal;
+extern char * _PREHASH_EnergyLevel;
+extern char * _PREHASH_PriceForListing;
+extern char * _PREHASH_Scale;
+extern char * _PREHASH_EstateCovenantReply;
+extern char * _PREHASH_ParentEstateID;
+extern char * _PREHASH_Extra2;
+extern char * _PREHASH_Throttle;
+extern char * _PREHASH_SimIP;
+extern char * _PREHASH_GodID;
+extern char * _PREHASH_TeleportMinPrice;
+extern char * _PREHASH_VoteItem;
+extern char * _PREHASH_ObjectRotation;
+extern char * _PREHASH_SitRotation;
+extern char * _PREHASH_SnapSelection;
+extern char * _PREHASH_SoundTrigger;
+extern char * _PREHASH_TerrainRaiseLimit;
+extern char * _PREHASH_Quorum;
+extern char * _PREHASH_TokenBlock;
+extern char * _PREHASH_AgentBlock;
+extern char * _PREHASH_CommandBlock;
+extern char * _PREHASH_PricePublicObjectDecay;
+extern char * _PREHASH_SpawnPointPos;
+extern char * _PREHASH_AttachedSoundCutoffRadius;
+extern char * _PREHASH_VolumeDetail;
+extern char * _PREHASH_FromAgentName;
+extern char * _PREHASH_Range;
+extern char * _PREHASH_DirectoryVisibility;
+extern char * _PREHASH_PublicIP;
+extern char * _PREHASH_TeleportFailed;
+extern char * _PREHASH_OnlineStatusReply;
+extern char * _PREHASH_RequestAvatarInfo;
+extern char * _PREHASH_PreloadSound;
+extern char * _PREHASH_ScreenshotID;
+extern char * _PREHASH_CovenantTimestamp;
+extern char * _PREHASH_OldestUnacked;
+extern char * _PREHASH_SimulatorIP;
+extern char * _PREHASH_ObjectImport;
+extern char * _PREHASH_Value;
+extern char * _PREHASH_JointAxisOrAnchor;
+extern char * _PREHASH_Test0;
+extern char * _PREHASH_Test1;
+extern char * _PREHASH_Test2;
+extern char * _PREHASH_SunPhase;
+extern char * _PREHASH_Place;
+extern char * _PREHASH_Phase;
+extern char * _PREHASH_ParcelDivide;
+extern char * _PREHASH_PriceObjectClaim;
+extern char * _PREHASH_Field;
+extern char * _PREHASH_Ratio;
+extern char * _PREHASH_JoinGroupReply;
+extern char * _PREHASH_LiveHelpGroupReply;
+extern char * _PREHASH_Score;
+extern char * _PREHASH_ExpungeData;
+extern char * _PREHASH_Image;
+extern char * _PREHASH_ObjectClickAction;
+extern char * _PREHASH_Delta;
+extern char * _PREHASH_InitiateUpload;
+extern char * _PREHASH_Parameter;
+extern char * _PREHASH_Flags;
+extern char * _PREHASH_Plane;
+extern char * _PREHASH_Width;
+extern char * _PREHASH_Right;
+extern char * _PREHASH_DirFindQuery;
+extern char * _PREHASH_Textures;
+extern char * _PREHASH_EventData;
+extern char * _PREHASH_Final;
+extern char * _PREHASH_TelehubPos;
+extern char * _PREHASH_ReportAutosaveCrash;
+extern char * _PREHASH_Reset;
+extern char * _PREHASH_CreateTrustedCircuit;
+extern char * _PREHASH_DenyTrustedCircuit;
+extern char * _PREHASH_RequestTrustedCircuit;
+extern char * _PREHASH_Codec;
+extern char * _PREHASH_Level;
+extern char * _PREHASH_Modal;
+extern char * _PREHASH_ChildAgentUnknown;
+extern char * _PREHASH_LandingType;
+extern char * _PREHASH_ScriptRunningReply;
+extern char * _PREHASH_MoneyDetailsReply;
+extern char * _PREHASH_Reply;
+extern char * _PREHASH_TelehubRot;
+extern char * _PREHASH_RequestFriendship;
+extern char * _PREHASH_AcceptFriendship;
+extern char * _PREHASH_GroupAccountDetailsReply;
+extern char * _PREHASH_DwellInfo;
+extern char * _PREHASH_AgentResume;
+extern char * _PREHASH_ItemType;
+extern char * _PREHASH_MailFilter;
+extern char * _PREHASH_Disconnect;
+extern char * _PREHASH_SimPosition;
+extern char * _PREHASH_SimWideTotalPrims;
+extern char * _PREHASH_Index;
+extern char * _PREHASH_BaseFilename;
+extern char * _PREHASH_SimFilename;
+extern char * _PREHASH_LastOwnerID;
+extern char * _PREHASH_GroupNoticeRequest;
+extern char * _PREHASH_EmailMessageRequest;
+extern char * _PREHASH_MapItemRequest;
+extern char * _PREHASH_AgentCount;
+extern char * _PREHASH_MessageBlock;
+extern char * _PREHASH_FuseBlock;
+extern char * _PREHASH_AgentGroupData;
+extern char * _PREHASH_ClassifiedInfoUpdate;
+extern char * _PREHASH_RegionPos;
+extern char * _PREHASH_ParcelMediaUpdate;
+extern char * _PREHASH_NoticeID;
+extern char * _PREHASH_GridX;
+extern char * _PREHASH_GridY;
+extern char * _PREHASH_Title;
+extern char * _PREHASH_AuctionID;
+extern char * _PREHASH_VoteType;
+extern char * _PREHASH_CategoryID;
+extern char * _PREHASH_Token;
+extern char * _PREHASH_AggregatePerms;
+extern char * _PREHASH_StartParcelRemoveAck;
+extern char * _PREHASH_ObjectSelect;
+extern char * _PREHASH_ForceObjectSelect;
+extern char * _PREHASH_Price;
+extern char * _PREHASH_SunDirection;
+extern char * _PREHASH_FromName;
+extern char * _PREHASH_ChangeInventoryItemFlags;
+extern char * _PREHASH_Force;
+extern char * _PREHASH_TransactionBlock;
+extern char * _PREHASH_PowersMask;
+extern char * _PREHASH_Stamp;
+extern char * _PREHASH_TotalCredits;
+extern char * _PREHASH_State;
+extern char * _PREHASH_TextureIndex;
+extern char * _PREHASH_InviteeID;
+extern char * _PREHASH_ParcelReclaim;
+extern char * _PREHASH_Money;
+extern char * _PREHASH_PathTwist;
+extern char * _PREHASH_AuthBuyerID;
+extern char * _PREHASH_Color;
+extern char * _PREHASH_SourceType;
+extern char * _PREHASH_World;
+extern char * _PREHASH_QueryData;
+extern char * _PREHASH_Users;
+extern char * _PREHASH_SysOS;
+extern char * _PREHASH_Notes;
+extern char * _PREHASH_AvatarID;
+extern char * _PREHASH_FounderID;
+extern char * _PREHASH_EndPointID;
+extern char * _PREHASH_StipendEstimate;
+extern char * _PREHASH_LocationLookAt;
+extern char * _PREHASH_Sound;
+extern char * _PREHASH_Cover;
+extern char * _PREHASH_TotalObjectCount;
+extern char * _PREHASH_TextureEntry;
+extern char * _PREHASH_SquareMetersCommitted;
+extern char * _PREHASH_ChannelID;
+extern char * _PREHASH_Dwell;
+extern char * _PREHASH_North;
+extern char * _PREHASH_AgentUpdate;
+extern char * _PREHASH_PickGodDelete;
+extern char * _PREHASH_HostName;
+extern char * _PREHASH_PriceParcelClaim;
+extern char * _PREHASH_ParcelClaim;
+extern char * _PREHASH_AgentPowers;
+extern char * _PREHASH_ProfileHollow;
+extern char * _PREHASH_GroupRoleChanges;
+extern char * _PREHASH_Count;
+extern char * _PREHASH_South;
+extern char * _PREHASH_Entry;
+extern char * _PREHASH_ObjectUpdateCompressed;
+extern char * _PREHASH_MuteFlags;
+extern char * _PREHASH_Group;
+extern char * _PREHASH_AgentPause;
+extern char * _PREHASH_LanguagesText;
+extern char * _PREHASH_InternalScriptMail;
+extern char * _PREHASH_FindAgent;
+extern char * _PREHASH_AgentData;
+extern char * _PREHASH_FolderData;
+extern char * _PREHASH_AssetBlock;
+extern char * _PREHASH_AcceptNotices;
+extern char * _PREHASH_SetGroupAcceptNotices;
+extern char * _PREHASH_CloseCircuit;
+extern char * _PREHASH_LogControl;
+extern char * _PREHASH_TeleportFinish;
+extern char * _PREHASH_PathRevolutions;
+extern char * _PREHASH_ClassifiedInfoReply;
+extern char * _PREHASH_ParcelInfoReply;
+extern char * _PREHASH_AutosaveData;
+extern char * _PREHASH_SetStartLocation;
+extern char * _PREHASH_PassHours;
+extern char * _PREHASH_AttachmentPt;
+extern char * _PREHASH_ParcelFlags;
+extern char * _PREHASH_NumVotes;
+extern char * _PREHASH_AvatarPickerRequest;
+extern char * _PREHASH_TeleportLocationRequest;
+extern char * _PREHASH_DataHomeLocationRequest;
+extern char * _PREHASH_EventNotificationAddRequest;
+extern char * _PREHASH_ParcelDwellRequest;
+extern char * _PREHASH_EventLocationRequest;
+extern char * _PREHASH_EndPeriod;
+extern char * _PREHASH_SetStartLocationRequest;
+extern char * _PREHASH_QueryStart;
+extern char * _PREHASH_EjectData;
+extern char * _PREHASH_AvatarTextureUpdate;
+extern char * _PREHASH_RPCServerPort;
+extern char * _PREHASH_Bytes;
+extern char * _PREHASH_Extra;
+extern char * _PREHASH_ForceScriptControlRelease;
+extern char * _PREHASH_ParcelRelease;
+extern char * _PREHASH_VFileType;
+extern char * _PREHASH_EjectGroupMemberReply;
+extern char * _PREHASH_ImageData;
+extern char * _PREHASH_SpaceServerSimulatorTimeMessage;
+extern char * _PREHASH_SimulatorViewerTimeMessage;
+extern char * _PREHASH_Rotation;
+extern char * _PREHASH_Selection;
+extern char * _PREHASH_TransactionData;
+extern char * _PREHASH_OperationData;
+extern char * _PREHASH_ExpirationDate;
+extern char * _PREHASH_ParcelDeedToGroup;
+extern char * _PREHASH_DirPicksReply;
+extern char * _PREHASH_AvatarPicksReply;
+extern char * _PREHASH_GroupTitlesReply;
+extern char * _PREHASH_AgentInfo;
+extern char * _PREHASH_MoneyTransferBackend;
+extern char * _PREHASH_NextOwnerMask;
+extern char * _PREHASH_MuteData;
+extern char * _PREHASH_PassPrice;
+extern char * _PREHASH_SourceID;
+extern char * _PREHASH_ChangeUserRights;
+extern char * _PREHASH_TeleportFlags;
+extern char * _PREHASH_AssetData;
+extern char * _PREHASH_SlaveParcelData;
+extern char * _PREHASH_MultipleObjectUpdate;
+extern char * _PREHASH_ObjectUpdate;
+extern char * _PREHASH_ImprovedTerseObjectUpdate;
+extern char * _PREHASH_ConfirmXferPacket;
+extern char * _PREHASH_StartPingCheck;
+extern char * _PREHASH_SimWideDeletes;
+extern char * _PREHASH_LandStatReply;
+extern char * _PREHASH_IsPhantom;
+extern char * _PREHASH_AgentList;
+extern char * _PREHASH_SimApproved;
+extern char * _PREHASH_RezObject;
+extern char * _PREHASH_TaskLocalID;
+extern char * _PREHASH_ClaimDate;
+extern char * _PREHASH_MergeParcel;
+extern char * _PREHASH_Priority;
+extern char * _PREHASH_Building;
+extern char * _PREHASH_QueryText;
+extern char * _PREHASH_GroupNoticeAdd;
+extern char * _PREHASH_ReturnType;
+extern char * _PREHASH_FetchFolders;
+extern char * _PREHASH_SimulatorPublicHostBlock;
+extern char * _PREHASH_HeaderData;
+extern char * _PREHASH_RequestMultipleObjects;
+extern char * _PREHASH_RetrieveInstantMessages;
+extern char * _PREHASH_DequeueInstantMessages;
+extern char * _PREHASH_OpenCircuit;
+extern char * _PREHASH_SecureSessionID;
+extern char * _PREHASH_CrossedRegion;
+extern char * _PREHASH_DirGroupsReply;
+extern char * _PREHASH_AvatarGroupsReply;
+extern char * _PREHASH_EmailMessageReply;
+extern char * _PREHASH_GroupVoteHistoryItemReply;
+extern char * _PREHASH_ViewerPosition;
+extern char * _PREHASH_Position;
+extern char * _PREHASH_ParentEstate;
+extern char * _PREHASH_EstateName;
+extern char * _PREHASH_MuteName;
+extern char * _PREHASH_StartParcelRename;
+extern char * _PREHASH_BulkParcelRename;
+extern char * _PREHASH_ParcelRename;
+extern char * _PREHASH_ViewerFilename;
+extern char * _PREHASH_Positive;
+extern char * _PREHASH_UserReportInternal;
+extern char * _PREHASH_AvatarPropertiesRequest;
+extern char * _PREHASH_ParcelPropertiesRequest;
+extern char * _PREHASH_GroupProfileRequest;
+extern char * _PREHASH_AgentDataUpdateRequest;
+extern char * _PREHASH_PriceObjectScaleFactor;
+extern char * _PREHASH_DirPicksQuery;
+extern char * _PREHASH_OpenEnrollment;
+extern char * _PREHASH_GroupData;
+extern char * _PREHASH_RequestGodlikePowers;
+extern char * _PREHASH_GrantGodlikePowers;
+extern char * _PREHASH_TransactionID;
+extern char * _PREHASH_DestinationID;
+extern char * _PREHASH_Controls;
+extern char * _PREHASH_FirstDetachAll;
+extern char * _PREHASH_EstateID;
+extern char * _PREHASH_ImprovedInstantMessage;
+extern char * _PREHASH_AgentQuit;
+extern char * _PREHASH_CheckParcelSales;
+extern char * _PREHASH_ParcelSales;
+extern char * _PREHASH_CurrentInterval;
+extern char * _PREHASH_PriceRentLight;
+extern char * _PREHASH_MediaAutoScale;
+extern char * _PREHASH_NeighborBlock;
+extern char * _PREHASH_LayerData;
+extern char * _PREHASH_NVPairData;
+extern char * _PREHASH_TeleportLocal;
+extern char * _PREHASH_EjecteeID;
+extern char * _PREHASH_VoteInitiator;
+extern char * _PREHASH_TypeData;
+extern char * _PREHASH_OwnerIDs;
+extern char * _PREHASH_SystemKickUser;
+extern char * _PREHASH_TransactionTime;
+extern char * _PREHASH_TimeToLive;
+extern char * _PREHASH_StartParcelRemove;
+extern char * _PREHASH_BulkParcelRemove;
+extern char * _PREHASH_OldAgentID;
+extern char * _PREHASH_BonusEstimate;
+extern char * _PREHASH_MusicURL;
+extern char * _PREHASH_CompleteLure;
+extern char * _PREHASH_ParcelPrimBonus;
+extern char * _PREHASH_EjectUser;
+extern char * _PREHASH_CoarseLocationUpdate;
+extern char * _PREHASH_ChildAgentPositionUpdate;
+extern char * _PREHASH_StoreLocal;
+extern char * _PREHASH_GroupName;
+extern char * _PREHASH_PriceParcelRent;
+extern char * _PREHASH_SimStatus;
+extern char * _PREHASH_TransactionSuccess;
+extern char * _PREHASH_LureType;
+extern char * _PREHASH_GroupMask;
+extern char * _PREHASH_SitObject;
+extern char * _PREHASH_Override;
+extern char * _PREHASH_LocomotionState;
+extern char * _PREHASH_PriceUpload;
+extern char * _PREHASH_RemoveParcel;
+extern char * _PREHASH_ConfirmAuctionStart;
+extern char * _PREHASH_RpcScriptRequestInbound;
+extern char * _PREHASH_ActiveGroupID;
+extern char * _PREHASH_ParcelReturnObjects;
+extern char * _PREHASH_TotalObjects;
+extern char * _PREHASH_ObjectExtraParams;
+extern char * _PREHASH_Questions;
+extern char * _PREHASH_TransferAbort;
+extern char * _PREHASH_TransferInventory;
+extern char * _PREHASH_RayTargetID;
+extern char * _PREHASH_ClaimPrice;
+extern char * _PREHASH_ObjectProperties;
+extern char * _PREHASH_ParcelProperties;
+extern char * _PREHASH_EstateOwnerID;
+extern char * _PREHASH_LogoutRequest;
+extern char * _PREHASH_AssetUploadRequest;
+extern char * _PREHASH_ReputationIndividualRequest;
+extern char * _PREHASH_MajorVersion;
+extern char * _PREHASH_MinorVersion;
+extern char * _PREHASH_SimulatorAssign;
+extern char * _PREHASH_TransactionType;
+extern char * _PREHASH_AvatarPropertiesUpdate;
+extern char * _PREHASH_ParcelPropertiesUpdate;
+extern char * _PREHASH_FetchItems;
+extern char * _PREHASH_AbortXfer;
+extern char * _PREHASH_DeRezAck;
+extern char * _PREHASH_TakeControls;
+extern char * _PREHASH_DirLandReply;
+extern char * _PREHASH_SpaceLocationTeleportReply;
+extern char * _PREHASH_MuteType;
+extern char * _PREHASH_IMViaEMail;
+extern char * _PREHASH_StartExpungeProcessAck;
+extern char * _PREHASH_RentPrice;
+extern char * _PREHASH_GenericMessage;
+extern char * _PREHASH_ChildAgentAlive;
+extern char * _PREHASH_AssetType;
+extern char * _PREHASH_SpawnPointBlock;
+extern char * _PREHASH_AttachmentBlock;
+extern char * _PREHASH_ObjectMaterial;
+extern char * _PREHASH_OwnerName;
+extern char * _PREHASH_AvatarNotesReply;
+extern char * _PREHASH_CacheID;
+extern char * _PREHASH_OwnerMask;
+extern char * _PREHASH_TransferInventoryAck;
+
+
+void init_prehash_data();
+
+
+
+
+
+#endif
diff --git a/indra/llmessage/message_string_table.cpp b/indra/llmessage/message_string_table.cpp
new file mode 100644
index 0000000000..687b47a112
--- /dev/null
+++ b/indra/llmessage/message_string_table.cpp
@@ -0,0 +1,75 @@
+/**
+ * @file message_string_table.cpp
+ * @brief static string table for message template
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llerror.h"
+#include "message.h"
+
+inline U32 message_hash_my_string(const char *str)
+{
+ U32 retval = 0;
+ while (*str++)
+ {
+ retval += *str;
+ retval <<= 1;
+ }
+ return (retval % MESSAGE_NUMBER_OF_HASH_BUCKETS);
+}
+
+
+LLMessageStringTable gMessageStringTable;
+
+
+LLMessageStringTable::LLMessageStringTable()
+: mUsed(0)
+{
+ for (U32 i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ mEmpty[i] = TRUE;
+ mString[i][0] = 0;
+ }
+}
+
+
+LLMessageStringTable::~LLMessageStringTable()
+{ }
+
+
+char* LLMessageStringTable::getString(const char *str)
+{
+ U32 hash_value = message_hash_my_string(str);
+ while (!mEmpty[hash_value])
+ {
+ if (!strncmp(str, mString[hash_value], MESSAGE_MAX_STRINGS_LENGTH))
+ {
+ return mString[hash_value];
+ }
+ else
+ {
+ hash_value++;
+ hash_value %= MESSAGE_NUMBER_OF_HASH_BUCKETS;
+ }
+ }
+ // not found, so add!
+ strncpy(mString[hash_value], str, MESSAGE_MAX_STRINGS_LENGTH);
+ mString[hash_value][MESSAGE_MAX_STRINGS_LENGTH - 1] = 0;
+ mEmpty[hash_value] = FALSE;
+ mUsed++;
+ if (mUsed >= MESSAGE_NUMBER_OF_HASH_BUCKETS - 1)
+ {
+ U32 i;
+ llinfos << "Dumping string table before crashing on HashTable full!" << llendl;
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ llinfos << "Entry #" << i << ": " << mString[i] << llendl;
+ }
+ }
+ return mString[hash_value];
+}
+
diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp
new file mode 100644
index 0000000000..2b712840d8
--- /dev/null
+++ b/indra/llmessage/net.cpp
@@ -0,0 +1,516 @@
+/**
+ * @file net.cpp
+ * @brief Cross-platform routines for sending and receiving packets.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "net.h"
+
+// system library includes
+#include <stdexcept>
+#include <stdio.h>
+
+#if !LL_WINDOWS // Windows Versions
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+// linden library includes
+#include "network.h"
+#include "llerror.h"
+#include "llhost.h"
+#include "lltimer.h"
+#include "indra_constants.h"
+
+
+// Globals
+#if LL_WINDOWS
+
+SOCKADDR_IN stDstAddr;
+SOCKADDR_IN stSrcAddr;
+SOCKADDR_IN stLclAddr;
+static WSADATA stWSAData;
+
+#else
+
+struct sockaddr_in stDstAddr;
+struct sockaddr_in stSrcAddr;
+struct sockaddr_in stLclAddr;
+
+#if LL_DARWIN
+#ifndef _SOCKLEN_T
+#define _SOCKLEN_T
+typedef int socklen_t;
+#endif
+#endif
+
+#endif
+
+
+const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1";
+
+#if LL_DARWIN
+ // Mac OS X returns an error when trying to set these to 400000. Smaller values succeed.
+ const int SEND_BUFFER_SIZE = 200000;
+ const int RECEIVE_BUFFER_SIZE = 200000;
+#else // LL_DARWIN
+ const int SEND_BUFFER_SIZE = 400000;
+ const int RECEIVE_BUFFER_SIZE = 400000;
+#endif // LL_DARWIN
+
+// universal functions (cross-platform)
+
+LLHost get_sender()
+{
+ return LLHost(stSrcAddr.sin_addr.s_addr, ntohs(stSrcAddr.sin_port));
+}
+
+U32 get_sender_ip(void)
+{
+ return stSrcAddr.sin_addr.s_addr;
+}
+
+U32 get_sender_port()
+{
+ return ntohs(stSrcAddr.sin_port);
+}
+
+const char* u32_to_ip_string(U32 ip)
+{
+ static char buffer[MAXADDRSTR]; /* Flawfinder: ignore */
+
+ // Convert the IP address into a string
+ in_addr in;
+ in.s_addr = ip;
+ char* result = inet_ntoa(in);
+
+ // NULL indicates error in conversion
+ if (result != NULL)
+ {
+ strncpy( buffer, result, MAXADDRSTR ); /* Flawfinder: ignore */
+ buffer[MAXADDRSTR-1] = '\0';
+ return buffer;
+ }
+ else
+ {
+ return "(bad IP addr)";
+ }
+}
+
+
+// Returns ip_string if successful, NULL if not. Copies into ip_string
+char *u32_to_ip_string(U32 ip, char *ip_string)
+{
+ char *result;
+ in_addr in;
+
+ // Convert the IP address into a string
+ in.s_addr = ip;
+ result = inet_ntoa(in);
+
+ // NULL indicates error in conversion
+ if (result != NULL)
+ {
+ //the function signature needs to change to pass in the lengfth of first and last.
+ strcpy(ip_string, result);
+ return ip_string;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// Wrapper for inet_addr()
+U32 ip_string_to_u32(const char* ip_string)
+{
+ return inet_addr(ip_string);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Windows Versions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#if LL_WINDOWS
+
+S32 start_net(S32& socket_out, int& nPort)
+{
+ // Create socket, make non-blocking
+ // Init WinSock
+ int nRet;
+ int hSocket;
+
+ int snd_size = SEND_BUFFER_SIZE;
+ int rec_size = RECEIVE_BUFFER_SIZE;
+ int buff_size = 4;
+
+ // Initialize windows specific stuff
+ if(WSAStartup(0x0202, &stWSAData))
+ {
+ S32 err = WSAGetLastError();
+ WSACleanup();
+ llwarns << "Windows Sockets initialization failed, err " << err << llendl;
+ return 1;
+ }
+
+ // Get a datagram socket
+ hSocket = (int)socket(AF_INET, SOCK_DGRAM, 0);
+ if (hSocket == INVALID_SOCKET)
+ {
+ S32 err = WSAGetLastError();
+ WSACleanup();
+ llwarns << "socket() failed, err " << err << llendl;
+ return 2;
+ }
+
+ // Name the socket (assign the local port number to receive on)
+ stLclAddr.sin_family = AF_INET;
+ stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ stLclAddr.sin_port = htons(nPort);
+
+ S32 attempt_port = nPort;
+ llinfos << "attempting to connect on port " << attempt_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+
+ if (nRet == SOCKET_ERROR)
+ {
+ // If we got an address in use error...
+ if (WSAGetLastError() == WSAEADDRINUSE)
+ {
+ // Try all ports from PORT_DISCOVERY_RANGE_MIN to PORT_DISCOVERY_RANGE_MAX
+ for(attempt_port = PORT_DISCOVERY_RANGE_MIN;
+ attempt_port <= PORT_DISCOVERY_RANGE_MAX;
+ attempt_port++)
+ {
+ stLclAddr.sin_port = htons(attempt_port);
+ llinfos << "trying port " << attempt_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+
+ if (!(nRet == SOCKET_ERROR &&
+ WSAGetLastError() == WSAEADDRINUSE))
+ {
+ break;
+ }
+ }
+
+ if (nRet == SOCKET_ERROR)
+ {
+ llwarns << "startNet() : Couldn't find available network port." << llendl;
+ // Fail gracefully here in release
+ return 3;
+ }
+ }
+ else
+ // Some other socket error
+ {
+ llwarns << llformat("bind() port: %d failed, Err: %d\n", nPort, WSAGetLastError()) << llendl;
+ // Fail gracefully in release.
+ return 4;
+ }
+ }
+ llinfos << "connected on port " << attempt_port << llendl;
+ nPort = attempt_port;
+
+ // Set socket to be non-blocking
+ unsigned long argp = 1;
+ nRet = ioctlsocket (hSocket, FIONBIO, &argp);
+ if (nRet == SOCKET_ERROR)
+ {
+ printf("Failed to set socket non-blocking, Err: %d\n",
+ WSAGetLastError());
+ }
+
+ // set a large receive buffer
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set receive buffer size!" << llendl;
+ }
+
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set send buffer size!" << llendl;
+ }
+
+ getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
+ getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
+
+ llinfos << "startNet - receive buffer size : " << rec_size << llendl;
+ llinfos << "startNet - send buffer size : " << snd_size << llendl;
+
+ // Setup a destination address
+ char achMCAddr[MAXADDRSTR] = " "; /* Flawfinder: ignore */
+ stDstAddr.sin_family = AF_INET;
+ stDstAddr.sin_addr.s_addr = inet_addr(achMCAddr);
+ stDstAddr.sin_port = htons(nPort);
+
+ socket_out = hSocket;
+ return 0;
+}
+
+void end_net()
+{
+ WSACleanup();
+}
+
+S32 receive_packet(int hSocket, char * receiveBuffer)
+{
+ // Receives data asynchronously from the socket set by initNet().
+ // Returns the number of bytes received into dataReceived, or zero
+ // if there is no data received.
+ int nRet;
+ int addr_size = sizeof(struct sockaddr_in);
+
+ nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, 0, (struct sockaddr*)&stSrcAddr, &addr_size);
+ if (nRet == SOCKET_ERROR )
+ {
+ if (WSAEWOULDBLOCK == WSAGetLastError())
+ return 0;
+ if (WSAECONNRESET == WSAGetLastError())
+ return 0;
+ llinfos << "receivePacket() failed, Error: " << WSAGetLastError() << llendl;
+ }
+
+ return nRet;
+}
+
+// Returns TRUE on success.
+BOOL send_packet(int hSocket, const char *sendBuffer, int size, U32 recipient, int nPort)
+{
+ // Sends a packet to the address set in initNet
+ //
+ int nRet = 0;
+ U32 last_error = 0;
+
+ stDstAddr.sin_addr.s_addr = recipient;
+ stDstAddr.sin_port = htons(nPort);
+ do
+ {
+ nRet = sendto(hSocket, sendBuffer, size, 0, (struct sockaddr*)&stDstAddr, sizeof(stDstAddr));
+
+ if (nRet == SOCKET_ERROR )
+ {
+ last_error = WSAGetLastError();
+ if (last_error != WSAEWOULDBLOCK)
+ {
+ // WSAECONNRESET - I think this is caused by an ICMP "connection refused"
+ // message being sent back from a Linux box... I'm not finding helpful
+ // documentation or web pages on this. The question is whether the packet
+ // actually got sent or not. Based on the structure of this code, I would
+ // assume it is. JNC 2002.01.18
+ if (WSAECONNRESET == WSAGetLastError())
+ {
+ return TRUE;
+ }
+ llinfos << "sendto() failed to " << u32_to_ip_string(recipient) << ":" << nPort
+ << ", Error " << last_error << llendl;
+ }
+ }
+ } while ( (nRet == SOCKET_ERROR)
+ &&(last_error == WSAEWOULDBLOCK));
+
+ return (nRet != SOCKET_ERROR);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Linux Versions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#else
+
+// Create socket, make non-blocking
+S32 start_net(S32& socket_out, int& nPort)
+{
+ int hSocket, nRet;
+ int snd_size = SEND_BUFFER_SIZE;
+ int rec_size = RECEIVE_BUFFER_SIZE;
+
+ socklen_t buff_size = 4;
+
+ // Create socket
+ hSocket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (hSocket < 0)
+ {
+ llwarns << "socket() failed" << llendl;
+ return 1;
+ }
+
+ // Don't bind() if we want the operating system to assign our ports for
+ // us.
+ if (NET_USE_OS_ASSIGNED_PORT == nPort)
+ {
+ // Do nothing; the operating system will do it for us.
+ }
+ else
+ {
+ // Name the socket (assign the local port number to receive on)
+ stLclAddr.sin_family = AF_INET;
+ stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ stLclAddr.sin_port = htons(nPort);
+ U32 attempt_port = nPort;
+ llinfos << "attempting to connect on port " << attempt_port << llendl;
+
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+ if (nRet < 0)
+ {
+ // If we got an address in use error...
+ if (errno == EADDRINUSE)
+ {
+ // Try all ports from PORT_DISCOVERY_RANGE_MIN to PORT_DISCOVERY_RANGE_MAX
+ for(attempt_port = PORT_DISCOVERY_RANGE_MIN;
+ attempt_port <= PORT_DISCOVERY_RANGE_MAX;
+ attempt_port++)
+ {
+ stLclAddr.sin_port = htons(attempt_port);
+ llinfos << "trying port " << attempt_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+ if (!((nRet < 0) && (errno == EADDRINUSE)))
+ {
+ break;
+ }
+ }
+ if (nRet < 0)
+ {
+ llwarns << "startNet() : Couldn't find available network port." << llendl;
+ // Fail gracefully in release.
+ return 3;
+ }
+ }
+ // Some other socket error
+ else
+ {
+ llwarns << llformat ("bind() port: %d failed, Err: %s\n", nPort, strerror(errno)) << llendl;
+ // Fail gracefully in release.
+ return 4;
+ }
+ }
+ llinfos << "connected on port " << attempt_port << llendl;
+ nPort = attempt_port;
+ }
+ // Set socket to be non-blocking
+ fcntl(hSocket, F_SETFL, O_NONBLOCK);
+ // set a large receive buffer
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set receive size!" << llendl;
+ }
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set send size!" << llendl;
+ }
+ getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
+ getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
+
+ llinfos << "startNet - receive buffer size : " << rec_size << llendl;
+ llinfos << "startNet - send buffer size : " << snd_size << llendl;
+
+ // Setup a destination address
+ char achMCAddr[MAXADDRSTR] = "127.0.0.1"; /* Flawfinder: ignore */
+ stDstAddr.sin_family = AF_INET;
+ stDstAddr.sin_addr.s_addr = inet_addr(achMCAddr);
+ stDstAddr.sin_port = htons(nPort);
+
+ socket_out = hSocket;
+ return 0;
+}
+
+void end_net()
+{
+}
+
+int receive_packet(int hSocket, char * receiveBuffer)
+{
+ // Receives data asynchronously from the socket set by initNet().
+ // Returns the number of bytes received into dataReceived, or zero
+ // if there is no data received.
+ // or -1 if an error occured!
+ int nRet;
+ socklen_t addr_size = sizeof(struct sockaddr_in);
+
+ nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, 0, (struct sockaddr*)&stSrcAddr, &addr_size);
+
+ if (nRet == -1)
+ {
+ // To maintain consistency with the Windows implementation, return a zero for size on error.
+ return 0;
+ }
+
+ return nRet;
+}
+
+BOOL send_packet(int hSocket, const char * sendBuffer, int size, U32 recipient, int nPort)
+{
+ int ret;
+ BOOL success;
+ BOOL resend;
+ S32 send_attempts = 0;
+
+ stDstAddr.sin_addr.s_addr = recipient;
+ stDstAddr.sin_port = htons(nPort);
+
+ do
+ {
+ ret = sendto(hSocket, sendBuffer, size, 0, (struct sockaddr*)&stDstAddr, sizeof(stDstAddr));
+ send_attempts++;
+
+ if (ret >= 0)
+ {
+ // successful send
+ success = TRUE;
+ resend = FALSE;
+ }
+ else
+ {
+ // send failed, check to see if we should resend
+ success = FALSE;
+
+ if (errno == EAGAIN)
+ {
+ // say nothing, just repeat send
+ llinfos << "sendto() reported buffer full, resending (attempt " << send_attempts << ")" << llendl;
+ llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl;
+ resend = TRUE;
+ }
+ else if (errno == ECONNREFUSED)
+ {
+ // response to ICMP connection refused message on earlier send
+ llinfos << "sendto() reported connection refused, resending (attempt " << send_attempts << ")" << llendl;
+ llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl;
+ resend = TRUE;
+ }
+ else
+ {
+ // some other error
+ llinfos << "sendto() failed: " << errno << ", " << strerror(errno) << llendl;
+ llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl;
+ resend = FALSE;
+ }
+ }
+ }
+ while ( resend && send_attempts < 3);
+
+ if (send_attempts >= 3)
+ {
+ llinfos << "sendPacket() bailed out of send!" << llendl;
+ return FALSE;
+ }
+
+ return success;
+}
+
+#endif
+
+//EOF
diff --git a/indra/llmessage/net.h b/indra/llmessage/net.h
new file mode 100644
index 0000000000..1044807ba6
--- /dev/null
+++ b/indra/llmessage/net.h
@@ -0,0 +1,50 @@
+/**
+ * @file net.h
+ * @brief Cross platform UDP network code.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_NET_H
+#define LL_NET_H
+
+class LLTimer;
+class LLHost;
+
+#define NET_BUFFER_SIZE (0x2000)
+
+// Request a free local port from the operating system
+#define NET_USE_OS_ASSIGNED_PORT 0
+
+// Returns 0 on success, non-zero on error.
+// Sets socket handler/descriptor, changes nPort if port requested is unavailable.
+S32 start_net(S32& socket_out, int& nPort);
+void end_net();
+
+// returns size of packet or -1 in case of error
+S32 receive_packet(int hSocket, char * receiveBuffer);
+
+BOOL send_packet(int hSocket, const char *sendBuffer, int size, U32 recipient, int nPort); // Returns TRUE on success.
+
+//void get_sender(char * tmp);
+LLHost get_sender();
+U32 get_sender_port();
+U32 get_sender_ip(void);
+
+const char* u32_to_ip_string(U32 ip); // Returns pointer to internal string buffer, "(bad IP addr)" on failure, cannot nest calls
+char* u32_to_ip_string(U32 ip, char *ip_string); // NULL on failure, ip_string on success, you must allocate at least MAXADDRSTR chars
+U32 ip_string_to_u32(const char* ip_string); // Wrapper for inet_addr()
+
+extern const char* LOOPBACK_ADDRESS_STRING;
+
+
+// useful MTU consts
+
+const S32 MTUBYTES = 1200; // 1500 = standard Ethernet MTU
+const S32 ETHERNET_MTU_BYTES = 1500;
+const S32 MTUBITS = MTUBYTES*8;
+const S32 MTUU32S = MTUBITS/32;
+
+
+#endif
diff --git a/indra/llmessage/partsyspacket.cpp b/indra/llmessage/partsyspacket.cpp
new file mode 100644
index 0000000000..4030cd815b
--- /dev/null
+++ b/indra/llmessage/partsyspacket.cpp
@@ -0,0 +1,1277 @@
+/**
+ * @file partsyspacket.cpp
+ * @brief Object for packing particle system initialization parameters
+ * before sending them over the network.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "partsyspacket.h"
+#include "imageids.h"
+
+// this function is global
+void gSetInitDataDefaults(LLPartInitData *setMe)
+{
+ U32 i;
+
+ //for(i = 0; i < 18; i++)
+ //{
+ // setMe->k[i] = 0.0f;
+ //}
+
+ //setMe->kill_p[0] = setMe->kill_p[1] = setMe->kill_p[2] = 0.0f;
+ //setMe->kill_p[3] = -0.2f; // time parameter, die when t= 5.0f
+ //setMe->kill_p[4] = 1.0f;
+ //setMe->kill_p[5] = -0.5f; // or radius == 2 (contracting)
+
+ //setMe->bounce_p[0] = setMe->bounce_p[1] =
+ // setMe->bounce_p[2] = setMe->bounce_p[3] = 0.0f;
+ //setMe->bounce_p[4] = 1.0f;
+
+ setMe->bounce_b = 1.0f;
+ // i just changed the meaning of bounce_b
+ // its now the attenuation from revlecting your velocity across the normal
+ // set by bounce_p
+
+ //setMe->pos_ranges[0] = setMe->pos_ranges[2] = setMe->pos_ranges[4] = -1.0f;
+ //setMe->pos_ranges[1] = setMe->pos_ranges[3] = setMe->pos_ranges[5] = 1.0f;
+
+ //setMe->vel_ranges[0] = setMe->vel_ranges[2] = setMe->vel_ranges[4] = -1.0f;
+ //setMe->vel_ranges[1] = setMe->vel_ranges[3] = setMe->vel_ranges[5] = 1.0f;
+
+ for(i = 0; i < 3; i++)
+ {
+ setMe->diffEqAlpha[i] = 0.0f;
+ setMe->diffEqScale[i] = 0.0f;
+ }
+
+ setMe->scale_range[0] = 1.00f;
+ setMe->scale_range[1] = 5.00f;
+ setMe->scale_range[2] = setMe->scale_range[3] = 0.0f;
+
+ setMe->alpha_range[0] = setMe->alpha_range[1] = 1.0f;
+ setMe->alpha_range[2] = setMe->alpha_range[3] = 0.0f;
+
+ setMe->vel_offset[0] = 0.0f;
+ setMe->vel_offset[1] = 0.0f;
+ setMe->vel_offset[2] = 0.0f;
+
+ // start dropping particles when I'm more then one sim away
+ setMe->mDistBeginFadeout = 256.0f;
+ setMe->mDistEndFadeout = 1.414f * 512.0f;
+ // stop displaying particles when I'm more then two sim diagonals away
+
+ setMe->mImageUuid = IMG_SHOT;
+
+ for(i = 0; i < 8; i++)
+ {
+ setMe->mFlags[i] = 0x00;
+ }
+
+ setMe->createMe = TRUE;
+
+ setMe->maxParticles = 25;
+ setMe->initialParticles = 25;
+
+ //These defaults are for an explosion - a short lived set of debris affected by gravity.
+ //Action flags default to PART_SYS_AFFECTED_BY_WIND + PART_SYS_AFFECTED_BY_GRAVITY + PART_SYS_DISTANCE_DEATH
+ setMe->mFlags[PART_SYS_ACTION_BYTE] = PART_SYS_AFFECTED_BY_WIND | PART_SYS_AFFECTED_BY_GRAVITY | PART_SYS_DISTANCE_DEATH;
+ setMe->mFlags[PART_SYS_KILL_BYTE] = PART_SYS_DISTANCE_DEATH + PART_SYS_TIME_DEATH;
+
+ setMe->killPlaneNormal[0] = 0.0f;setMe->killPlaneNormal[1] = 0.0f;setMe->killPlaneNormal[2] = 1.0f; //Straight up
+ setMe->killPlaneZ = 0.0f; //get local ground z as an approximation if turn on PART_SYS_KILL_PLANE
+ setMe->bouncePlaneNormal[0] = 0.0f;setMe->bouncePlaneNormal[1] = 0.0f;setMe->bouncePlaneNormal[2] = 1.0f; //Straight up
+ setMe->bouncePlaneZ = 0.0f; //get local ground z as an approximation if turn on PART_SYS_BOUNCE
+ setMe->spawnRange = 1.0f;
+ setMe->spawnFrequency = 0.0f; //Create the instant one dies
+ setMe->spawnFreqencyRange = 0.0f;
+ setMe->spawnDirection[0] = 0.0f;setMe->spawnDirection[1] = 0.0f;setMe->spawnDirection[2] = 1.0f; //Straight up
+ setMe->spawnDirectionRange = 1.0f; //global scattering
+ setMe->spawnVelocity = 0.75f;
+ setMe->spawnVelocityRange = 0.25f; //velocity +/- 0.25
+ setMe->speedLimit = 1.0f;
+
+ setMe->windWeight = 0.5f; //0.0f means looks like a heavy object (if gravity is on), 1.0f means light and fluffy
+ setMe->currentGravity[0] = 0.0f;setMe->currentGravity[1] = 0.0f;setMe->currentGravity[2] = -9.81f;
+ //This has to be constant to allow for compression
+
+ setMe->gravityWeight = 0.5f; //0.0f means boyed by air, 1.0f means it's a lead weight
+ setMe->globalLifetime = 0.0f; //Arbitrary, but default is no global die, so doesn't matter
+ setMe->individualLifetime = 5.0f;
+ setMe->individualLifetimeRange = 1.0f; //Particles last 5 secs +/- 1
+ setMe->alphaDecay = 1.0f; //normal alpha fadeout
+ setMe->scaleDecay = 0.0f; //no scale decay
+ setMe->distanceDeath = 10.0f; //die if hit unit radius
+ setMe->dampMotionFactor = 0.0f;
+
+ setMe->windDiffusionFactor[0] = 0.0f;
+ setMe->windDiffusionFactor[1] = 0.0f;
+ setMe->windDiffusionFactor[2] = 0.0f;
+}
+
+LLPartSysCompressedPacket::LLPartSysCompressedPacket()
+{
+ // default constructor for mDefaults called implicitly/automatically here
+ for(int i = 0; i < MAX_PART_SYS_PACKET_SIZE; i++)
+ {
+ mData[i] = '\0';
+ }
+
+ gSetInitDataDefaults(&mDefaults);
+}
+
+LLPartSysCompressedPacket::~LLPartSysCompressedPacket()
+{
+ // no dynamic data is stored by this class, do nothing.
+}
+
+void LLPartSysCompressedPacket::writeFlagByte(LLPartInitData *in)
+{
+ mData[0] = mData[1] = mData[2] = '\0';
+
+ U32 i;
+ //for(i = 1; i < 18; i++) {
+ // if(in->k[i] != mDefaults.k[i])
+ // {
+ // mData[0] |= PART_SYS_K_MASK;
+ // break;
+ // }
+ //}
+
+ if(in->killPlaneZ != mDefaults.killPlaneZ ||
+ in->killPlaneNormal[0] != mDefaults.killPlaneNormal[0] ||
+ in->killPlaneNormal[1] != mDefaults.killPlaneNormal[1] ||
+ in->killPlaneNormal[2] != mDefaults.killPlaneNormal[2] ||
+ in->distanceDeath != mDefaults.distanceDeath)
+ {
+ mData[0] |= PART_SYS_KILL_P_MASK;
+ }
+
+
+
+ if(in->bouncePlaneZ != mDefaults.bouncePlaneZ ||
+ in->bouncePlaneNormal[0] != mDefaults.bouncePlaneNormal[0] ||
+ in->bouncePlaneNormal[1] != mDefaults.bouncePlaneNormal[1] ||
+ in->bouncePlaneNormal[2] != mDefaults.bouncePlaneNormal[2])
+ {
+ mData[0] |= PART_SYS_BOUNCE_P_MASK;
+ }
+
+ if(in->bounce_b != mDefaults.bounce_b)
+ {
+ mData[0] |= PART_SYS_BOUNCE_B_MASK;
+ }
+
+
+ //if(in->pos_ranges[0] != mDefaults.pos_ranges[0] || in->pos_ranges[1] != mDefaults.pos_ranges[1] ||
+ // in->pos_ranges[2] != mDefaults.pos_ranges[2] || in->pos_ranges[3] != mDefaults.pos_ranges[3] ||
+ // in->pos_ranges[4] != mDefaults.pos_ranges[4] || in->pos_ranges[5] != mDefaults.pos_ranges[5])
+ //{
+ // mData[0] |= PART_SYS_POS_RANGES_MASK;
+ //}
+
+ //if(in->vel_ranges[0] != mDefaults.vel_ranges[0] || in->vel_ranges[1] != mDefaults.vel_ranges[1] ||
+ // in->vel_ranges[2] != mDefaults.vel_ranges[2] || in->vel_ranges[3] != mDefaults.vel_ranges[3] ||
+ // in->vel_ranges[4] != mDefaults.vel_ranges[4] || in->vel_ranges[5] != mDefaults.vel_ranges[5])
+ //{
+// mData[0] |= PART_SYS_VEL_RANGES_MASK;
+ //}
+
+
+ if(in->diffEqAlpha[0] != mDefaults.diffEqAlpha[0] ||
+ in->diffEqAlpha[1] != mDefaults.diffEqAlpha[1] ||
+ in->diffEqAlpha[2] != mDefaults.diffEqAlpha[2] ||
+ in->diffEqScale[0] != mDefaults.diffEqScale[0] ||
+ in->diffEqScale[1] != mDefaults.diffEqScale[1] ||
+ in->diffEqScale[2] != mDefaults.diffEqScale[2])
+ {
+ mData[0] |= PART_SYS_ALPHA_SCALE_DIFF_MASK;
+ }
+
+
+ if(in->scale_range[0] != mDefaults.scale_range[0] ||
+ in->scale_range[1] != mDefaults.scale_range[1] ||
+ in->scale_range[2] != mDefaults.scale_range[2] ||
+ in->scale_range[3] != mDefaults.scale_range[3])
+ {
+ mData[0] |= PART_SYS_SCALE_RANGE_MASK;
+ }
+
+
+ if(in->alpha_range[0] != mDefaults.alpha_range[0] ||
+ in->alpha_range[1] != mDefaults.alpha_range[1] ||
+ in->alpha_range[2] != mDefaults.alpha_range[2] ||
+ in->alpha_range[3] != mDefaults.alpha_range[3])
+ {
+ mData[2] |= PART_SYS_BYTE_3_ALPHA_MASK;
+ }
+
+ if(in->vel_offset[0] != mDefaults.vel_offset[0] ||
+ in->vel_offset[1] != mDefaults.vel_offset[1] ||
+ in->vel_offset[2] != mDefaults.vel_offset[2])
+ {
+ mData[0] |= PART_SYS_VEL_OFFSET_MASK;
+ }
+
+
+ if(in->mImageUuid != mDefaults.mImageUuid)
+ {
+ mData[0] |= PART_SYS_M_IMAGE_UUID_MASK;
+ }
+
+ for( i = 0; i < 8; i++)
+ {
+ if(in->mFlags[i])
+ {
+ mData[1] |= 1<<i;
+// llprintline("Flag \"%x\" gets byte \"%x\"\n", i<<i, in->mFlags[i]);
+ }
+ }
+
+
+ if(in->spawnRange != mDefaults.spawnRange ||
+ in->spawnFrequency != mDefaults.spawnFrequency ||
+ in->spawnFreqencyRange != mDefaults.spawnFreqencyRange ||
+ in->spawnDirection[0] != mDefaults.spawnDirection[0] ||
+ in->spawnDirection[1] != mDefaults.spawnDirection[1] ||
+ in->spawnDirection[2] != mDefaults.spawnDirection[2] ||
+ in->spawnDirectionRange != mDefaults.spawnDirectionRange ||
+ in->spawnVelocity != mDefaults.spawnVelocity ||
+ in->spawnVelocityRange != mDefaults.spawnVelocityRange)
+ {
+ mData[3] |= PART_SYS_BYTE_SPAWN_MASK;
+ }
+
+
+ if(in->windWeight != mDefaults.windWeight ||
+ in->currentGravity[0] != mDefaults.currentGravity[0] ||
+ in->currentGravity[1] != mDefaults.currentGravity[1] ||
+ in->currentGravity[2] != mDefaults.currentGravity[2] ||
+ in->gravityWeight != mDefaults.gravityWeight)
+ {
+ mData[3] |= PART_SYS_BYTE_ENVIRONMENT_MASK;
+ }
+
+
+ if(in->globalLifetime != mDefaults.globalLifetime ||
+ in->individualLifetime != mDefaults.individualLifetime ||
+ in->individualLifetimeRange != mDefaults.individualLifetimeRange)
+ {
+ mData[3] |= PART_SYS_BYTE_LIFESPAN_MASK;
+ }
+
+
+ if(in->speedLimit != mDefaults.speedLimit ||
+ in->alphaDecay != mDefaults.alphaDecay ||
+ in->scaleDecay != mDefaults.scaleDecay ||
+ in->dampMotionFactor != mDefaults.dampMotionFactor)
+ {
+ mData[3] |= PART_SYS_BYTE_DECAY_DAMP_MASK;
+ }
+
+ if(in->windDiffusionFactor[0] != mDefaults.windDiffusionFactor[0] ||
+ in->windDiffusionFactor[1] != mDefaults.windDiffusionFactor[1] ||
+ in->windDiffusionFactor[2] != mDefaults.windDiffusionFactor[2])
+ {
+ mData[3] |= PART_SYS_BYTE_WIND_DIFF_MASK;
+ }
+}
+
+F32 floatFromTwoBytes(S8 bMant, S8 bExp)
+{
+ F32 result = bMant;
+ while(bExp > 0)
+ {
+ result *= 2.0f;
+ bExp--;
+ }
+ while(bExp < 0)
+ {
+ result *= 0.5f;
+ bExp++;
+ }
+ return result;
+}
+
+void twoBytesFromFloat(F32 fIn, S8 &bMant, S8 &bExp)
+{
+ bExp = 0;
+ if(fIn > 127.0f)
+ {
+ fIn = 127.0f;
+ }
+ if(fIn < -127.0f)
+ {
+ fIn = -127.0f;
+ }
+ while(fIn < 64 && fIn > -64 && bExp > -127)
+ {
+ fIn *= 2.0f;
+ bExp--;
+ }
+ while((fIn > 128 || fIn < -128) && bExp < 127)
+ {
+ fIn *= 0.5f;
+ bExp++;
+ }
+ bMant = (S8)fIn;
+}
+
+
+
+/*
+U32 LLPartSysCompressedPacket::writeK(LLPartInitData *in, U32 startByte)
+{
+ U32 i, kFlag, i_mod_eight;
+ S8 bMant, bExp;
+
+ kFlag = startByte;
+
+ startByte += 3; // 3 bytes contain enough room for 18 flag bits
+ mData[kFlag] = 0x00;
+// llprintline("In the writeK\n");
+
+ i_mod_eight = 0;
+ for(i = 0; i < 18; i++)
+ {
+ if(in->k[i] != mDefaults.k[i])
+ {
+
+ mData[kFlag] |= 1<<i_mod_eight;
+ twoBytesFromFloat(in->k[i], bMant, bExp);
+
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ i_mod_eight++;
+ while(i_mod_eight >= 8)
+ {
+ kFlag++;
+ i_mod_eight -= 8;
+ }
+ }
+
+ return startByte;
+}*/
+
+U32 LLPartSysCompressedPacket::writeKill_p(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ twoBytesFromFloat(in->killPlaneNormal[0], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->killPlaneNormal[1], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->killPlaneNormal[2], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->killPlaneZ, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->distanceDeath, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeBounce_p(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ twoBytesFromFloat(in->bouncePlaneNormal[0], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->bouncePlaneNormal[1], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->bouncePlaneNormal[2], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+
+ twoBytesFromFloat(in->bouncePlaneZ, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeBounce_b(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ twoBytesFromFloat(in->bounce_b, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ return startByte;
+}
+
+//U32 LLPartSysCompressedPacket::writePos_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8) in->pos_ranges[i]; // float to int conversion (keep the sign)
+// mData[startByte++] = (U8)tmp; // signed to unsigned typecast
+// }
+// return startByte;
+//}
+
+//U32 LLPartSysCompressedPacket::writeVel_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8) in->vel_ranges[i]; // float to int conversion (keep the sign)
+// mData[startByte++] = (U8)tmp; // signed to unsigned typecast
+// }
+// return startByte;
+//}
+
+U32 LLPartSysCompressedPacket::writeAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->diffEqAlpha[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->diffEqScale[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeScale_range(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ twoBytesFromFloat(in->scale_range[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+
+U32 LLPartSysCompressedPacket::writeAlpha_range(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ twoBytesFromFloat(in->alpha_range[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeVelocityOffset(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->vel_offset[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeUUID(LLPartInitData *in, U32 startByte)
+{
+ U8 * bufPtr = mData + startByte;
+ if(in->mImageUuid == IMG_SHOT) {
+ mData[startByte++] = 0x01;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_SPARK) {
+ mData[startByte++] = 0x02;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_BIG_EXPLOSION_1) {
+ mData[startByte++] = 0x03;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_BIG_EXPLOSION_2) {
+ mData[startByte++] = 0x04;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_SMOKE_POOF) {
+ mData[startByte++] = 0x05;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_FIRE) {
+ mData[startByte++] = 0x06;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_EXPLOSION) {
+ mData[startByte++] = 0x07;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_EXPLOSION_2) {
+ mData[startByte++] = 0x08;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_EXPLOSION_3) {
+ mData[startByte++] = 0x09;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_EXPLOSION_4) {
+ mData[startByte++] = 0x0A;
+ return startByte;
+ }
+
+ mData[startByte++] = 0x00; // flag for "read whole UUID"
+
+ memcpy(bufPtr, in->mImageUuid.mData, 16); /* Flawfinder: ignore */
+ return (startByte+16);
+}
+
+U32 LLPartSysCompressedPacket::writeSpawn(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+
+ twoBytesFromFloat(in->spawnRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnFrequency, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnFreqencyRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+
+
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->spawnDirection[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+
+ twoBytesFromFloat(in->spawnDirectionRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnVelocity, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnVelocityRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeEnvironment(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+
+ twoBytesFromFloat(in->windWeight, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->currentGravity[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+
+ twoBytesFromFloat(in->gravityWeight, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeLifespan(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+
+ twoBytesFromFloat(in->globalLifetime, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->individualLifetime, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->individualLifetimeRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+
+U32 LLPartSysCompressedPacket::writeDecayDamp(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+
+ twoBytesFromFloat(in->speedLimit, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->alphaDecay, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->scaleDecay, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->dampMotionFactor, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeWindDiffusionFactor(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+
+ twoBytesFromFloat(in->windDiffusionFactor[0], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->windDiffusionFactor[1], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->windDiffusionFactor[2], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+
+
+
+
+
+/*
+U32 LLPartSysCompressedPacket::readK(LLPartInitData *in, U32 startByte)
+{
+ U32 i, i_mod_eight, kFlag;
+ S8 bMant, bExp; // 1 bytes mantissa and exponent for a float
+ kFlag = startByte;
+ startByte += 3; // 3 bytes has enough room for 18 bits
+
+ i_mod_eight = 0;
+ for(i = 0; i < 18; i++)
+ {
+ if(mData[kFlag]&(1<<i_mod_eight))
+ {
+
+
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+
+
+ in->k[i] = floatFromTwoBytes(bMant, bExp); // much tighter platform-independent
+ // way to ship floats
+
+ }
+ i_mod_eight++;
+ if(i_mod_eight >= 8)
+ {
+ i_mod_eight -= 8;
+ kFlag++;
+ }
+ }
+
+ return startByte;
+}
+*/
+
+U32 LLPartSysCompressedPacket::readKill_p(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneNormal[0] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneNormal[1] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneNormal[2] = floatFromTwoBytes(bMant, bExp);
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneZ = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->distanceDeath = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readBounce_p(LLPartInitData *in, U32 startByte)
+{
+
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneNormal[0] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneNormal[1] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneNormal[2] = floatFromTwoBytes(bMant, bExp);
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneZ = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readBounce_b(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bounce_b = floatFromTwoBytes(bMant, bExp);
+ return startByte;
+}
+
+
+//U32 LLPartSysCompressedPacket::readPos_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8)mData[startByte++];
+// in->pos_ranges[i] = tmp;
+// }
+// return startByte;
+//}
+
+//U32 LLPartSysCompressedPacket::readVel_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8)mData[startByte++];
+// in->vel_ranges[i] = tmp;
+// }
+// return startByte;
+//}
+
+
+
+U32 LLPartSysCompressedPacket::readAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->diffEqAlpha[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->diffEqScale[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readAlpha_range(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 4; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->alpha_range[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readScale_range(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 4; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->scale_range[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readVelocityOffset(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->vel_offset[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readUUID(LLPartInitData *in, U32 startByte)
+{
+ U8 * bufPtr = mData + startByte;
+
+ if(mData[startByte] == 0x01)
+ {
+ in->mImageUuid = IMG_SHOT;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x02)
+ {
+ in->mImageUuid = IMG_SPARK;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x03)
+ {
+ in->mImageUuid = IMG_BIG_EXPLOSION_1;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x04)
+ {
+ in->mImageUuid = IMG_BIG_EXPLOSION_2;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x05)
+ {
+ in->mImageUuid = IMG_SMOKE_POOF;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x06)
+ {
+ in->mImageUuid = IMG_FIRE;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x07)
+ {
+ in->mImageUuid = IMG_EXPLOSION;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x08)
+ {
+ in->mImageUuid = IMG_EXPLOSION_2;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x09)
+ {
+ in->mImageUuid = IMG_EXPLOSION_3;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x0A)
+ {
+ in->mImageUuid = IMG_EXPLOSION_4;
+ return startByte+1;
+ }
+
+ startByte++; // cause we actually have to read the UUID now.
+ memcpy(in->mImageUuid.mData, bufPtr, 16); /* Flawfinder: ignore */
+ return (startByte+16);
+}
+
+
+
+
+U32 LLPartSysCompressedPacket::readSpawn(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ U32 i;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnRange = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnFrequency = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnFreqencyRange = floatFromTwoBytes(bMant, bExp);
+
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnDirection[i] = floatFromTwoBytes(bMant, bExp);
+ }
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnDirectionRange = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnVelocity = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnVelocityRange = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readEnvironment(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ U32 i;
+
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->windWeight = floatFromTwoBytes(bMant, bExp);
+
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->currentGravity[i] = floatFromTwoBytes(bMant, bExp);
+ }
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->gravityWeight = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readLifespan(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->globalLifetime = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->individualLifetime = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->individualLifetimeRange = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readDecayDamp(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->speedLimit = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->alphaDecay = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->scaleDecay = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->dampMotionFactor = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readWindDiffusionFactor(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->windDiffusionFactor[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+BOOL LLPartSysCompressedPacket::fromLLPartInitData(LLPartInitData *in, U32 &bytesUsed)
+{
+
+ writeFlagByte(in);
+ U32 currByte = 4;
+
+// llprintline("calling \"fromLLPartInitData\"\n");
+
+ //if(mData[0] & PART_SYS_K_MASK)
+ //{
+ // currByte = writeK(in, 3); // first 3 bytes are reserved for header data
+ //}
+
+
+
+ if(mData[0] & PART_SYS_KILL_P_MASK)
+ {
+ currByte = writeKill_p(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_P_MASK)
+ {
+ currByte = writeBounce_p(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_B_MASK)
+ {
+ currByte = writeBounce_b(in, currByte);
+ }
+
+ //if(mData[0] & PART_SYS_POS_RANGES_MASK)
+ //{
+ // currByte = writePos_ranges(in, currByte);
+ //}
+
+ //if(mData[0] & PART_SYS_VEL_RANGES_MASK)
+ //{
+ // currByte = writeVel_ranges(in, currByte);
+ //}
+
+ if(mData[0] & PART_SYS_ALPHA_SCALE_DIFF_MASK)
+ {
+ currByte = writeAlphaScaleDiffEqn_range(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_SCALE_RANGE_MASK)
+ {
+ currByte = writeScale_range(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_VEL_OFFSET_MASK)
+ {
+ currByte = writeVelocityOffset(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_M_IMAGE_UUID_MASK)
+ {
+ currByte = writeUUID(in, currByte);
+ }
+
+
+ if(mData[3] & PART_SYS_BYTE_SPAWN_MASK)
+ {
+ currByte = writeSpawn(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_ENVIRONMENT_MASK)
+ {
+ currByte = writeEnvironment(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_LIFESPAN_MASK)
+ {
+ currByte = writeLifespan(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_DECAY_DAMP_MASK)
+ {
+ currByte = writeDecayDamp(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_WIND_DIFF_MASK)
+ {
+ currByte = writeWindDiffusionFactor(in, currByte);
+ }
+
+
+ if(mData[2] & PART_SYS_BYTE_3_ALPHA_MASK)
+ {
+ currByte = writeAlpha_range(in, currByte);
+ }
+
+ mData[currByte++] = (U8)in->maxParticles;
+ mData[currByte++] = (U8)in->initialParticles;
+
+
+ U32 flagFlag = 1; // flag indicating which flag bytes are non-zero
+ // yeah, I know, the name sounds funny
+ for(U32 i = 0; i < 8; i++)
+ {
+
+// llprintline("Flag \"%x\" gets byte \"%x\"\n", flagFlag, in->mFlags[i]);
+ if(mData[1] & flagFlag)
+ {
+ mData[currByte++] = in->mFlags[i];
+// llprintline("and is valid...\n");
+ }
+ flagFlag <<= 1;
+ }
+
+ bytesUsed = mNumBytes = currByte;
+
+
+
+// llprintline("returning from \"fromLLPartInitData\" with %d bytes\n", bytesUsed);
+
+ return TRUE;
+}
+
+BOOL LLPartSysCompressedPacket::toLLPartInitData(LLPartInitData *out, U32 *bytesUsed)
+{
+ U32 currByte = 4;
+
+ gSetInitDataDefaults(out);
+
+ if(mData[0] & PART_SYS_KILL_P_MASK)
+ {
+ currByte = readKill_p(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_P_MASK)
+ {
+ currByte = readBounce_p(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_B_MASK)
+ {
+ currByte = readBounce_b(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_ALPHA_SCALE_DIFF_MASK)
+ {
+ currByte = readAlphaScaleDiffEqn_range(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_SCALE_RANGE_MASK)
+ {
+ currByte = readScale_range(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_VEL_OFFSET_MASK)
+ {
+ currByte = readVelocityOffset(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_M_IMAGE_UUID_MASK)
+ {
+ currByte = readUUID(out, currByte);
+ }
+
+
+ if(mData[3] & PART_SYS_BYTE_SPAWN_MASK)
+ {
+ currByte = readSpawn(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_ENVIRONMENT_MASK)
+ {
+ currByte = readEnvironment(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_LIFESPAN_MASK)
+ {
+ currByte = readLifespan(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_DECAY_DAMP_MASK)
+ {
+ currByte = readDecayDamp(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_WIND_DIFF_MASK)
+ {
+ currByte = readWindDiffusionFactor(out, currByte);
+ }
+
+ if(mData[2] & PART_SYS_BYTE_3_ALPHA_MASK)
+ {
+ currByte = readAlpha_range(out, currByte);
+ }
+
+ out->maxParticles = mData[currByte++];
+ out->initialParticles = mData[currByte++];
+
+ U32 flagFlag = 1; // flag indicating which flag bytes are non-zero
+ // yeah, I know, the name sounds funny
+ for(U32 i = 0; i < 8; i++)
+ {
+ flagFlag = 1<<i;
+
+ if((mData[1] & flagFlag))
+ {
+ out->mFlags[i] = mData[currByte++];
+ }
+ }
+
+ *bytesUsed = currByte;
+ return TRUE;
+}
+
+BOOL LLPartSysCompressedPacket::fromUnsignedBytes(U8 *in, U32 bytesUsed)
+{
+ if ((in != NULL) && (bytesUsed <= sizeof(mData)))
+ {
+ memcpy(mData, in, bytesUsed);
+ mNumBytes = bytesUsed;
+ return TRUE;
+ }
+ else
+ {
+ llerrs << "NULL input data or number of bytes exceed mData size" << llendl;
+ return FALSE;
+ }
+}
+
+
+U32 LLPartSysCompressedPacket::bufferSize()
+{
+ return mNumBytes;
+}
+
+BOOL LLPartSysCompressedPacket::toUnsignedBytes(U8 *out)
+{
+ memcpy(out, mData, mNumBytes); /* Flawfinder: ignore */
+ return TRUE;
+}
+
+U8 * LLPartSysCompressedPacket::getBytePtr()
+{
+ return mData;
+}
+
+
diff --git a/indra/llmessage/partsyspacket.h b/indra/llmessage/partsyspacket.h
new file mode 100644
index 0000000000..c98eb42bfb
--- /dev/null
+++ b/indra/llmessage/partsyspacket.h
@@ -0,0 +1,243 @@
+/**
+ * @file partsyspacket.h
+ * @brief Object for packing particle system initialization parameters
+ * before sending them over the network
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PARTSYSPACKET_H
+#define LL_PARTSYSPACKET_H
+
+#include "lluuid.h"
+
+// Particle system stuff
+
+const U64 PART_SYS_MAX_TIME_IN_USEC = 1000000; // 1 second, die not quite near instantaneously
+
+
+// this struct is for particle system initialization parameters
+// I'm breaking some rules here, but I need a storage structure to hold initialization data
+// for these things. Sorry guys, they're not simple enough (yet) to avoid this cleanly
+struct LLPartInitData {
+ // please do not add functions to this class -- data only!
+ //F32 k[18]; // first 9 --> x,y,z last 9 --> scale, alpha, rot
+ //F32 kill_p[6]; // last one is for particles that die when they reach a spherical bounding radius
+ //F32 kill_plane[3];
+ //F32 bounce_p[5];
+ F32 bounce_b; // recently changed
+ // no need to store orientation and position here, as they're sent over seperately
+ //F32 pos_ranges[6];
+ //F32 vel_ranges[6];
+ F32 scale_range[4];
+ F32 alpha_range[4];
+ F32 vel_offset[3]; //new - more understandable!
+
+ F32 mDistBeginFadeout; // for fadeout LOD optimization
+ F32 mDistEndFadeout;
+
+ LLUUID mImageUuid;
+ //U8 n; // number of particles
+ U8 mFlags[8]; // for miscellaneous data --> its interpretation can change at my whim!
+ U8 createMe; // do I need to be created? or has the work allready been done?
+ //ActionFlag is now mFlags[PART_SYS_ACTION_BYTE]
+ //Spawn point is initially object creation center
+
+ F32 diffEqAlpha[3];
+ F32 diffEqScale[3];
+
+ U8 maxParticles;
+ //How many particles exist at any time within the system?
+ U8 initialParticles;
+ //How many particles exist when the system is created?
+ F32 killPlaneZ;
+ //For simplicity assume the XY plane, so this sets an altitude at which to die
+ F32 killPlaneNormal[3];
+ //Normal if not planar XY
+ F32 bouncePlaneZ;
+ //For simplicity assume the XY plane, so this sets an altitude at which to bounce
+ F32 bouncePlaneNormal[3];
+ //Normal if not planar XY
+ F32 spawnRange;
+ //Range of emission points about the mSpawnPoint
+ F32 spawnFrequency;
+ //Required if the system is to spawn new particles.
+ //This variable determines the time after a particle dies when it is respawned.
+ F32 spawnFreqencyRange;
+ //Determines the random range of time until a new particle is spawned.
+ F32 spawnDirection[3];
+ //Direction vector giving the mean direction in which particles are spawned
+ F32 spawnDirectionRange;
+ //Direction limiting the angular range of emissions about the mean direction. 1.0f means everywhere, 0.0f means uni-directional
+ F32 spawnVelocity;
+ //The mean speed at which particles are emitted
+ F32 spawnVelocityRange;
+ //The range of speeds about the mean at which particles are emitted.
+ F32 speedLimit;
+ //Used to constrain particle maximum velocity
+ F32 windWeight;
+ //How much of an effect does wind have
+ F32 currentGravity[3];
+ //Gravity direction used in update calculations
+ F32 gravityWeight;
+ //How much of an effect does gravity have
+ F32 globalLifetime;
+ //If particles re-spawn, a system can exist forever.
+ //If (ActionFlags & PART_SYS_GLOBAL_DIE) is TRUE this variable is used to determine how long the system lasts.
+ F32 individualLifetime;
+ //How long does each particle last if nothing else happens to it
+ F32 individualLifetimeRange;
+ //Range of variation in individual lifetimes
+ F32 alphaDecay;
+ //By what factor does alpha decrease as the lifetime of a particle is approached.
+ F32 scaleDecay;
+ //By what factor does scale decrease as the lifetime of a particle is approached.
+ F32 distanceDeath;
+ //With the increased functionality, particle systems can expand to indefinite size
+ //(e.g. wind can chaotically move particles into a wide spread).
+ //To avoid particles exceeding normal object size constraints,
+ //set the PART_SYS_DISTANCE_DEATH flag, and set a distance value here, representing a radius around the spawn point.
+ F32 dampMotionFactor;
+ //How much to damp motion
+ F32 windDiffusionFactor[3];
+ //Change the size and alpha of particles as wind speed increases (scale gets bigger, alpha smaller)
+};
+
+// constants for setting flag values
+// BYTES are in range 0-8, bits are in range 2^0 - 2^8 and can only be powers of two
+const int PART_SYS_NO_Z_BUFFER_BYTE = 0; // option to turn off z-buffer when rendering
+const int PART_SYS_NO_Z_BUFFER_BIT = 2; // particle systems --
+// I advise against using this, as it looks bad in every case I've tried
+
+const int PART_SYS_SLOW_ANIM_BYTE = 0; // slow animation down by a factor of 10
+const int PART_SYS_SLOW_ANIM_BIT = 1; // useful for tweaking anims during debugging
+
+const int PART_SYS_FOLLOW_VEL_BYTE = 0; // indicates whether to orient sprites towards
+const int PART_SYS_FOLLOW_VEL_BIT = 4; // their velocity vector -- default is FALSE
+
+const int PART_SYS_IS_LIGHT_BYTE = 0; // indicates whether a particular particle system
+const int PART_SYS_IS_LIGHT_BIT = 8; // is also a light object -- for andrew
+// should deprecate this once there is a general method for setting light properties of objects
+
+const int PART_SYS_SPAWN_COPY_BYTE = 0; // indicates whether to spawn baby particle systems on
+const int PART_SYS_SPAWN_COPY_BIT = 0x10; // particle death -- intended for smoke trails
+
+const int PART_SYS_COPY_VEL_BYTE = 0; // indicates whether baby particle systems inherit parents vel
+const int PART_SYS_COPY_VEL_BIT = 0x20; // (by default they don't)
+
+const int PART_SYS_INVISIBLE_BYTE = 0; // optional -- turn off display, just simulate
+const int PART_SYS_INVISIBLE_BIT = 0x40; // useful for smoke trails
+
+const int PART_SYS_ADAPT_TO_FRAMERATE_BYTE = 0; // drop sprites from render call proportionally
+const int PART_SYS_ADAPT_TO_FRAMERATE_BIT = 0x80; // to how far we are below 60 fps
+
+
+// 26 September 2001 - not even big enough to hold all changes, so should enlarge anyway
+//const U16 MAX_PART_SYS_PACKET_SIZE = 180;
+const U16 MAX_PART_SYS_PACKET_SIZE = 256;
+
+//const U8 PART_SYS_K_MASK = 0x01;
+const U8 PART_SYS_KILL_P_MASK = 0x02;
+const U8 PART_SYS_BOUNCE_P_MASK = 0x04;
+const U8 PART_SYS_BOUNCE_B_MASK = 0x08;
+//const U8 PART_SYS_POS_RANGES_MASK = 0x10;
+//const U8 PART_SYS_VEL_RANGES_MASK = 0x20;
+const U8 PART_SYS_VEL_OFFSET_MASK = 0x10; //re-use one of the original slots now commented out
+const U8 PART_SYS_ALPHA_SCALE_DIFF_MASK = 0x20; //re-use one of the original slots now commented out
+const U8 PART_SYS_SCALE_RANGE_MASK = 0x40;
+const U8 PART_SYS_M_IMAGE_UUID_MASK = 0x80;
+const U8 PART_SYS_BYTE_3_ALPHA_MASK = 0x01; // wrapped around, didn't we?
+
+const U8 PART_SYS_BYTE_SPAWN_MASK = 0x01;
+const U8 PART_SYS_BYTE_ENVIRONMENT_MASK = 0x02;
+const U8 PART_SYS_BYTE_LIFESPAN_MASK = 0x04;
+const U8 PART_SYS_BYTE_DECAY_DAMP_MASK = 0x08;
+const U8 PART_SYS_BYTE_WIND_DIFF_MASK = 0x10;
+
+
+// 26 September 2001 - new constants for mActionFlags
+const int PART_SYS_ACTION_BYTE = 1;
+const U8 PART_SYS_SPAWN = 0x01;
+const U8 PART_SYS_BOUNCE = 0x02;
+const U8 PART_SYS_AFFECTED_BY_WIND = 0x04;
+const U8 PART_SYS_AFFECTED_BY_GRAVITY = 0x08;
+const U8 PART_SYS_EVALUATE_WIND_PER_PARTICLE = 0x10;
+const U8 PART_SYS_DAMP_MOTION = 0x20;
+const U8 PART_SYS_WIND_DIFFUSION = 0x40;
+
+// 26 September 2001 - new constants for mKillFlags
+const int PART_SYS_KILL_BYTE = 2;
+const U8 PART_SYS_KILL_PLANE = 0x01;
+const U8 PART_SYS_GLOBAL_DIE = 0x02;
+const U8 PART_SYS_DISTANCE_DEATH = 0x04;
+const U8 PART_SYS_TIME_DEATH = 0x08;
+
+
+// global, because the sim-side also calls it in the LLPartInitDataFactory
+
+
+void gSetInitDataDefaults(LLPartInitData *setMe);
+
+class LLPartSysCompressedPacket
+{
+public:
+ LLPartSysCompressedPacket();
+ ~LLPartSysCompressedPacket();
+ BOOL fromLLPartInitData(LLPartInitData *in, U32 &bytesUsed);
+ BOOL toLLPartInitData(LLPartInitData *out, U32 *bytesUsed);
+ BOOL fromUnsignedBytes(U8 *in, U32 bytesUsed);
+ BOOL toUnsignedBytes(U8 *out);
+ U32 bufferSize();
+ U8 *getBytePtr();
+
+protected:
+ U8 mData[MAX_PART_SYS_PACKET_SIZE];
+ U32 mNumBytes;
+ LLPartInitData mDefaults; // this is intended to hold default LLPartInitData values
+ // please do not modify it
+ LLPartInitData mWorkingCopy; // uncompressed data I'm working with
+
+protected:
+ // private functions (used only to break up code)
+ void writeFlagByte(LLPartInitData *in);
+ //U32 writeK(LLPartInitData *in, U32 startByte);
+ U32 writeKill_p(LLPartInitData *in, U32 startByte);
+ U32 writeBounce_p(LLPartInitData *in, U32 startByte);
+ U32 writeBounce_b(LLPartInitData *in, U32 startByte);
+ //U32 writePos_ranges(LLPartInitData *in, U32 startByte);
+ //U32 writeVel_ranges(LLPartInitData *in, U32 startByte);
+ U32 writeAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte);
+ U32 writeScale_range(LLPartInitData *in, U32 startByte);
+ U32 writeAlpha_range(LLPartInitData *in, U32 startByte);
+ U32 writeUUID(LLPartInitData *in, U32 startByte);
+
+ U32 writeVelocityOffset(LLPartInitData *in, U32 startByte);
+ U32 writeSpawn(LLPartInitData *in, U32 startByte); //all spawn data
+ U32 writeEnvironment(LLPartInitData *in, U32 startByte); //wind and gravity
+ U32 writeLifespan(LLPartInitData *in, U32 startByte); //lifespan data - individual and global
+ U32 writeDecayDamp(LLPartInitData *in, U32 startByte); //alpha and scale, and motion damp
+ U32 writeWindDiffusionFactor(LLPartInitData *in, U32 startByte);
+
+
+ //U32 readK(LLPartInitData *in, U32 startByte);
+ U32 readKill_p(LLPartInitData *in, U32 startByte);
+ U32 readBounce_p(LLPartInitData *in, U32 startByte);
+ U32 readBounce_b(LLPartInitData *in, U32 startByte);
+ //U32 readPos_ranges(LLPartInitData *in, U32 startByte);
+ //U32 readVel_ranges(LLPartInitData *in, U32 startByte);
+ U32 readAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte);
+ U32 readScale_range(LLPartInitData *in, U32 startByte);
+ U32 readAlpha_range(LLPartInitData *in, U32 startByte);
+ U32 readUUID(LLPartInitData *in, U32 startByte);
+
+ U32 readVelocityOffset(LLPartInitData *in, U32 startByte);
+ U32 readSpawn(LLPartInitData *in, U32 startByte); //all spawn data
+ U32 readEnvironment(LLPartInitData *in, U32 startByte); //wind and gravity
+ U32 readLifespan(LLPartInitData *in, U32 startByte); //lifespan data - individual and global
+ U32 readDecayDamp(LLPartInitData *in, U32 startByte); //alpha and scale, and motion damp
+ U32 readWindDiffusionFactor(LLPartInitData *in, U32 startByte);
+};
+
+#endif
+
diff --git a/indra/llmessage/patch_code.cpp b/indra/llmessage/patch_code.cpp
new file mode 100644
index 0000000000..c8ebac53e7
--- /dev/null
+++ b/indra/llmessage/patch_code.cpp
@@ -0,0 +1,390 @@
+/**
+ * @file patch_code.cpp
+ * @brief Encode patch DCT data into bitcode.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+#include "bitpack.h"
+
+U32 gPatchSize, gWordBits;
+
+void init_patch_coding(LLBitPack &bitpack)
+{
+ bitpack.resetBitPacking();
+}
+
+void code_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp)
+{
+#ifdef LL_BIG_ENDIAN
+ U8 *stride = (U8 *)&gopp->stride;
+ bitpack.bitPack(&(stride[1]), 8);
+ bitpack.bitPack(&(stride[0]), 8);
+#else
+ bitpack.bitPack((U8 *)&gopp->stride, 16);
+#endif
+ bitpack.bitPack((U8 *)&gopp->patch_size, 8);
+ bitpack.bitPack((U8 *)&gopp->layer_type, 8);
+
+ gPatchSize = gopp->patch_size;
+}
+
+void code_patch_header(LLBitPack &bitpack, LLPatchHeader *ph, S32 *patch)
+{
+ S32 i, j, temp, patch_size = gPatchSize, wbits = (ph->quant_wbits & 0xf) + 2;
+ U32 max_wbits = wbits + 5, min_wbits = wbits>>1;
+
+ wbits = min_wbits;
+
+ for (i = 0; i < (int) patch_size*patch_size; i++)
+ {
+ temp = patch[i];
+ if (temp)
+ {
+ if (temp < 0)
+ temp *= -1;
+ for (j = max_wbits; j > (int) min_wbits; j--)
+ {
+ if (temp & (1<<j))
+ {
+ if (j > wbits)
+ wbits = j;
+ break;
+ }
+ }
+ }
+ }
+
+ wbits += 1;
+
+ ph->quant_wbits &= 0xf0;
+
+ if ( (wbits > 17)
+ ||(wbits < 2))
+ {
+ llerrs << "Bits needed per word in code_patch_header out of legal range. Adjust compression quatization." << llendl;
+ }
+
+ ph->quant_wbits |= (wbits - 2);
+
+ bitpack.bitPack((U8 *)&ph->quant_wbits, 8);
+#ifdef LL_BIG_ENDIAN
+ U8 *offset = (U8 *)&ph->dc_offset;
+ bitpack.bitPack(&(offset[3]), 8);
+ bitpack.bitPack(&(offset[2]), 8);
+ bitpack.bitPack(&(offset[1]), 8);
+ bitpack.bitPack(&(offset[0]), 8);
+#else
+ bitpack.bitPack((U8 *)&ph->dc_offset, 32);
+#endif
+#ifdef LL_BIG_ENDIAN
+ U8 *range = (U8 *)&ph->range;
+ bitpack.bitPack(&(range[1]), 8);
+ bitpack.bitPack(&(range[0]), 8);
+#else
+ bitpack.bitPack((U8 *)&ph->range, 16);
+#endif
+#ifdef LL_BIG_ENDIAN
+ U8 *ids = (U8 *)&ph->patchids;
+ bitpack.bitPack(&(ids[1]), 8);
+ bitpack.bitPack(&(ids[0]), 2);
+#else
+ bitpack.bitPack((U8 *)&ph->patchids, 10);
+#endif
+
+ gWordBits = wbits;
+}
+
+void code_end_of_data(LLBitPack &bitpack)
+{
+ bitpack.bitPack((U8 *)&END_OF_PATCHES, 8);
+}
+
+void code_patch(LLBitPack &bitpack, S32 *patch, S32 postquant)
+{
+ S32 i, j, patch_size = gPatchSize, wbits = gWordBits;
+ S32 temp;
+ BOOL b_eob;
+
+ if ( (postquant > patch_size*patch_size)
+ ||(postquant < 0))
+ {
+ llerrs << "Bad postquant in code_patch!" << llendl;
+ }
+
+ if (postquant)
+ patch[patch_size*patch_size - postquant] = 0;
+
+ for (i = 0; i < patch_size*patch_size; i++)
+ {
+ b_eob = FALSE;
+ temp = patch[i];
+ if (!temp)
+ {
+ b_eob = TRUE;
+ for (j = i; j < patch_size*patch_size - postquant; j++)
+ {
+ if (patch[j])
+ {
+ b_eob = FALSE;
+ break;
+ }
+ }
+ if (b_eob)
+ {
+ bitpack.bitPack((U8 *)&ZERO_EOB, 2);
+ return;
+ }
+ else
+ {
+ bitpack.bitPack((U8 *)&ZERO_CODE, 1);
+ }
+ }
+ else
+ {
+ if (temp < 0)
+ {
+ temp *= -1;
+ if (temp > (1<<wbits))
+ {
+ temp = (1<<wbits);
+// printf("patch quatization exceeding allowable bits!");
+ }
+ bitpack.bitPack((U8 *)&NEGATIVE_VALUE, 3);
+ bitpack.bitPack((U8 *)&temp, wbits);
+ }
+ else
+ {
+ if (temp > (1<<wbits))
+ {
+ temp = (1<<wbits);
+// printf("patch quatization exceeding allowable bits!");
+ }
+ bitpack.bitPack((U8 *)&POSITIVE_VALUE, 3);
+ bitpack.bitPack((U8 *)&temp, wbits);
+ }
+ }
+ }
+}
+
+
+void end_patch_coding(LLBitPack &bitpack)
+{
+ bitpack.flushBitPack();
+}
+
+void init_patch_decoding(LLBitPack &bitpack)
+{
+ bitpack.resetBitPacking();
+}
+
+void decode_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp)
+{
+ U16 retvalu16;
+
+ retvalu16 = 0;
+#ifdef LL_BIG_ENDIAN
+ U8 *ret = (U8 *)&retvalu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 8);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu16, 16);
+#endif
+ gopp->stride = retvalu16;
+
+ U8 retvalu8 = 0;
+ bitpack.bitUnpack(&retvalu8, 8);
+ gopp->patch_size = retvalu8;
+
+ retvalu8 = 0;
+ bitpack.bitUnpack(&retvalu8, 8);
+ gopp->layer_type = retvalu8;
+
+ gPatchSize = gopp->patch_size;
+}
+
+void decode_patch_header(LLBitPack &bitpack, LLPatchHeader *ph)
+{
+ U8 retvalu8;
+
+ retvalu8 = 0;
+ bitpack.bitUnpack(&retvalu8, 8);
+ ph->quant_wbits = retvalu8;
+
+ if (END_OF_PATCHES == ph->quant_wbits)
+ {
+ // End of data, blitz the rest.
+ ph->dc_offset = 0;
+ ph->range = 0;
+ ph->patchids = 0;
+ return;
+ }
+
+ U32 retvalu32 = 0;
+#ifdef LL_BIG_ENDIAN
+ U8 *ret = (U8 *)&retvalu32;
+ bitpack.bitUnpack(&(ret[3]), 8);
+ bitpack.bitUnpack(&(ret[2]), 8);
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 8);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu32, 32);
+#endif
+ ph->dc_offset = *(F32 *)&retvalu32;
+
+ U16 retvalu16 = 0;
+#ifdef LL_BIG_ENDIAN
+ ret = (U8 *)&retvalu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 8);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu16, 16);
+#endif
+ ph->range = retvalu16;
+
+ retvalu16 = 0;
+#ifdef LL_BIG_ENDIAN
+ ret = (U8 *)&retvalu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 2);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu16, 10);
+#endif
+ ph->patchids = retvalu16;
+
+ gWordBits = (ph->quant_wbits & 0xf) + 2;
+}
+
+void decode_patch(LLBitPack &bitpack, S32 *patches)
+{
+#ifdef LL_BIG_ENDIAN
+ S32 i, j, patch_size = gPatchSize, wbits = gWordBits;
+ U8 tempu8;
+ U16 tempu16;
+ U32 tempu32;
+ for (i = 0; i < patch_size*patch_size; i++)
+ {
+ bitpack.bitUnpack((U8 *)&tempu8, 1);
+ if (tempu8)
+ {
+ // either 0 EOB or Value
+ bitpack.bitUnpack((U8 *)&tempu8, 1);
+ if (tempu8)
+ {
+ // value
+ bitpack.bitUnpack((U8 *)&tempu8, 1);
+ if (tempu8)
+ {
+ // negative
+ patches[i] = -1;
+ }
+ else
+ {
+ // positive
+ patches[i] = 1;
+ }
+ if (wbits <= 8)
+ {
+ bitpack.bitUnpack((U8 *)&tempu8, wbits);
+ patches[i] *= tempu8;
+ }
+ else if (wbits <= 16)
+ {
+ tempu16 = 0;
+ U8 *ret = (U8 *)&tempu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), wbits - 8);
+ patches[i] *= tempu16;
+ }
+ else if (wbits <= 24)
+ {
+ tempu32 = 0;
+ U8 *ret = (U8 *)&tempu32;
+ bitpack.bitUnpack(&(ret[2]), 8);
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), wbits - 16);
+ patches[i] *= tempu32;
+ }
+ else if (wbits <= 32)
+ {
+ tempu32 = 0;
+ U8 *ret = (U8 *)&tempu32;
+ bitpack.bitUnpack(&(ret[3]), 8);
+ bitpack.bitUnpack(&(ret[2]), 8);
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), wbits - 24);
+ patches[i] *= tempu32;
+ }
+ }
+ else
+ {
+ for (j = i; j < patch_size*patch_size; j++)
+ {
+ patches[j] = 0;
+ }
+ return;
+ }
+ }
+ else
+ {
+ patches[i] = 0;
+ }
+ }
+#else
+ S32 i, j, patch_size = gPatchSize, wbits = gWordBits;
+ U32 temp;
+ for (i = 0; i < patch_size*patch_size; i++)
+ {
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, 1);
+ if (temp)
+ {
+ // either 0 EOB or Value
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, 1);
+ if (temp)
+ {
+ // value
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, 1);
+ if (temp)
+ {
+ // negative
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, wbits);
+ patches[i] = temp;
+ patches[i] *= -1;
+ }
+ else
+ {
+ // positive
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, wbits);
+ patches[i] = temp;
+ }
+ }
+ else
+ {
+ for (j = i; j < patch_size*patch_size; j++)
+ {
+ patches[j] = 0;
+ }
+ return;
+ }
+ }
+ else
+ {
+ patches[i] = 0;
+ }
+ }
+#endif
+}
+
diff --git a/indra/llmessage/patch_code.h b/indra/llmessage/patch_code.h
new file mode 100644
index 0000000000..a18736df11
--- /dev/null
+++ b/indra/llmessage/patch_code.h
@@ -0,0 +1,28 @@
+/**
+ * @file patch_code.h
+ * @brief Function declarations for encoding and decoding patches.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PATCH_CODE_H
+#define LL_PATCH_CODE_H
+
+class LLBitPack;
+class LLGroupHeader;
+class LLPatchHeader;
+
+void init_patch_coding(LLBitPack &bitpack);
+void code_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp);
+void code_patch_header(LLBitPack &bitpack, LLPatchHeader *ph, S32 *patch);
+void code_end_of_data(LLBitPack &bitpack);
+void code_patch(LLBitPack &bitpack, S32 *patch, S32 postquant);
+void end_patch_coding(LLBitPack &bitpack);
+
+void init_patch_decoding(LLBitPack &bitpack);
+void decode_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp);
+void decode_patch_header(LLBitPack &bitpack, LLPatchHeader *ph);
+void decode_patch(LLBitPack &bitpack, S32 *patches);
+
+#endif
diff --git a/indra/llmessage/patch_dct.cpp b/indra/llmessage/patch_dct.cpp
new file mode 100644
index 0000000000..8d6969a2cd
--- /dev/null
+++ b/indra/llmessage/patch_dct.cpp
@@ -0,0 +1,751 @@
+/**
+ * @file patch_dct.cpp
+ * @brief DCT patch.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "patch_dct.h"
+
+typedef struct s_patch_compress_global_data
+{
+ S32 patch_size;
+ S32 patch_stride;
+ U32 charptr;
+ S32 layer_type;
+} PCGD;
+
+PCGD gPatchCompressGlobalData;
+
+void reset_patch_compressor(void)
+{
+ PCGD *pcp = &gPatchCompressGlobalData;
+
+ pcp->charptr = 0;
+}
+
+S32 gCurrentSize = 0;
+
+F32 gPatchQuantizeTable[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void build_patch_quantize_table(S32 size)
+{
+ S32 i, j;
+ for (j = 0; j < size; j++)
+ {
+ for (i = 0; i < size; i++)
+ {
+ gPatchQuantizeTable[j*size + i] = 1.f/(1.f + 2.f*(i+j));
+ }
+ }
+}
+
+F32 gPatchCosines[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void setup_patch_cosines(S32 size)
+{
+ S32 n, u;
+ F32 oosob = F_PI*0.5f/size;
+
+ for (u = 0; u < size; u++)
+ {
+ for (n = 0; n < size; n++)
+ {
+ gPatchCosines[u*size+n] = cosf((2.f*n+1.f)*u*oosob);
+ }
+ }
+}
+
+S32 gCopyMatrix[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void build_copy_matrix(S32 size)
+{
+ S32 i, j, count;
+ BOOL b_diag = FALSE;
+ BOOL b_right = TRUE;
+
+ i = 0;
+ j = 0;
+ count = 0;
+
+ while ( (i < size)
+ &&(j < size))
+ {
+ gCopyMatrix[j*size + i] = count;
+
+ count++;
+
+ if (!b_diag)
+ {
+ if (b_right)
+ {
+ if (i < size - 1)
+ i++;
+ else
+ j++;
+ b_right = FALSE;
+ b_diag = TRUE;
+ }
+ else
+ {
+ if (j < size - 1)
+ j++;
+ else
+ i++;
+ b_right = TRUE;
+ b_diag = TRUE;
+ }
+ }
+ else
+ {
+ if (b_right)
+ {
+ i++;
+ j--;
+ if ( (i == size - 1)
+ ||(j == 0))
+ {
+ b_diag = FALSE;
+ }
+ }
+ else
+ {
+ i--;
+ j++;
+ if ( (i == 0)
+ ||(j == size - 1))
+ {
+ b_diag = FALSE;
+ }
+ }
+ }
+ }
+}
+
+
+void init_patch_compressor(S32 patch_size, S32 patch_stride, S32 layer_type)
+{
+ PCGD *pcp = &gPatchCompressGlobalData;
+
+ pcp->charptr = 0;
+
+ pcp->patch_size = patch_size;
+ pcp->patch_stride = patch_stride;
+ pcp->layer_type = layer_type;
+
+ if (patch_size != gCurrentSize)
+ {
+ gCurrentSize = patch_size;
+ build_patch_quantize_table(patch_size);
+ setup_patch_cosines(patch_size);
+ build_copy_matrix(patch_size);
+ }
+}
+
+void prescan_patch(F32 *patch, LLPatchHeader *php, F32 &zmax, F32 &zmin)
+{
+ S32 i, j;
+ PCGD *pcp = &gPatchCompressGlobalData;
+ S32 stride = pcp->patch_stride;
+ S32 size = pcp->patch_size;
+ S32 jstride;
+
+ zmax = -99999999.f;
+ zmin = 99999999.f;
+
+ for (j = 0; j < size; j++)
+ {
+ jstride = j*stride;
+ for (i = 0; i < size; i++)
+ {
+ if (*(patch + jstride + i) > zmax)
+ {
+ zmax = *(patch + jstride + i);
+ }
+ if (*(patch + jstride + i) < zmin)
+ {
+ zmin = *(patch + jstride + i);
+ }
+ }
+ }
+
+ php->dc_offset = zmin;
+ php->range = (U16) ((zmax - zmin) + 1.f);
+}
+
+void dct_line(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 u;
+ F32 total;
+ F32 *pcp = gPatchCosines;
+ S32 line_size = line*NORMAL_PATCH_SIZE;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 *tlinein, *tpcp;
+
+ tlinein = linein + line_size;
+
+ total = *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein);
+
+ *(lineout + line_size) = OO_SQRT2*total;
+
+ for (u = 1; u < NORMAL_PATCH_SIZE; u++)
+ {
+ tlinein = linein + line_size;
+ tpcp = pcp + (u<<4);
+
+ total = *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein)*(*tpcp);
+
+ *(lineout + line_size + u) = total;
+ }
+#else
+ S32 n;
+ S32 size = gPatchCompressGlobalData.patch_size;
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[line_size + n];
+ }
+ lineout[line_size] = OO_SQRT2*total;
+
+ for (u = 1; u < size; u++)
+ {
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[line_size + n]*pcp[u*size+n];
+ }
+ lineout[line_size + u] = total;
+ }
+#endif
+}
+
+void dct_line_large(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 u;
+ F32 total;
+ F32 *pcp = gPatchCosines;
+ S32 line_size = line*LARGE_PATCH_SIZE;
+
+ F32 *tlinein, *tpcp;
+
+ tlinein = linein + line_size;
+
+ total = *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein);
+
+ *(lineout + line_size) = OO_SQRT2*total;
+
+ for (u = 1; u < LARGE_PATCH_SIZE; u++)
+ {
+ tlinein = linein + line_size;
+ tpcp = pcp + (u<<5);
+
+ total = *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein)*(*tpcp);
+
+ *(lineout + line_size + u) = total;
+ }
+}
+
+inline void dct_column(F32 *linein, S32 *lineout, S32 column)
+{
+ S32 u;
+ F32 total;
+ F32 oosob = 2.f/16.f;
+ F32 *pcp = gPatchCosines;
+ S32 *copy_matrix = gCopyMatrix;
+ F32 *qt = gPatchQuantizeTable;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 *tlinein, *tpcp;
+ S32 sizeu;
+
+ tlinein = linein + column;
+
+ total = *(tlinein);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ *(lineout + *(copy_matrix + column)) = (S32)(OO_SQRT2*total*oosob*(*(qt + column)));
+
+ for (u = 1; u < NORMAL_PATCH_SIZE; u++)
+ {
+ tlinein = linein + column;
+ tpcp = pcp + (u<<4);
+
+ total = *(tlinein)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp));
+
+ sizeu = NORMAL_PATCH_SIZE*u + column;
+
+ *(lineout + *(copy_matrix + sizeu)) = (S32)(total*oosob*(*(qt+sizeu)));
+ }
+#else
+ S32 size = gPatchCompressGlobalData.patch_size;
+ F32 oosob = 2.f/size;
+ S32 n;
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[size*n + column];
+ }
+ lineout[copy_matrix[column]] = OO_SQRT2*total*oosob*qt[column];
+
+ for (u = 1; u < size; u++)
+ {
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[size*n + column]*pcp[u*size+n];
+ }
+ lineout[copy_matrix[size*u + column]] = total*oosob*qt[size*u + column];
+ }
+#endif
+}
+
+inline void dct_column_large(F32 *linein, S32 *lineout, S32 column)
+{
+ S32 u;
+ F32 total;
+ F32 oosob = 2.f/32.f;
+ F32 *pcp = gPatchCosines;
+ S32 *copy_matrix = gCopyMatrix;
+ F32 *qt = gPatchQuantizeTable;
+
+ F32 *tlinein, *tpcp;
+ S32 sizeu;
+
+ tlinein = linein + column;
+
+ total = *(tlinein);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ *(lineout + *(copy_matrix + column)) = (S32)(OO_SQRT2*total*oosob*(*(qt + column)));
+
+ for (u = 1; u < LARGE_PATCH_SIZE; u++)
+ {
+ tlinein = linein + column;
+ tpcp = pcp + (u<<5);
+
+ total = *(tlinein)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp));
+
+ sizeu = LARGE_PATCH_SIZE*u + column;
+
+ *(lineout + *(copy_matrix + sizeu)) = (S32)(total*oosob*(*(qt+sizeu)));
+ }
+}
+
+inline void dct_patch(F32 *block, S32 *cpatch)
+{
+ F32 temp[NORMAL_PATCH_SIZE*NORMAL_PATCH_SIZE];
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ dct_line(block, temp, 0);
+ dct_line(block, temp, 1);
+ dct_line(block, temp, 2);
+ dct_line(block, temp, 3);
+
+ dct_line(block, temp, 4);
+ dct_line(block, temp, 5);
+ dct_line(block, temp, 6);
+ dct_line(block, temp, 7);
+
+ dct_line(block, temp, 8);
+ dct_line(block, temp, 9);
+ dct_line(block, temp, 10);
+ dct_line(block, temp, 11);
+
+ dct_line(block, temp, 12);
+ dct_line(block, temp, 13);
+ dct_line(block, temp, 14);
+ dct_line(block, temp, 15);
+
+ dct_column(temp, cpatch, 0);
+ dct_column(temp, cpatch, 1);
+ dct_column(temp, cpatch, 2);
+ dct_column(temp, cpatch, 3);
+
+ dct_column(temp, cpatch, 4);
+ dct_column(temp, cpatch, 5);
+ dct_column(temp, cpatch, 6);
+ dct_column(temp, cpatch, 7);
+
+ dct_column(temp, cpatch, 8);
+ dct_column(temp, cpatch, 9);
+ dct_column(temp, cpatch, 10);
+ dct_column(temp, cpatch, 11);
+
+ dct_column(temp, cpatch, 12);
+ dct_column(temp, cpatch, 13);
+ dct_column(temp, cpatch, 14);
+ dct_column(temp, cpatch, 15);
+#else
+ S32 i;
+ S32 size = gPatchCompressGlobalData.patch_size;
+ for (i = 0; i < size; i++)
+ {
+ dct_line(block, temp, i);
+ }
+ for (i = 0; i < size; i++)
+ {
+ dct_column(temp, cpatch, i);
+ }
+#endif
+}
+
+inline void dct_patch_large(F32 *block, S32 *cpatch)
+{
+ F32 temp[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+ dct_line_large(block, temp, 0);
+ dct_line_large(block, temp, 1);
+ dct_line_large(block, temp, 2);
+ dct_line_large(block, temp, 3);
+
+ dct_line_large(block, temp, 4);
+ dct_line_large(block, temp, 5);
+ dct_line_large(block, temp, 6);
+ dct_line_large(block, temp, 7);
+
+ dct_line_large(block, temp, 8);
+ dct_line_large(block, temp, 9);
+ dct_line_large(block, temp, 10);
+ dct_line_large(block, temp, 11);
+
+ dct_line_large(block, temp, 12);
+ dct_line_large(block, temp, 13);
+ dct_line_large(block, temp, 14);
+ dct_line_large(block, temp, 15);
+
+ dct_line_large(block, temp, 16);
+ dct_line_large(block, temp, 17);
+ dct_line_large(block, temp, 18);
+ dct_line_large(block, temp, 19);
+
+ dct_line_large(block, temp, 20);
+ dct_line_large(block, temp, 21);
+ dct_line_large(block, temp, 22);
+ dct_line_large(block, temp, 23);
+
+ dct_line_large(block, temp, 24);
+ dct_line_large(block, temp, 25);
+ dct_line_large(block, temp, 26);
+ dct_line_large(block, temp, 27);
+
+ dct_line_large(block, temp, 28);
+ dct_line_large(block, temp, 29);
+ dct_line_large(block, temp, 30);
+ dct_line_large(block, temp, 31);
+
+ dct_column_large(temp, cpatch, 0);
+ dct_column_large(temp, cpatch, 1);
+ dct_column_large(temp, cpatch, 2);
+ dct_column_large(temp, cpatch, 3);
+
+ dct_column_large(temp, cpatch, 4);
+ dct_column_large(temp, cpatch, 5);
+ dct_column_large(temp, cpatch, 6);
+ dct_column_large(temp, cpatch, 7);
+
+ dct_column_large(temp, cpatch, 8);
+ dct_column_large(temp, cpatch, 9);
+ dct_column_large(temp, cpatch, 10);
+ dct_column_large(temp, cpatch, 11);
+
+ dct_column_large(temp, cpatch, 12);
+ dct_column_large(temp, cpatch, 13);
+ dct_column_large(temp, cpatch, 14);
+ dct_column_large(temp, cpatch, 15);
+
+ dct_column_large(temp, cpatch, 16);
+ dct_column_large(temp, cpatch, 17);
+ dct_column_large(temp, cpatch, 18);
+ dct_column_large(temp, cpatch, 19);
+
+ dct_column_large(temp, cpatch, 20);
+ dct_column_large(temp, cpatch, 21);
+ dct_column_large(temp, cpatch, 22);
+ dct_column_large(temp, cpatch, 23);
+
+ dct_column_large(temp, cpatch, 24);
+ dct_column_large(temp, cpatch, 25);
+ dct_column_large(temp, cpatch, 26);
+ dct_column_large(temp, cpatch, 27);
+
+ dct_column_large(temp, cpatch, 28);
+ dct_column_large(temp, cpatch, 29);
+ dct_column_large(temp, cpatch, 30);
+ dct_column_large(temp, cpatch, 31);
+}
+
+void compress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *php, S32 prequant)
+{
+ S32 i, j;
+ PCGD *pcp = &gPatchCompressGlobalData;
+ S32 stride = pcp->patch_stride;
+ S32 size = pcp->patch_size;
+ F32 block[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE], *tblock;
+ F32 *tpatch;
+
+ S32 wordsize = prequant;
+ F32 oozrange = 1.f/php->range;
+
+ F32 dc = php->dc_offset;
+
+ S32 range = (1<<prequant);
+ F32 premult = oozrange*range;
+// F32 sub = (F32)(1<<(prequant - 1));
+ F32 sub = (F32)(1<<(prequant - 1)) + dc*premult;
+
+ php->quant_wbits = wordsize - 2;
+ php->quant_wbits |= (prequant - 2)<<4;
+
+ for (j = 0; j < size; j++)
+ {
+ tblock = block + j*size;
+ tpatch = patch + j*stride;
+ for (i = 0; i < size; i++)
+ {
+// block[j*size + i] = (patch[j*stride + i] - dc)*premult - sub;
+ *(tblock++) = *(tpatch++)*premult - sub;
+ }
+ }
+
+ if (size == 16)
+ dct_patch(block, cpatch);
+ else
+ dct_patch_large(block, cpatch);
+}
+
+void get_patch_group_header(LLGroupHeader *gopp)
+{
+ PCGD *pcp = &gPatchCompressGlobalData;
+ gopp->stride = pcp->patch_stride;
+ gopp->patch_size = pcp->patch_size;
+ gopp->layer_type = pcp->layer_type;
+}
diff --git a/indra/llmessage/patch_dct.h b/indra/llmessage/patch_dct.h
new file mode 100644
index 0000000000..d5c0d067fc
--- /dev/null
+++ b/indra/llmessage/patch_dct.h
@@ -0,0 +1,73 @@
+/**
+ * @file patch_dct.h
+ * @brief Function declarations for DCT and IDCT routines
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PATCH_DCT_H
+#define LL_PATCH_DCT_H
+
+class LLVector3;
+
+// Code Values
+const U8 ZERO_CODE = 0x0;
+const U8 ZERO_EOB = 0x2;
+const U8 POSITIVE_VALUE = 0x6;
+const U8 NEGATIVE_VALUE = 0x7;
+
+const S8 NORMAL_PATCH_SIZE = 16;
+const S8 LARGE_PATCH_SIZE = 32;
+
+const U8 END_OF_PATCHES = 97;
+
+#define _PATCH_SIZE_16_AND_32_ONLY
+
+// Top level header for group of headers
+//typedef struct LL_Group_Header
+//{
+// U16 stride; // 2 = 2
+// U8 patch_size; // 1 = 3
+// U8 layer_type; // 1 = 4
+//} LLGroupHeader;
+
+class LLGroupHeader
+{
+public:
+ U16 stride; // 2 = 2
+ U8 patch_size; // 1 = 3
+ U8 layer_type; // 1 = 4
+};
+
+// Individual patch header
+
+//typedef struct LL_Patch_Header
+//{
+// F32 dc_offset; // 4 bytes
+// U16 range; // 2 = 7 ((S16) FP range (breaks if we need > 32K meters in 1 patch)
+// U8 quant_wbits; // 1 = 8 (upper 4 bits is quant - 2, lower 4 bits is word bits - 2)
+// U16 patchids; // 2 = 10 (actually only uses 10 bits, 5 for each)
+//} LLPatchHeader;
+class LLPatchHeader
+{
+public:
+ F32 dc_offset; // 4 bytes
+ U16 range; // 2 = 7 ((S16) FP range (breaks if we need > 32K meters in 1 patch)
+ U8 quant_wbits; // 1 = 8 (upper 4 bits is quant - 2, lower 4 bits is word bits - 2)
+ U16 patchids; // 2 = 10 (actually only uses 10 bits, 5 for each)
+};
+
+// Compression routines
+void init_patch_compressor(S32 patch_size, S32 patch_stride, S32 layer_type);
+void prescan_patch(F32 *patch, LLPatchHeader *php, F32 &zmax, F32 &zmin);
+void compress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *php, S32 prequant);
+void get_patch_group_header(LLGroupHeader *gopp);
+
+// Decompression routines
+void set_group_of_patch_header(LLGroupHeader *gopp);
+void init_patch_decompressor(S32 size);
+void decompress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *ph);
+void decompress_patchv(LLVector3 *v, S32 *cpatch, LLPatchHeader *ph);
+
+#endif
diff --git a/indra/llmessage/patch_idct.cpp b/indra/llmessage/patch_idct.cpp
new file mode 100644
index 0000000000..cfc52c551d
--- /dev/null
+++ b/indra/llmessage/patch_idct.cpp
@@ -0,0 +1,666 @@
+/**
+ * @file patch_idct.cpp
+ * @brief IDCT patch.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "patch_dct.h"
+
+LLGroupHeader *gGOPP;
+
+void set_group_of_patch_header(LLGroupHeader *gopp)
+{
+ gGOPP = gopp;
+}
+
+F32 gPatchDequantizeTable[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+void build_patch_dequantize_table(S32 size)
+{
+ S32 i, j;
+ for (j = 0; j < size; j++)
+ {
+ for (i = 0; i < size; i++)
+ {
+ gPatchDequantizeTable[j*size + i] = (1.f + 2.f*(i+j));
+ }
+ }
+}
+
+S32 gCurrentDeSize = 0;
+
+F32 gPatchICosines[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void setup_patch_icosines(S32 size)
+{
+ S32 n, u;
+ F32 oosob = F_PI*0.5f/size;
+
+ for (u = 0; u < size; u++)
+ {
+ for (n = 0; n < size; n++)
+ {
+ gPatchICosines[u*size+n] = cosf((2.f*n+1.f)*u*oosob);
+ }
+ }
+}
+
+S32 gDeCopyMatrix[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void build_decopy_matrix(S32 size)
+{
+ S32 i, j, count;
+ BOOL b_diag = FALSE;
+ BOOL b_right = TRUE;
+
+ i = 0;
+ j = 0;
+ count = 0;
+
+ while ( (i < size)
+ &&(j < size))
+ {
+ gDeCopyMatrix[j*size + i] = count;
+
+ count++;
+
+ if (!b_diag)
+ {
+ if (b_right)
+ {
+ if (i < size - 1)
+ i++;
+ else
+ j++;
+ b_right = FALSE;
+ b_diag = TRUE;
+ }
+ else
+ {
+ if (j < size - 1)
+ j++;
+ else
+ i++;
+ b_right = TRUE;
+ b_diag = TRUE;
+ }
+ }
+ else
+ {
+ if (b_right)
+ {
+ i++;
+ j--;
+ if ( (i == size - 1)
+ ||(j == 0))
+ {
+ b_diag = FALSE;
+ }
+ }
+ else
+ {
+ i--;
+ j++;
+ if ( (i == 0)
+ ||(j == size - 1))
+ {
+ b_diag = FALSE;
+ }
+ }
+ }
+ }
+}
+
+void init_patch_decompressor(S32 size)
+{
+ if (size != gCurrentDeSize)
+ {
+ gCurrentDeSize = size;
+ build_patch_dequantize_table(size);
+ setup_patch_icosines(size);
+ build_decopy_matrix(size);
+ }
+}
+
+inline void idct_line(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 oosob = 2.f/16.f;
+ S32 line_size = line*NORMAL_PATCH_SIZE;
+ F32 *tlinein, *tpcp;
+
+
+ for (n = 0; n < NORMAL_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + line_size;
+
+ total = OO_SQRT2*(*(tlinein++));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ *(lineout + line_size + n) = total*oosob;
+ }
+#else
+ F32 oosob = 2.f/size;
+ S32 size = gGOPP->patch_size;
+ S32 line_size = line*size;
+ S32 u;
+ for (n = 0; n < size; n++)
+ {
+ total = OO_SQRT2*linein[line_size];
+ for (u = 1; u < size; u++)
+ {
+ total += linein[line_size + u]*pcp[u*size+n];
+ }
+ lineout[line_size + n] = total*oosob;
+ }
+#endif
+}
+
+inline void idct_line_large_slow(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 oosob = 2.f/32.f;
+ S32 line_size = line*LARGE_PATCH_SIZE;
+ F32 *tlinein, *tpcp;
+
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + line_size;
+
+ total = OO_SQRT2*(*(tlinein++));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ *(lineout + line_size + n) = total*oosob;
+ }
+}
+
+// Nota Bene: assumes that coefficients beyond 128 are 0!
+
+void idct_line_large(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 oosob = 2.f/32.f;
+ S32 line_size = line*LARGE_PATCH_SIZE;
+ F32 *tlinein, *tpcp;
+ F32 *baselinein = linein + line_size;
+ F32 *baselineout = lineout + line_size;
+
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp++;
+ tlinein = baselinein;
+
+ total = OO_SQRT2*(*(tlinein++));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein)*(*(tpcp));
+
+ *baselineout++ = total*oosob;
+ }
+}
+
+inline void idct_column(F32 *linein, F32 *lineout, S32 column)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 *tlinein, *tpcp;
+
+ for (n = 0; n < NORMAL_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + column;
+
+ total = OO_SQRT2*(*tlinein);
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ *(lineout + (n<<4) + column) = total;
+ }
+
+#else
+ S32 size = gGOPP->patch_size;
+ S32 u;
+ S32 u_size;
+
+ for (n = 0; n < size; n++)
+ {
+ total = OO_SQRT2*linein[column];
+ for (u = 1; u < size; u++)
+ {
+ u_size = u*size;
+ total += linein[u_size + column]*pcp[u_size+n];
+ }
+ lineout[size*n + column] = total;
+ }
+#endif
+}
+
+inline void idct_column_large_slow(F32 *linein, F32 *lineout, S32 column)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 *tlinein, *tpcp;
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + column;
+
+ total = OO_SQRT2*(*tlinein);
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ *(lineout + (n<<5) + column) = total;
+ }
+}
+
+// Nota Bene: assumes that coefficients beyond 128 are 0!
+
+void idct_column_large(F32 *linein, F32 *lineout, S32 column)
+{
+ S32 n, m;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 *tlinein, *tpcp;
+ F32 *baselinein = linein + column;
+ F32 *baselineout = lineout + column;
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp++;
+ tlinein = baselinein;
+
+ total = OO_SQRT2*(*tlinein);
+ for (m = 1; m < NORMAL_PATCH_SIZE; m++)
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ *(baselineout + (n<<5)) = total;
+ }
+}
+
+inline void idct_patch(F32 *block)
+{
+ F32 temp[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ idct_column(block, temp, 0);
+ idct_column(block, temp, 1);
+ idct_column(block, temp, 2);
+ idct_column(block, temp, 3);
+
+ idct_column(block, temp, 4);
+ idct_column(block, temp, 5);
+ idct_column(block, temp, 6);
+ idct_column(block, temp, 7);
+
+ idct_column(block, temp, 8);
+ idct_column(block, temp, 9);
+ idct_column(block, temp, 10);
+ idct_column(block, temp, 11);
+
+ idct_column(block, temp, 12);
+ idct_column(block, temp, 13);
+ idct_column(block, temp, 14);
+ idct_column(block, temp, 15);
+
+ idct_line(temp, block, 0);
+ idct_line(temp, block, 1);
+ idct_line(temp, block, 2);
+ idct_line(temp, block, 3);
+
+ idct_line(temp, block, 4);
+ idct_line(temp, block, 5);
+ idct_line(temp, block, 6);
+ idct_line(temp, block, 7);
+
+ idct_line(temp, block, 8);
+ idct_line(temp, block, 9);
+ idct_line(temp, block, 10);
+ idct_line(temp, block, 11);
+
+ idct_line(temp, block, 12);
+ idct_line(temp, block, 13);
+ idct_line(temp, block, 14);
+ idct_line(temp, block, 15);
+#else
+ S32 i;
+ S32 size = gGOPP->patch_size;
+ for (i = 0; i < size; i++)
+ {
+ idct_column(block, temp, i);
+ }
+ for (i = 0; i < size; i++)
+ {
+ idct_line(temp, block, i);
+ }
+#endif
+}
+
+inline void idct_patch_large(F32 *block)
+{
+ F32 temp[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+ idct_column_large_slow(block, temp, 0);
+ idct_column_large_slow(block, temp, 1);
+ idct_column_large_slow(block, temp, 2);
+ idct_column_large_slow(block, temp, 3);
+
+ idct_column_large_slow(block, temp, 4);
+ idct_column_large_slow(block, temp, 5);
+ idct_column_large_slow(block, temp, 6);
+ idct_column_large_slow(block, temp, 7);
+
+ idct_column_large_slow(block, temp, 8);
+ idct_column_large_slow(block, temp, 9);
+ idct_column_large_slow(block, temp, 10);
+ idct_column_large_slow(block, temp, 11);
+
+ idct_column_large_slow(block, temp, 12);
+ idct_column_large_slow(block, temp, 13);
+ idct_column_large_slow(block, temp, 14);
+ idct_column_large_slow(block, temp, 15);
+
+ idct_column_large_slow(block, temp, 16);
+ idct_column_large_slow(block, temp, 17);
+ idct_column_large_slow(block, temp, 18);
+ idct_column_large_slow(block, temp, 19);
+
+ idct_column_large_slow(block, temp, 20);
+ idct_column_large_slow(block, temp, 21);
+ idct_column_large_slow(block, temp, 22);
+ idct_column_large_slow(block, temp, 23);
+
+ idct_column_large_slow(block, temp, 24);
+ idct_column_large_slow(block, temp, 25);
+ idct_column_large_slow(block, temp, 26);
+ idct_column_large_slow(block, temp, 27);
+
+ idct_column_large_slow(block, temp, 28);
+ idct_column_large_slow(block, temp, 29);
+ idct_column_large_slow(block, temp, 30);
+ idct_column_large_slow(block, temp, 31);
+
+ idct_line_large_slow(temp, block, 0);
+ idct_line_large_slow(temp, block, 1);
+ idct_line_large_slow(temp, block, 2);
+ idct_line_large_slow(temp, block, 3);
+
+ idct_line_large_slow(temp, block, 4);
+ idct_line_large_slow(temp, block, 5);
+ idct_line_large_slow(temp, block, 6);
+ idct_line_large_slow(temp, block, 7);
+
+ idct_line_large_slow(temp, block, 8);
+ idct_line_large_slow(temp, block, 9);
+ idct_line_large_slow(temp, block, 10);
+ idct_line_large_slow(temp, block, 11);
+
+ idct_line_large_slow(temp, block, 12);
+ idct_line_large_slow(temp, block, 13);
+ idct_line_large_slow(temp, block, 14);
+ idct_line_large_slow(temp, block, 15);
+
+ idct_line_large_slow(temp, block, 16);
+ idct_line_large_slow(temp, block, 17);
+ idct_line_large_slow(temp, block, 18);
+ idct_line_large_slow(temp, block, 19);
+
+ idct_line_large_slow(temp, block, 20);
+ idct_line_large_slow(temp, block, 21);
+ idct_line_large_slow(temp, block, 22);
+ idct_line_large_slow(temp, block, 23);
+
+ idct_line_large_slow(temp, block, 24);
+ idct_line_large_slow(temp, block, 25);
+ idct_line_large_slow(temp, block, 26);
+ idct_line_large_slow(temp, block, 27);
+
+ idct_line_large_slow(temp, block, 28);
+ idct_line_large_slow(temp, block, 29);
+ idct_line_large_slow(temp, block, 30);
+ idct_line_large_slow(temp, block, 31);
+}
+
+S32 gDitherNoise = 128;
+
+void decompress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *ph)
+{
+ S32 i, j;
+
+ F32 block[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE], *tblock = block;
+ F32 *tpatch;
+
+ LLGroupHeader *gopp = gGOPP;
+ S32 size = gopp->patch_size;
+ F32 range = ph->range;
+ S32 prequant = (ph->quant_wbits >> 4) + 2;
+ S32 quantize = 1<<prequant;
+ F32 hmin = ph->dc_offset;
+ S32 stride = gopp->stride;
+
+ F32 ooq = 1.f/(F32)quantize;
+ F32 *dq = gPatchDequantizeTable;
+ S32 *decopy_matrix = gDeCopyMatrix;
+
+ F32 mult = ooq*range;
+ F32 addval = mult*(F32)(1<<(prequant - 1))+hmin;
+
+ for (i = 0; i < size*size; i++)
+ {
+ *(tblock++) = *(cpatch + *(decopy_matrix++))*(*dq++);
+ }
+
+ if (size == 16)
+ {
+ idct_patch(block);
+ }
+ else
+ {
+ idct_patch_large(block);
+ }
+
+ for (j = 0; j < size; j++)
+ {
+ tpatch = patch + j*stride;
+ tblock = block + j*size;
+ for (i = 0; i < size; i++)
+ {
+ *(tpatch++) = *(tblock++)*mult+addval;
+ }
+ }
+}
+
+
+void decompress_patchv(LLVector3 *v, S32 *cpatch, LLPatchHeader *ph)
+{
+ S32 i, j;
+
+ F32 block[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE], *tblock = block;
+ LLVector3 *tvec;
+
+ LLGroupHeader *gopp = gGOPP;
+ S32 size = gopp->patch_size;
+ F32 range = ph->range;
+ S32 prequant = (ph->quant_wbits >> 4) + 2;
+ S32 quantize = 1<<prequant;
+ F32 hmin = ph->dc_offset;
+ S32 stride = gopp->stride;
+
+ F32 ooq = 1.f/(F32)quantize;
+ F32 *dq = gPatchDequantizeTable;
+ S32 *decopy_matrix = gDeCopyMatrix;
+
+ F32 mult = ooq*range;
+ F32 addval = mult*(F32)(1<<(prequant - 1))+hmin;
+
+// BOOL b_diag = FALSE;
+// BOOL b_right = TRUE;
+
+ for (i = 0; i < size*size; i++)
+ {
+ *(tblock++) = *(cpatch + *(decopy_matrix++))*(*dq++);
+ }
+
+ if (size == 16)
+ idct_patch(block);
+ else
+ idct_patch_large(block);
+
+ for (j = 0; j < size; j++)
+ {
+ tvec = v + j*stride;
+ tblock = block + j*size;
+ for (i = 0; i < size; i++)
+ {
+ (*tvec++).mV[VZ] = *(tblock++)*mult+addval;
+ }
+ }
+}
+
diff --git a/indra/llmessage/sound_ids.h b/indra/llmessage/sound_ids.h
new file mode 100644
index 0000000000..35c7f6e438
--- /dev/null
+++ b/indra/llmessage/sound_ids.h
@@ -0,0 +1,293 @@
+/**
+ * @file sound_ids.h
+ * @brief Temporary holder for sound IDs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SOUND_IDS_H
+#define LL_SOUND_IDS_H
+
+#include "lluuid.h"
+
+const LLUUID SND_NULL = LLUUID::null;
+const LLUUID SND_RIDE ("00000000-0000-0000-0000-000000000100");
+const LLUUID SND_SHOT ("00000000-0000-0000-0000-000000000101");
+const LLUUID SND_MORTAR ("00000000-0000-0000-0000-000000000102");
+const LLUUID SND_HIT ("00000000-0000-0000-0000-000000000103");
+const LLUUID SND_EXPLOSION ("00000000-0000-0000-0000-000000000104");
+const LLUUID SND_BOING ("00000000-0000-0000-0000-000000000105");
+const LLUUID SND_OBJECT_CREATE ("9f1bc096-3592-411e-9b0b-c447a9ff054c");
+
+//
+// Different bird sounds for different states
+//
+
+const LLUUID SND_CHIRP ("00000000-0000-0000-0000-000000000106"); // Flying random chirp
+const LLUUID SND_CHIRP2 ("828a9526-175b-455d-8af0-0e3c0fb602b2"); // Spooked by user
+const LLUUID SND_CHIRP3 ("f99772d6-1ce6-4a39-a28b-06d26c94c9e3"); // Spooked by object
+const LLUUID SND_CHIRP4 ("54472ca4-7fc9-42cb-b7d5-99ad5b12bd50"); // Chasing other bird
+const LLUUID SND_CHIRP5 ("2929964f-fac5-40d7-9179-2864a8fa9ace"); // Hopping random chirp
+const LLUUID SND_CHIRPDEAD ("9abff1d3-863a-4e04-bd83-3834fd7fcff4"); // Hit by grenade - dead!
+
+
+const LLUUID SND_MUNCH ("00000000-0000-0000-0000-000000000107");
+const LLUUID SND_PUNCH ("00000000-0000-0000-0000-000000000108");
+const LLUUID SND_SPLASH ("00000000-0000-0000-0000-000000000109");
+const LLUUID SND_CLICK ("00000000-0000-0000-0000-000000000110");
+const LLUUID SND_WHISTLE ("ab858f9a-1f44-4d39-9b33-351543d03ccb");
+const LLUUID SND_TYPING ("5e191c7b-8996-9ced-a177-b2ac32bfea06");
+
+const LLUUID SND_ARROW_SHOT ("00000000-0000-0000-0000-000000000111");
+const LLUUID SND_ARROW_THUD ("00000000-0000-0000-0000-000000000112");
+const LLUUID SND_LASER_SHOT ("00000000-0000-0000-0000-000000000113");
+const LLUUID SND_JET_THRUST ("67f5e4f0-0534-4d97-bc01-f297648d20e0");
+
+const LLUUID SND_SILENCE ("00000000-0000-0000-0000-000000000114");
+const LLUUID SND_BUBBLES ("00000000-0000-0000-0000-000000000115");
+const LLUUID SND_WELCOME ("00000000-0000-0000-0000-000000000116");
+const LLUUID SND_SQUISH ("00000000-0000-0000-0000-000000000117");
+const LLUUID SND_SUBPOD ("00000000-0000-0000-0000-000000000118");
+const LLUUID SND_FOOTSTEPS ("00000000-0000-0000-0000-000000000119");
+const LLUUID SND_STEP_LEFT ("00000000-0000-0000-0000-000000000124");
+const LLUUID SND_STEP_RIGHT ("00000000-0000-0000-0000-000000000125");
+
+const LLUUID SND_BALL_COLLISION ("00000000-0000-0000-0000-000000000120");
+
+const LLUUID SND_OOOH_SCARE_ME ("00000000-0000-0000-0000-000000000121");
+const LLUUID SND_PAYBACK_TIME ("00000000-0000-0000-0000-000000000122");
+const LLUUID SND_READY_FOR_BATTLE ("00000000-0000-0000-0000-000000000123");
+
+const LLUUID SND_FLESH_FLESH ("dce5fdd4-afe4-4ea1-822f-dd52cac46b08");
+const LLUUID SND_FLESH_PLASTIC ("51011582-fbca-4580-ae9e-1a5593f094ec");
+const LLUUID SND_FLESH_RUBBER ("68d62208-e257-4d0c-bbe2-20c9ea9760bb");
+const LLUUID SND_GLASS_FLESH ("75872e8c-bc39-451b-9b0b-042d7ba36cba");
+const LLUUID SND_GLASS_GLASS ("6a45ba0b-5775-4ea8-8513-26008a17f873");
+const LLUUID SND_GLASS_PLASTIC ("992a6d1b-8c77-40e0-9495-4098ce539694");
+const LLUUID SND_GLASS_RUBBER ("2de4da5a-faf8-46be-bac6-c4d74f1e5767");
+const LLUUID SND_GLASS_WOOD ("6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d");
+const LLUUID SND_METAL_FLESH ("14209133-4961-4acc-9649-53fc38ee1667");
+const LLUUID SND_METAL_GLASS ("bc4a4348-cfcc-4e5e-908e-8a52a8915fe6");
+const LLUUID SND_METAL_METAL ("9e5c1297-6eed-40c0-825a-d9bcd86e3193");
+const LLUUID SND_METAL_PLASTIC ("e534761c-1894-4b61-b20c-658a6fb68157");
+const LLUUID SND_METAL_RUBBER ("8761f73f-6cf9-4186-8aaa-0948ed002db1");
+const LLUUID SND_METAL_WOOD ("874a26fd-142f-4173-8c5b-890cd846c74d");
+const LLUUID SND_PLASTIC_PLASTIC ("0e24a717-b97e-4b77-9c94-b59a5a88b2da");
+const LLUUID SND_RUBBER_PLASTIC ("75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2");
+const LLUUID SND_RUBBER_RUBBER ("153c8bf7-fb89-4d89-b263-47e58b1b4774");
+const LLUUID SND_STONE_FLESH ("55c3e0ce-275a-46fa-82ff-e0465f5e8703");
+const LLUUID SND_STONE_GLASS ("24babf58-7156-4841-9a3f-761bdbb8e237");
+const LLUUID SND_STONE_METAL ("aca261d8-e145-4610-9e20-9eff990f2c12");
+const LLUUID SND_STONE_PLASTIC ("0642fba6-5dcf-4d62-8e7b-94dbb529d117");
+const LLUUID SND_STONE_RUBBER ("25a863e8-dc42-4e8a-a357-e76422ace9b5");
+const LLUUID SND_STONE_STONE ("9538f37c-456e-4047-81be-6435045608d4");
+const LLUUID SND_STONE_WOOD ("8c0f84c3-9afd-4396-b5f5-9bca2c911c20");
+const LLUUID SND_WOOD_FLESH ("be582e5d-b123-41a2-a150-454c39e961c8");
+const LLUUID SND_WOOD_PLASTIC ("c70141d4-ba06-41ea-bcbc-35ea81cb8335");
+const LLUUID SND_WOOD_RUBBER ("7d1826f4-24c4-4aac-8c2e-eff45df37783");
+const LLUUID SND_WOOD_WOOD ("063c97d3-033a-4e9b-98d8-05c8074922cb");
+
+
+const LLUUID SND_SLIDE_FLESH_FLESH ("614eec22-f73d-4fdc-8691-a37dc5c58333");
+const LLUUID SND_SLIDE_FLESH_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_FLESH_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_FLESH_FABRIC ("3678b9b9-2a0c-42b5-9c83-80b64ad6e898");
+const LLUUID SND_SLIDE_FLESH_GRAVEL ("02eaa42a-ce1a-4b6b-9c38-cd7ad0e8f4a6");
+const LLUUID SND_SLIDE_FLESH_GRAVEL_02 ("e7d3b501-79f8-4419-b842-ab6843e0f840");
+const LLUUID SND_SLIDE_FLESH_GRAVEL_03 ("4c3e8b52-6244-4e44-85a6-f4ab994418ed");
+const LLUUID SND_SLIDE_GLASS_GRAVEL ("ca491e77-5c47-4ea1-8021-b3ebbf636cab");
+const LLUUID SND_SLIDE_GLASS_GRAVEL_02 ("30794d49-91ce-48e3-a527-c06f67bd6cbe");
+const LLUUID SND_SLIDE_GLASS_GRAVEL_03 ("04c78e54-fd8d-46b6-8ab9-7678b5d6e5cb");
+const LLUUID SND_SLIDE_GLASS_FLESH (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_GLASS (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_WOOD (SND_NULL);
+const LLUUID SND_SLIDE_METAL_FABRIC ("18b66e81-2958-42d4-a373-7a5054919adc");
+const LLUUID SND_SLIDE_METAL_FLESH ("dde65837-633c-4841-af2f-62ec471bf61e");
+const LLUUID SND_SLIDE_METAL_FLESH_02 ("f3cc2cbe-1a1a-4db7-a8d2-e9c8f8fa1f4f");
+const LLUUID SND_SLIDE_METAL_GLASS ("4188be39-7b1f-4495-bf2b-83ddd82eea05");
+const LLUUID SND_SLIDE_METAL_GLASS_02 ("336faa2b-9d96-4e14-93ad-b63b60074379");
+const LLUUID SND_SLIDE_METAL_GLASS_03 ("34d912aa-cf73-4462-b7d0-dcba2c66caba");
+const LLUUID SND_SLIDE_METAL_GLASS_04 ("97ffc063-e872-4469-8e95-1450ac6bad2b");
+const LLUUID SND_SLIDE_METAL_GRAVEL ("2bbff37d-009a-4cfc-9a0d-817652c08fbe");
+const LLUUID SND_SLIDE_METAL_GRAVEL_02 ("a906a228-783b-49e7-9f0a-e20a41d0e39f");
+const LLUUID SND_SLIDE_METAL_METAL ("09461277-c691-45de-b2c5-89dfd3712f79");
+const LLUUID SND_SLIDE_METAL_METAL_02 ("e00a5d97-8fdc-46c1-bd53-7e312727466c");
+const LLUUID SND_SLIDE_METAL_METAL_03 ("8ebfa780-c440-4b52-ab65-5edf3bc15bf1");
+const LLUUID SND_SLIDE_METAL_METAL_04 ("d6d03cb2-5b16-4e31-b7d4-2a81d2a0909b");
+const LLUUID SND_SLIDE_METAL_METAL_05 ("3a46f447-916e-47de-a1e5-95d1af46bd0f");
+const LLUUID SND_SLIDE_METAL_METAL_06 ("cd423231-e70d-4fd2-ad26-f1c6cf5f0610");
+const LLUUID SND_SLIDE_METAL_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_METAL_RUBBER ("12d97bc0-3c15-4744-b6bd-77d1316eb4f0");
+const LLUUID SND_SLIDE_METAL_WOOD ("4afb6926-a73f-4cb7-85d5-0f9a40107434");
+const LLUUID SND_SLIDE_METAL_WOOD_02 ("349970bf-187d-4bcb-b2cf-e7bb6581590f");
+const LLUUID SND_SLIDE_METAL_WOOD_03 ("64bf6e87-73d4-4cb4-84f7-55cecfd97cd3");
+const LLUUID SND_SLIDE_METAL_WOOD_04 ("0dc670a9-dbe8-41bc-b8ee-4d96d99219d5");
+const LLUUID SND_SLIDE_METAL_WOOD_05 ("6e3cc57b-c9aa-4829-86a1-8e82aeaccb47");
+const LLUUID SND_SLIDE_METAL_WOOD_06 ("c1237f4c-8c88-4da1-bfbc-2af26a8d9e5a");
+const LLUUID SND_SLIDE_METAL_WOOD_07 ("0e1ec243-063b-4dcb-a903-52b8dffed3d2");
+const LLUUID SND_SLIDE_METAL_WOOD_08 ("66736d0f-533d-4007-a8ee-0f27c2034126");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL ("35092c21-5c48-4b4d-a818-3cf240af2348");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_02("c37f5776-0020-47e8-89a0-c74cc6f5742d");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_03("d2fc8db6-2e66-464a-8ccb-f99b61ee4987");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_04("93cbdb10-6e82-4c0b-a547-7b3b79ac25f6");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_05("2f6d0542-fcd1-4264-a17b-f57bf5ebf402");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_06("5b8887d4-3be2-45a0-b25d-85af3b1e6392");
+const LLUUID SND_SLIDE_PLASTIC_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_PLASTIC_PLASTIC_02 (SND_NULL);
+const LLUUID SND_SLIDE_PLASTIC_PLASTIC_03 (SND_NULL);
+const LLUUID SND_SLIDE_PLASTIC_FABRIC ("7294d9ad-3e41-4373-992c-a9f21d5d66ad");
+const LLUUID SND_SLIDE_PLASTIC_FABRIC_02("58608ce1-f524-472f-b447-bbe6ce4a46e0");
+const LLUUID SND_SLIDE_PLASTIC_FABRIC_03("06ae285e-0b34-4ea6-84ab-9c6c31b414fc");
+const LLUUID SND_SLIDE_PLASTIC_FABRIC_04("211613db-0461-49bd-9554-5c14ad8b31f6");
+const LLUUID SND_SLIDE_RUBBER_PLASTIC ("a98ffa5a-e48e-4f9d-9242-b9a3210ad84a");
+const LLUUID SND_SLIDE_RUBBER_PLASTIC_02 ("d4136c40-eeaa-49c6-a982-8e5a16f5d93a");
+const LLUUID SND_SLIDE_RUBBER_PLASTIC_03 ("29ec0fb2-0b23-47b2-835b-c83cc7cf9fb0");
+const LLUUID SND_SLIDE_RUBBER_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_STONE_FLESH (SND_NULL);
+const LLUUID SND_SLIDE_STONE_GLASS (SND_NULL);
+const LLUUID SND_SLIDE_STONE_METAL (SND_NULL);
+const LLUUID SND_SLIDE_STONE_PLASTIC ("afd0bcc3-d41a-4572-9e7f-08a29eeb0b8a");
+const LLUUID SND_SLIDE_STONE_PLASTIC_02 ("881b720a-96cf-4128-bb98-5d87e03e93c7");
+const LLUUID SND_SLIDE_STONE_PLASTIC_03 ("293dac42-658a-4c5a-a7a2-6d4c5e5658b0");
+const LLUUID SND_SLIDE_STONE_RUBBER ("0724b946-6a3f-4eeb-bb50-0a3b33120974");
+const LLUUID SND_SLIDE_STONE_RUBBER_02 ("ada93d00-76e2-4bf1-9ad9-493727630717");
+const LLUUID SND_SLIDE_STONE_STONE ("ade766dc-2e75-4699-9b41-7c8e53d2b3f2");
+const LLUUID SND_SLIDE_STONE_STONE_02 ("66698375-6594-47b0-8046-c3973de1291d");
+const LLUUID SND_SLIDE_STONE_WOOD ("174ef324-ed50-4f65-9479-b4da580aeb3c");
+const LLUUID SND_SLIDE_STONE_WOOD_02 ("33d517fd-ff11-4d01-a7b5-0e3abf818dcf");
+const LLUUID SND_SLIDE_STONE_WOOD_03 ("1bac4b63-e6fd-4659-9761-991284cf4582");
+const LLUUID SND_SLIDE_STONE_WOOD_04 ("a7d28564-6821-4c01-a378-cde98fba7ba9");
+const LLUUID SND_SLIDE_WOOD_FABRIC ("22c58e74-22cd-4960-9ab7-5bf08ab824e5");
+const LLUUID SND_SLIDE_WOOD_FABRIC_02 ("0b0ed22e-4a0f-4617-a4cf-20d0f2b78ccc");
+const LLUUID SND_SLIDE_WOOD_FABRIC_03 ("42b80abb-9823-4b74-a210-326ccf23636a");
+const LLUUID SND_SLIDE_WOOD_FABRIC_04 ("8538298a-1e6b-4b69-a9ee-5e01e4a02b35");
+const LLUUID SND_SLIDE_WOOD_FLESH ("84b026f3-a11c-4366-aa7c-07edcd89b2bb");
+const LLUUID SND_SLIDE_WOOD_FLESH_02 ("2644191f-4848-47ba-8ba7-bddc0bfcb3da");
+const LLUUID SND_SLIDE_WOOD_FLESH_03 ("edb978e4-9be9-456f-b2fc-e8502bfe25be");
+const LLUUID SND_SLIDE_WOOD_FLESH_04 ("bf2b972e-f42a-46d7-b53e-5fca38f5bc61");
+const LLUUID SND_SLIDE_WOOD_GRAVEL ("d063bb4d-0eff-4403-a6cc-c6c6c073e624");
+const LLUUID SND_SLIDE_WOOD_GRAVEL_02 ("511eb679-6d93-47fa-9141-c3ef9261c919");
+const LLUUID SND_SLIDE_WOOD_GRAVEL_03 ("4ed1fd43-4707-4e5c-b7b7-21ec4e72c1ac");
+const LLUUID SND_SLIDE_WOOD_GRAVEL_04 ("99ea89b3-aa76-4b87-99c8-670365c6d8c3");
+const LLUUID SND_SLIDE_WOOD_PLASTIC ("505ca3c4-94a0-4e28-8fc1-ea72a428396b");
+const LLUUID SND_SLIDE_WOOD_PLASTIC_02 ("fc404011-df71-4ed0-8f22-b72bdd18f63c");
+const LLUUID SND_SLIDE_WOOD_PLASTIC_03 ("67dbe225-26df-4efa-8c8b-f1ef669fec45");
+const LLUUID SND_SLIDE_WOOD_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_WOOD_WOOD ("3079d569-b3e8-4df4-9e09-f0d4611213ef");
+const LLUUID SND_SLIDE_WOOD_WOOD_02 ("276b093d-dbcb-4279-a89e-a54b0b416af6");
+const LLUUID SND_SLIDE_WOOD_WOOD_03 ("c3f3ca5e-2768-4081-847f-247139310fdb");
+const LLUUID SND_SLIDE_WOOD_WOOD_04 ("f08d44b8-ff87-4a98-9561-c72f1f2fec81");
+const LLUUID SND_SLIDE_WOOD_WOOD_05 ("2d8a58cf-f139-4238-8503-27d334d05c85");
+const LLUUID SND_SLIDE_WOOD_WOOD_06 ("e157ebbd-b12d-4225-aa7c-d47b026a7687");
+const LLUUID SND_SLIDE_WOOD_WOOD_07 ("35e17956-e7b4-478c-b274-e37db8a166b2");
+const LLUUID SND_SLIDE_WOOD_WOOD_08 ("e606fc65-0643-4964-9979-ff964fa6a62c");
+
+
+const LLUUID SND_ROLL_FLESH_FLESH (SND_NULL);
+const LLUUID SND_ROLL_FLESH_PLASTIC ("89a0be4c-848d-4a6e-8886-298f56c2cff4");
+const LLUUID SND_ROLL_FLESH_PLASTIC_02 ("beb06343-1aa1-4af2-b320-5d2ec31c53b1");
+const LLUUID SND_ROLL_FLESH_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_GLASS_GRAVEL ("ba795c74-7e09-4572-b495-e09886a46b86");
+const LLUUID SND_ROLL_GLASS_GRAVEL_02 ("4c93c3b7-14cb-4d9b-a7df-628ad935f1f2");
+const LLUUID SND_ROLL_GLASS_FLESH (SND_NULL);
+const LLUUID SND_ROLL_GLASS_GLASS (SND_NULL);
+const LLUUID SND_ROLL_GLASS_PLASTIC (SND_NULL);
+const LLUUID SND_ROLL_GLASS_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_GLASS_WOOD ("d40b1f48-a061-4f6e-b18f-4326a3dd5c29");
+const LLUUID SND_ROLL_GLASS_WOOD_02 ("78cd407a-bb36-4163-ba09-20f2e6d9d44b");
+const LLUUID SND_ROLL_GRAVEL_GRAVEL ("c7354cc3-6df5-4738-8dbb-b28a6ac46a05");
+const LLUUID SND_ROLL_GRAVEL_GRAVEL_02 ("01d194c4-72a6-47df-81a5-8db430faff87");
+const LLUUID SND_ROLL_METAL_FABRIC ("ce6e6564-20fd-48e4-81e2-cd3f81c00a3e");
+const LLUUID SND_ROLL_METAL_FABRIC_02 ("fc4d0065-32f6-4bb0-9f3f-f4737eb27163");
+const LLUUID SND_ROLL_METAL_FLESH (SND_NULL);
+const LLUUID SND_ROLL_METAL_GLASS ("63d530bb-a41f-402b-aa1f-be6b11959809");
+const LLUUID SND_ROLL_METAL_GLASS_02 ("f62642c2-6db5-4faa-8b77-939067d837c3");
+const LLUUID SND_ROLL_METAL_GLASS_03 ("db5b5a15-2817-4cd7-9f0b-9ad49b5e52c8");
+const LLUUID SND_ROLL_METAL_GRAVEL ("447164e3-9646-4c1a-a16d-606892891466");
+const LLUUID SND_ROLL_METAL_METAL ("c3c22cf3-5d1f-4cc3-b4b5-708b9f65979c");
+const LLUUID SND_ROLL_METAL_METAL_02 ("d8386277-a1ea-460e-b6fd-bb285c323bf1");
+const LLUUID SND_ROLL_METAL_METAL_03 ("69ee1f02-f9cd-4c8b-aedd-39a2d6705680");
+const LLUUID SND_ROLL_METAL_METAL_04 ("5cc6b5fd-26ce-47ad-b21d-3a7c190dd375");
+const LLUUID SND_ROLL_METAL_PLASTIC ("c6a9bbf6-df15-4713-9f84-7237fce4051e");
+const LLUUID SND_ROLL_METAL_PLASTIC_01 ("0fedb59b-2dbb-4cec-b6cc-8559ec027749");
+const LLUUID SND_ROLL_METAL_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_METAL_WOOD ("1d76af57-01b1-4c73-9a1d-69523bfa50ea");
+const LLUUID SND_ROLL_METAL_WOOD_02 ("78aa4e71-8e7c-4b90-a561-3ebdc639f99b");
+const LLUUID SND_ROLL_METAL_WOOD_03 ("777d95bf-962f-48fa-93bf-8c1806557d72");
+const LLUUID SND_ROLL_METAL_WOOD_04 ("1833da76-45e2-4a8b-97da-d17413e056c9");
+const LLUUID SND_ROLL_METAL_WOOD_05 ("b13e1232-3d8d-42e9-92ec-b30f9f823962");
+const LLUUID SND_ROLL_PLASTIC_FABRIC ("616a1f03-209f-4c55-b264-83a000b6ef0a");
+const LLUUID SND_ROLL_PLASTIC_PLASTIC ("873f3d82-00b2-4082-9c69-7aef3461dba1");
+const LLUUID SND_ROLL_PLASTIC_PLASTIC_02 ("cc39879f-ebc8-4405-a4fc-8342f5bed31e");
+const LLUUID SND_ROLL_RUBBER_PLASTIC (SND_NULL);
+const LLUUID SND_ROLL_RUBBER_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_STONE_FLESH (SND_NULL);
+const LLUUID SND_ROLL_STONE_GLASS (SND_NULL);
+const LLUUID SND_ROLL_STONE_METAL (SND_NULL);
+const LLUUID SND_ROLL_STONE_PLASTIC ("155f65a8-cae7-476e-a58b-fd362be7fd0e");
+const LLUUID SND_ROLL_STONE_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_STONE_STONE ("67d56e3f-6ed5-4658-9418-14f020c38b11");
+const LLUUID SND_ROLL_STONE_STONE_02 ("43d99d10-d75b-4246-accf-4ceb2c909aa7");
+const LLUUID SND_ROLL_STONE_STONE_03 ("f04e83ff-eed7-4e99-8f45-eb97e4e1d3b7");
+const LLUUID SND_ROLL_STONE_STONE_04 ("10fcc5ad-fa89-48d6-b774-986b580c1efc");
+const LLUUID SND_ROLL_STONE_STONE_05 ("3d86f5a3-1a91-49d9-b99f-8521a7422497");
+const LLUUID SND_ROLL_STONE_WOOD ("53e46fb7-6c21-4fe1-bffe-0567475d48fa");
+const LLUUID SND_ROLL_STONE_WOOD_02 ("5eba8c9a-a014-4299-87f1-315c45ec795b");
+const LLUUID SND_ROLL_STONE_WOOD_03 ("ea6c05fc-6e9c-4526-8a20-bc47810bb549");
+const LLUUID SND_ROLL_STONE_WOOD_04 ("64618cbf-3f42-4728-8094-e77807545efb");
+const LLUUID SND_ROLL_WOOD_FLESH ("26ee185d-6fc3-49f8-89ba-51cab04cfc42");
+const LLUUID SND_ROLL_WOOD_FLESH_02 ("334faa25-1e80-4c99-b29f-4c9c2a3d079d");
+const LLUUID SND_ROLL_WOOD_FLESH_03 ("2f876626-4dce-4f71-a91e-a25302edfab7");
+const LLUUID SND_ROLL_WOOD_FLESH_04 ("d6877aac-07fc-4931-bcde-585f223802ad");
+const LLUUID SND_ROLL_WOOD_GRAVEL ("2a23ebb5-a4a2-4f1f-8d75-7384239354aa");
+const LLUUID SND_ROLL_WOOD_GRAVEL_02 ("208bf26d-f097-450c-95c4-9d26317c613c");
+const LLUUID SND_ROLL_WOOD_GRAVEL_03 ("a26ecaf4-92c6-4e32-9864-56b7c70cab8e");
+const LLUUID SND_ROLL_WOOD_PLASTIC ("71c1000a-9f16-4cc3-8ede-ec4aa3bf5723");
+const LLUUID SND_ROLL_WOOD_PLASTIC_02 ("7bc20ba6-1e6d-4eea-83ad-c5cc3ae0e409");
+const LLUUID SND_ROLL_WOOD_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_WOOD_WOOD ("2cc8eec4-bb4a-4ba8-b783-71526ec708e8");
+const LLUUID SND_ROLL_WOOD_WOOD_02 ("0a1f8070-a11a-4b4c-b260-5ffb6acb0a5d");
+const LLUUID SND_ROLL_WOOD_WOOD_03 ("160bef64-da9c-4be8-b07b-a5060b501700");
+const LLUUID SND_ROLL_WOOD_WOOD_04 ("1c62ea16-cc60-48ed-829a-68b8f4cf0c1c");
+const LLUUID SND_ROLL_WOOD_WOOD_05 ("be9cc8fe-b920-4bf5-8924-453088cbc03f");
+const LLUUID SND_ROLL_WOOD_WOOD_06 ("a76cfe60-56b0-43b1-8f31-93e56947d78b");
+const LLUUID SND_ROLL_WOOD_WOOD_07 ("0c6aa481-b5bc-4573-ae83-8e16ff27e750");
+const LLUUID SND_ROLL_WOOD_WOOD_08 ("214ab2c7-871a-451b-b0db-4c5677199011");
+const LLUUID SND_ROLL_WOOD_WOOD_09 ("0086e4db-3ac6-4545-b414-6f359bedd9a5");
+
+const LLUUID SND_SLIDE_STONE_STONE_01 ("2a7dcbd1-d3e6-4767-8432-8322648e7b9d");
+
+const LLUUID SND_STONE_DIRT_01 ("97727335-392c-4338-ac4b-23a7883279c2");
+const LLUUID SND_STONE_DIRT_02 ("cbe75eb2-3375-41d8-9e3f-2ae46b4164ed");
+const LLUUID SND_STONE_DIRT_03 ("31e236ee-001b-4c8e-ad6c-c2074cb64357");
+const LLUUID SND_STONE_DIRT_04 ("c8091652-e04b-4a11-84ba-15dba06e7a1b");
+
+const LLUUID SND_STONE_STONE_02 ("ba4ef5ac-7435-4240-b826-c24ba8fa5a78");
+const LLUUID SND_STONE_STONE_04 ("ea296329-0f09-4993-af1b-e6784bab1dc9");
+
+
+
+// extra guids
+#if 0
+const LLUUID SND_ ("a839b8ac-b0af-4ba9-9fde-188754744e02");
+const LLUUID SND_ ("20165fa8-836f-4993-85dc-1529172dcd14");
+const LLUUID SND_ ("fba8e17b-a4b3-4693-9fce-c14800f8a349");
+const LLUUID SND_ ("2d48db8b-7260-4b02-ad2a-b2c6bee60e94");
+const LLUUID SND_ ("956d344b-1808-4d8b-88b1-cbc82b7a96a1");
+const LLUUID SND_ ("b8303cc6-f0b4-4c6f-a199-81f87aba342e");
+const LLUUID SND_ ("fbf7cd0c-bc8f-4cba-9c19-11f4dd03a06b");
+const LLUUID SND_ ("85047f7d-933a-4ce5-a7b5-34670243e1ab");
+const LLUUID SND_ ("0f81acf7-6a2e-4490-957f-c7b0eda00559");
+const LLUUID SND_ ("5631a6a1-79b4-4de8-bccf-1880b6882da1");
+const LLUUID SND_ ("43c87a6b-ffb2-437b-89a0-9deba890a4fc");
+const LLUUID SND_ ("58878d1d-3156-4d01-ac3c-0c4fb99f4d53");
+const LLUUID SND_ ("9a83f321-44bf-40f6-b006-46c085515345");
+const LLUUID SND_ ("ff144533-33ab-40f2-bac8-39c34699ecc4");
+const LLUUID SND_ ("09018e87-d52c-4cd5-9805-015f413319e7");
+const LLUUID SND_ ("17d4c057-7edd-401e-9589-d5b9fe981bf2");
+#endif
+
+#endif