diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llmessage |
Print done when done.
Diffstat (limited to 'indra/llmessage')
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 << "<"; + break; + case '>': + out << ">"; + break; + case '&': + out << "&"; + break; + case '\'': + out << "'"; + break; + case '"': + out << """; + 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 ®ion_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 ®ion_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 ®ion_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 ¶ms, + 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 ¶ms, + 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 ¶ms) = 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 ¶ms) +{ + 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 ¶ms); + /*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 ¶ms) +{ + 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 ¶ms); + /*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 |