diff options
Diffstat (limited to 'indra/llmessage')
80 files changed, 33268 insertions, 33268 deletions
diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 6b6d4a3ae7..88fa572092 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -1,431 +1,431 @@ -/**
- * @file llassetstorage.h
- * @brief definition of LLAssetStorage class which allows simple
- * up/downloads of uuid,type asets
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLASSETSTORAGE_H
-#define LL_LLASSETSTORAGE_H
-#include <string>
-#include <functional>
-
-#include "lluuid.h"
-#include "lltimer.h"
-#include "llnamevalue.h"
-#include "llhost.h"
-#include "lltransfermanager.h" // For LLTSCode enum
-#include "llassettype.h"
-#include "llstring.h"
-#include "llextendedstatus.h"
-#include "llxfer.h"
-
-// Forward declarations
-class LLMessageSystem;
-class LLXferManager;
-class LLAssetStorage;
-class LLSD;
-
-// anything that takes longer than this to download will abort.
-// HTTP Uploads also timeout if they take longer than this.
-const F32Minutes LL_ASSET_STORAGE_TIMEOUT(5);
-
-
-// Specific error codes
-const int LL_ERR_ASSET_REQUEST_FAILED = -1;
-//const int LL_ERR_ASSET_REQUEST_INVALID = -2;
-const int LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3;
-const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4;
-const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5;
-const int LL_ERR_PRICE_MISMATCH = -23018;
-
-// *TODO: these typedefs are passed into the cache via a legacy C function pointer
-// future project would be to convert these to C++ callables (std::function<>) so that
-// we can use bind and remove the userData parameter.
-//
-typedef std::function<void(const LLUUID &asset_id, LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status)> LLGetAssetCallback;
-typedef std::function<void(const LLUUID &asset_id, void *user_data, S32 status, LLExtStat ext_status)> LLStoreAssetCallback;
-
-
-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 LLBaseDownloadRequest
-{
-public:
- LLBaseDownloadRequest(const LLUUID &uuid, const LLAssetType::EType at);
- virtual ~LLBaseDownloadRequest();
-
- LLUUID getUUID() const { return mUUID; }
- LLAssetType::EType getType() const { return mType; }
-
- void setUUID(const LLUUID& id) { mUUID = id; }
- void setType(LLAssetType::EType type) { mType = type; }
-
- virtual LLBaseDownloadRequest* getCopy();
-
-protected:
- LLUUID mUUID;
- LLAssetType::EType mType;
-
-public:
- LLGetAssetCallback mDownCallback;
-
- void *mUserData;
- LLHost mHost;
- bool mIsTemp;
- F64Seconds mTime; // Message system time
- bool mIsPriority;
- bool mDataSentInFirstPacket;
- bool mDataIsInCache;
-};
-
-class LLAssetRequest : public LLBaseDownloadRequest
-{
-public:
- LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType at);
- virtual ~LLAssetRequest();
-
- void setTimeout(F64Seconds timeout) { mTimeout = timeout; }
-
- virtual LLBaseDownloadRequest* getCopy();
-
- LLStoreAssetCallback mUpCallback;
-// void (*mUpCallback)(const LLUUID&, void *, S32, LLExtStat);
- void (*mInfoCallback)(LLAssetInfo *, void *, S32);
-
- bool mIsLocal;
- bool mIsUserWaiting; // We don't want to try forever if a user is waiting for a result.
- F64Seconds mTimeout; // Amount of time before timing out.
- LLUUID mRequestingAgentID; // Only valid for uploads from an agent
- F64 mBytesFetched;
-
- virtual LLSD getTerseDetails() const;
- virtual LLSD getFullDetails() const;
-};
-
-template <class T>
-struct ll_asset_request_equal : public std::equal_to<T>
-{
- bool operator()(const T& x, const T& y) const
- {
- return ( x->getType() == y->getType()
- && x->getUUID() == y->getUUID() );
- }
-};
-
-
-class LLInvItemRequest : public LLBaseDownloadRequest
-{
-public:
- LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType at);
- virtual ~LLInvItemRequest();
-
- virtual LLBaseDownloadRequest* getCopy();
-};
-
-class LLEstateAssetRequest : public LLBaseDownloadRequest
-{
-public:
- LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType at, EstateAssetType et);
- virtual ~LLEstateAssetRequest();
-
- LLAssetType::EType getAType() const { return mType; }
-
- virtual LLBaseDownloadRequest* getCopy();
-
-protected:
- EstateAssetType mEstateAssetType;
-};
-
-
-// Map of known bad assets
-typedef std::map<LLUUID,U64,lluuid_less> toxic_asset_map_t;
-
-
-
-class LLAssetStorage
-{
-public:
- typedef ::LLStoreAssetCallback LLStoreAssetCallback;
- typedef ::LLGetAssetCallback LLGetAssetCallback;
-
- enum ERequestType
- {
- RT_INVALID = -1,
- RT_DOWNLOAD = 0,
- RT_UPLOAD = 1,
- RT_LOCALUPLOAD = 2,
- RT_COUNT = 3
- };
-
-protected:
- bool mShutDown;
- LLHost mUpstreamHost;
-
- LLMessageSystem *mMessageSys;
- LLXferManager *mXferManager;
-
-
- typedef std::list<LLAssetRequest*> request_list_t;
- request_list_t mPendingDownloads;
- request_list_t mPendingUploads;
- request_list_t mPendingLocalUploads;
-
- // Map of toxic assets - these caused problems when recently rezzed, so avoid them
- toxic_asset_map_t mToxicAssetMap; // Objects in this list are known to cause problems and are not loaded
-
-public:
- LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host);
-
- LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer);
- virtual ~LLAssetStorage();
-
- void setUpstream(const LLHost &upstream_host);
-
- bool hasLocalAsset(const LLUUID &uuid, LLAssetType::EType type);
-
- // public interface methods
- // note that your callback may get called BEFORE the function returns
- 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,
- bool user_waiting= false,
- F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT) = 0;
-
- virtual void logAssetStorageInfo() = 0;
-
- 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.
-
- // Check if an asset is in the toxic map. If it is, the entry is updated
- bool isAssetToxic( const LLUUID& uuid );
-
- // Clean the toxic asset list, remove old entries
- void flushOldToxicAssets( bool force_it );
-
- // Add an item to the toxic asset map
- void markAssetToxic( const LLUUID& uuid );
-
-protected:
- bool findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type,
- LLGetAssetCallback callback, void *user_data);
-
- LLSD getPendingDetailsImpl(const request_list_t* requests,
- LLAssetType::EType asset_type,
- const std::string& detail_prefix) const;
-
- LLSD getPendingRequestImpl(const request_list_t* requests,
- LLAssetType::EType asset_type,
- const LLUUID& asset_id) const;
-
- bool deletePendingRequestImpl(request_list_t* requests,
- LLAssetType::EType asset_type,
- const LLUUID& asset_id);
-
-public:
- static const LLAssetRequest* findRequest(const request_list_t* requests,
- LLAssetType::EType asset_type,
- const LLUUID& asset_id);
- static LLAssetRequest* findRequest(request_list_t* requests,
- LLAssetType::EType asset_type,
- const LLUUID& asset_id);
-
- request_list_t* getRequestList(ERequestType rt);
- const request_list_t* getRequestList(ERequestType rt) const;
- static std::string getRequestName(ERequestType rt);
-
- S32 getNumPendingDownloads() const;
- S32 getNumPendingUploads() const;
- S32 getNumPendingLocalUploads();
- S32 getNumPending(ERequestType rt) const;
-
- LLSD getPendingDetails(ERequestType rt,
- LLAssetType::EType asset_type,
- const std::string& detail_prefix) const;
-
- LLSD getPendingRequest(ERequestType rt,
- LLAssetType::EType asset_type,
- const LLUUID& asset_id) const;
-
- bool deletePendingRequest(ERequestType rt,
- LLAssetType::EType asset_type,
- const LLUUID& asset_id);
-
-
- static void removeAndCallbackPendingDownloads(const LLUUID& file_id, LLAssetType::EType file_type,
- const LLUUID& callback_id, LLAssetType::EType callback_type,
- S32 result_code, LLExtStat ext_status);
-
- // download process callbacks
- static void downloadCompleteCallback(
- S32 result,
- const LLUUID& file_id,
- LLAssetType::EType file_type,
- LLBaseDownloadRequest* user_data, LLExtStat ext_status);
- static void downloadEstateAssetCompleteCallback(
- S32 result,
- const LLUUID& file_id,
- LLAssetType::EType file_type,
- LLBaseDownloadRequest* user_data, LLExtStat ext_status);
- static void downloadInvItemCompleteCallback(
- S32 result,
- const LLUUID& file_id,
- LLAssetType::EType file_type,
- LLBaseDownloadRequest* user_data, LLExtStat ext_status);
-
- // upload process callbacks
- static void uploadCompleteCallback(const LLUUID&, void *user_data, S32 result, LLExtStat ext_status);
- static void processUploadComplete(LLMessageSystem *msg, void **this_handle);
-
- // debugging
- static const char* getErrorString( S32 status );
-
- // deprecated file-based methods
- // Not overriden
- void getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32, LLExtStat), void *user_data, bool is_priority = false);
-
- /*
- * TransactionID version
- */
- virtual void storeAssetData(
- const std::string& filename,
- const LLTransactionID &transaction_id,
- LLAssetType::EType type,
- LLStoreAssetCallback callback,
- void *user_data,
- bool temp_file = false,
- bool is_priority = false,
- bool user_waiting = false,
- F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT) = 0;
-
- static void legacyGetDataCallback(const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status);
- static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status);
-
- // 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, LLExtStat ext_status);
-
- virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback,
- void *user_data, bool duplicate,
- bool is_priority) = 0;
-
-private:
- void _init(LLMessageSystem *msg,
- LLXferManager *xfer,
- const LLHost &upstream_host);
-
-protected:
- enum EMetricResult
- {
- // Static valued enums for #dw readability - please copy this
- // declaration to them on updates -- source in llassetstorage.h
- MR_INVALID = -1, // Makes no sense
- MR_OKAY = 0, // Success - no metric normally
- MR_ZERO_SIZE = 1, // Zero size asset
- MR_BAD_FUNCTION = 2, // Tried to use a virtual base (PROGRAMMER ERROR)
- MR_FILE_NONEXIST = 3, // Old format store call - source file does not exist
- MR_NO_FILENAME = 4, // Old format store call - source filename is NULL/0-length
- MR_NO_UPSTREAM = 5, // Upstream provider is missing
- MR_CACHE_CORRUPTION = 6 // cache is corrupt - too-large or mismatched stated/returned sizes
- };
-
- static class LLMetrics *metric_recipient;
-
- static void reportMetric( const LLUUID& asset_id, const LLAssetType::EType asset_type, const std::string& filename,
- const LLUUID& agent_id, S32 asset_size, EMetricResult result,
- const char* file, const S32 line, const std::string& message );
-public:
- static void setMetricRecipient( LLMetrics *recip )
- {
- metric_recipient = recip;
- }
-};
-
-////////////////////////////////////////////////////////////////////////
-// Wrappers to replicate deprecated API
-////////////////////////////////////////////////////////////////////////
-
-class LLLegacyAssetRequest
-{
-public:
- void (*mDownCallback)(const char *, const LLUUID&, void *, S32, LLExtStat);
- LLStoreAssetCallback mUpCallback;
-
- void *mUserData;
-};
-
-extern LLAssetStorage *gAssetStorage;
-extern const LLUUID CATEGORIZE_LOST_AND_FOUND_ID;
-#endif
+/** + * @file llassetstorage.h + * @brief definition of LLAssetStorage class which allows simple + * up/downloads of uuid,type asets + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLASSETSTORAGE_H +#define LL_LLASSETSTORAGE_H +#include <string> +#include <functional> + +#include "lluuid.h" +#include "lltimer.h" +#include "llnamevalue.h" +#include "llhost.h" +#include "lltransfermanager.h" // For LLTSCode enum +#include "llassettype.h" +#include "llstring.h" +#include "llextendedstatus.h" +#include "llxfer.h" + +// Forward declarations +class LLMessageSystem; +class LLXferManager; +class LLAssetStorage; +class LLSD; + +// anything that takes longer than this to download will abort. +// HTTP Uploads also timeout if they take longer than this. +const F32Minutes LL_ASSET_STORAGE_TIMEOUT(5); + + +// Specific error codes +const int LL_ERR_ASSET_REQUEST_FAILED = -1; +//const int LL_ERR_ASSET_REQUEST_INVALID = -2; +const int LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3; +const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4; +const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5; +const int LL_ERR_PRICE_MISMATCH = -23018; + +// *TODO: these typedefs are passed into the cache via a legacy C function pointer +// future project would be to convert these to C++ callables (std::function<>) so that +// we can use bind and remove the userData parameter. +// +typedef std::function<void(const LLUUID &asset_id, LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status)> LLGetAssetCallback; +typedef std::function<void(const LLUUID &asset_id, void *user_data, S32 status, LLExtStat ext_status)> LLStoreAssetCallback; + + +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 LLBaseDownloadRequest +{ +public: + LLBaseDownloadRequest(const LLUUID &uuid, const LLAssetType::EType at); + virtual ~LLBaseDownloadRequest(); + + LLUUID getUUID() const { return mUUID; } + LLAssetType::EType getType() const { return mType; } + + void setUUID(const LLUUID& id) { mUUID = id; } + void setType(LLAssetType::EType type) { mType = type; } + + virtual LLBaseDownloadRequest* getCopy(); + +protected: + LLUUID mUUID; + LLAssetType::EType mType; + +public: + LLGetAssetCallback mDownCallback; + + void *mUserData; + LLHost mHost; + bool mIsTemp; + F64Seconds mTime; // Message system time + bool mIsPriority; + bool mDataSentInFirstPacket; + bool mDataIsInCache; +}; + +class LLAssetRequest : public LLBaseDownloadRequest +{ +public: + LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType at); + virtual ~LLAssetRequest(); + + void setTimeout(F64Seconds timeout) { mTimeout = timeout; } + + virtual LLBaseDownloadRequest* getCopy(); + + LLStoreAssetCallback mUpCallback; +// void (*mUpCallback)(const LLUUID&, void *, S32, LLExtStat); + void (*mInfoCallback)(LLAssetInfo *, void *, S32); + + bool mIsLocal; + bool mIsUserWaiting; // We don't want to try forever if a user is waiting for a result. + F64Seconds mTimeout; // Amount of time before timing out. + LLUUID mRequestingAgentID; // Only valid for uploads from an agent + F64 mBytesFetched; + + virtual LLSD getTerseDetails() const; + virtual LLSD getFullDetails() const; +}; + +template <class T> +struct ll_asset_request_equal : public std::equal_to<T> +{ + bool operator()(const T& x, const T& y) const + { + return ( x->getType() == y->getType() + && x->getUUID() == y->getUUID() ); + } +}; + + +class LLInvItemRequest : public LLBaseDownloadRequest +{ +public: + LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType at); + virtual ~LLInvItemRequest(); + + virtual LLBaseDownloadRequest* getCopy(); +}; + +class LLEstateAssetRequest : public LLBaseDownloadRequest +{ +public: + LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType at, EstateAssetType et); + virtual ~LLEstateAssetRequest(); + + LLAssetType::EType getAType() const { return mType; } + + virtual LLBaseDownloadRequest* getCopy(); + +protected: + EstateAssetType mEstateAssetType; +}; + + +// Map of known bad assets +typedef std::map<LLUUID,U64,lluuid_less> toxic_asset_map_t; + + + +class LLAssetStorage +{ +public: + typedef ::LLStoreAssetCallback LLStoreAssetCallback; + typedef ::LLGetAssetCallback LLGetAssetCallback; + + enum ERequestType + { + RT_INVALID = -1, + RT_DOWNLOAD = 0, + RT_UPLOAD = 1, + RT_LOCALUPLOAD = 2, + RT_COUNT = 3 + }; + +protected: + bool mShutDown; + LLHost mUpstreamHost; + + LLMessageSystem *mMessageSys; + LLXferManager *mXferManager; + + + typedef std::list<LLAssetRequest*> request_list_t; + request_list_t mPendingDownloads; + request_list_t mPendingUploads; + request_list_t mPendingLocalUploads; + + // Map of toxic assets - these caused problems when recently rezzed, so avoid them + toxic_asset_map_t mToxicAssetMap; // Objects in this list are known to cause problems and are not loaded + +public: + LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host); + + LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer); + virtual ~LLAssetStorage(); + + void setUpstream(const LLHost &upstream_host); + + bool hasLocalAsset(const LLUUID &uuid, LLAssetType::EType type); + + // public interface methods + // note that your callback may get called BEFORE the function returns + 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, + bool user_waiting= false, + F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT) = 0; + + virtual void logAssetStorageInfo() = 0; + + 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. + + // Check if an asset is in the toxic map. If it is, the entry is updated + bool isAssetToxic( const LLUUID& uuid ); + + // Clean the toxic asset list, remove old entries + void flushOldToxicAssets( bool force_it ); + + // Add an item to the toxic asset map + void markAssetToxic( const LLUUID& uuid ); + +protected: + bool findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, + LLGetAssetCallback callback, void *user_data); + + LLSD getPendingDetailsImpl(const request_list_t* requests, + LLAssetType::EType asset_type, + const std::string& detail_prefix) const; + + LLSD getPendingRequestImpl(const request_list_t* requests, + LLAssetType::EType asset_type, + const LLUUID& asset_id) const; + + bool deletePendingRequestImpl(request_list_t* requests, + LLAssetType::EType asset_type, + const LLUUID& asset_id); + +public: + static const LLAssetRequest* findRequest(const request_list_t* requests, + LLAssetType::EType asset_type, + const LLUUID& asset_id); + static LLAssetRequest* findRequest(request_list_t* requests, + LLAssetType::EType asset_type, + const LLUUID& asset_id); + + request_list_t* getRequestList(ERequestType rt); + const request_list_t* getRequestList(ERequestType rt) const; + static std::string getRequestName(ERequestType rt); + + S32 getNumPendingDownloads() const; + S32 getNumPendingUploads() const; + S32 getNumPendingLocalUploads(); + S32 getNumPending(ERequestType rt) const; + + LLSD getPendingDetails(ERequestType rt, + LLAssetType::EType asset_type, + const std::string& detail_prefix) const; + + LLSD getPendingRequest(ERequestType rt, + LLAssetType::EType asset_type, + const LLUUID& asset_id) const; + + bool deletePendingRequest(ERequestType rt, + LLAssetType::EType asset_type, + const LLUUID& asset_id); + + + static void removeAndCallbackPendingDownloads(const LLUUID& file_id, LLAssetType::EType file_type, + const LLUUID& callback_id, LLAssetType::EType callback_type, + S32 result_code, LLExtStat ext_status); + + // download process callbacks + static void downloadCompleteCallback( + S32 result, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, LLExtStat ext_status); + static void downloadEstateAssetCompleteCallback( + S32 result, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, LLExtStat ext_status); + static void downloadInvItemCompleteCallback( + S32 result, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, LLExtStat ext_status); + + // upload process callbacks + static void uploadCompleteCallback(const LLUUID&, void *user_data, S32 result, LLExtStat ext_status); + static void processUploadComplete(LLMessageSystem *msg, void **this_handle); + + // debugging + static const char* getErrorString( S32 status ); + + // deprecated file-based methods + // Not overriden + void getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32, LLExtStat), void *user_data, bool is_priority = false); + + /* + * TransactionID version + */ + virtual void storeAssetData( + const std::string& filename, + const LLTransactionID &transaction_id, + LLAssetType::EType type, + LLStoreAssetCallback callback, + void *user_data, + bool temp_file = false, + bool is_priority = false, + bool user_waiting = false, + F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT) = 0; + + static void legacyGetDataCallback(const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status); + static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status); + + // 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, LLExtStat ext_status); + + virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, + void *user_data, bool duplicate, + bool is_priority) = 0; + +private: + void _init(LLMessageSystem *msg, + LLXferManager *xfer, + const LLHost &upstream_host); + +protected: + enum EMetricResult + { + // Static valued enums for #dw readability - please copy this + // declaration to them on updates -- source in llassetstorage.h + MR_INVALID = -1, // Makes no sense + MR_OKAY = 0, // Success - no metric normally + MR_ZERO_SIZE = 1, // Zero size asset + MR_BAD_FUNCTION = 2, // Tried to use a virtual base (PROGRAMMER ERROR) + MR_FILE_NONEXIST = 3, // Old format store call - source file does not exist + MR_NO_FILENAME = 4, // Old format store call - source filename is NULL/0-length + MR_NO_UPSTREAM = 5, // Upstream provider is missing + MR_CACHE_CORRUPTION = 6 // cache is corrupt - too-large or mismatched stated/returned sizes + }; + + static class LLMetrics *metric_recipient; + + static void reportMetric( const LLUUID& asset_id, const LLAssetType::EType asset_type, const std::string& filename, + const LLUUID& agent_id, S32 asset_size, EMetricResult result, + const char* file, const S32 line, const std::string& message ); +public: + static void setMetricRecipient( LLMetrics *recip ) + { + metric_recipient = recip; + } +}; + +//////////////////////////////////////////////////////////////////////// +// Wrappers to replicate deprecated API +//////////////////////////////////////////////////////////////////////// + +class LLLegacyAssetRequest +{ +public: + void (*mDownCallback)(const char *, const LLUUID&, void *, S32, LLExtStat); + LLStoreAssetCallback mUpCallback; + + void *mUserData; +}; + +extern LLAssetStorage *gAssetStorage; +extern const LLUUID CATEGORIZE_LOST_AND_FOUND_ID; +#endif diff --git a/indra/llmessage/llblowfishcipher.h b/indra/llmessage/llblowfishcipher.h index 59d6f159c5..53dc94cce9 100644 --- a/indra/llmessage/llblowfishcipher.h +++ b/indra/llmessage/llblowfishcipher.h @@ -1,57 +1,57 @@ -/**
- * @file llblowfishcipher.h
- * @brief A symmetric block cipher, designed in 1993 by Bruce Schneier.
- * We use it because it has an 8 byte block size, allowing encryption of
- * two UUIDs and a timestamp (16x2 + 4 = 36 bytes) with only 40 bytes of
- * output. AES has a block size of 32 bytes, so this would require 64 bytes.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LLBLOWFISHCIPHER_H
-#define LLBLOWFISHCIPHER_H
-
-#include "llcipher.h"
-
-
-class LLBlowfishCipher : public LLCipher
-{
-public:
- // Secret may be up to 56 bytes in length per Blowfish spec.
- LLBlowfishCipher(const U8* secret, size_t secret_size);
- virtual ~LLBlowfishCipher();
-
- // See llcipher.h for documentation.
- /*virtual*/ U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
- /*virtual*/ U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
- /*virtual*/ U32 requiredEncryptionSpace(U32 src_len) const;
-
-#ifdef _DEBUG
- static bool testHarness();
-#endif
-
-private:
- U8* mSecret;
- size_t mSecretSize;
-};
-
-#endif // LL_LLCRYPTO_H
+/** + * @file llblowfishcipher.h + * @brief A symmetric block cipher, designed in 1993 by Bruce Schneier. + * We use it because it has an 8 byte block size, allowing encryption of + * two UUIDs and a timestamp (16x2 + 4 = 36 bytes) with only 40 bytes of + * output. AES has a block size of 32 bytes, so this would require 64 bytes. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLBLOWFISHCIPHER_H +#define LLBLOWFISHCIPHER_H + +#include "llcipher.h" + + +class LLBlowfishCipher : public LLCipher +{ +public: + // Secret may be up to 56 bytes in length per Blowfish spec. + LLBlowfishCipher(const U8* secret, size_t secret_size); + virtual ~LLBlowfishCipher(); + + // See llcipher.h for documentation. + /*virtual*/ U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + /*virtual*/ U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + /*virtual*/ U32 requiredEncryptionSpace(U32 src_len) const; + +#ifdef _DEBUG + static bool testHarness(); +#endif + +private: + U8* mSecret; + size_t mSecretSize; +}; + +#endif // LL_LLCRYPTO_H diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index cb654f5a30..6fb21957e0 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -1,1021 +1,1021 @@ -/**
- * @file llcachename.cpp
- * @brief A hierarchical cache of first and last names queried based on UUID.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llcachename.h"
-
-// linden library includes
-#include "lldbstrings.h"
-#include "llframetimer.h"
-#include "llhost.h"
-#include "llrand.h"
-#include "llsdserialize.h"
-#include "lluuid.h"
-#include "message.h"
-
-#include <boost/regex.hpp>
-
-// llsd serialization constants
-static const std::string AGENTS("agents");
-static const std::string GROUPS("groups");
-static const std::string CTIME("ctime");
-static const std::string FIRST("first");
-static const std::string LAST("last");
-static const std::string NAME("name");
-
-// We track name requests in flight for up to this long.
-// We won't re-request a name during this time
-const U32 PENDING_TIMEOUT_SECS = 5 * 60;
-
-// Globals
-LLCacheName* gCacheName = NULL;
-std::map<std::string, std::string> LLCacheName::sCacheName;
-
-/// ---------------------------------------------------------------------------
-/// class LLCacheNameEntry
-/// ---------------------------------------------------------------------------
-
-class LLCacheNameEntry
-{
-public:
- LLCacheNameEntry();
-
-public:
- bool mIsGroup;
- U32 mCreateTime; // unix time_t
- // IDEVO TODO collapse names to one field, which will eliminate
- // many string compares on "Resident"
- std::string mFirstName;
- std::string mLastName;
- std::string mGroupName;
-};
-
-LLCacheNameEntry::LLCacheNameEntry()
- : mIsGroup(false),
- mCreateTime(0)
-{
-}
-
-
-class PendingReply
-{
-public:
- LLUUID mID;
- LLCacheNameSignal mSignal;
- LLHost mHost;
-
- PendingReply(const LLUUID& id, const LLHost& host)
- : mID(id), mHost(host)
- {
- }
-
- boost::signals2::connection setCallback(const LLCacheNameCallback& cb)
- {
- return mSignal.connect(cb);
- }
-
- void done() { mID.setNull(); }
- bool isDone() const { return mID.isNull(); }
-};
-
-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), mCurrIsGroup(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::set<LLUUID> AskQueue;
-typedef std::list<PendingReply*> ReplyQueue;
-typedef std::map<LLUUID,U32> PendingQueue;
-typedef std::map<LLUUID, LLCacheNameEntry*> Cache;
-typedef std::map<std::string, LLUUID> ReverseCache;
-
-class LLCacheName::Impl
-{
-public:
- LLMessageSystem* mMsg;
- LLHost mUpstreamHost;
-
- Cache mCache;
- // the map of UUIDs to names
- ReverseCache mReverseCache;
- // map of names to UUIDs
-
- AskQueue mAskNameQueue;
- AskQueue mAskGroupQueue;
- // UUIDs to ask our upstream host about
-
- PendingQueue mPendingQueue;
- // UUIDs that have been requested but are not in cache yet.
-
- ReplyQueue mReplyQueue;
- // requests awaiting replies from us
-
- LLCacheNameSignal mSignal;
-
- LLFrameTimer mProcessTimer;
-
- Impl(LLMessageSystem* msg);
- ~Impl();
-
- bool getName(const LLUUID& id, std::string& first, std::string& last);
-
- boost::signals2::connection addPending(const LLUUID& id, const LLCacheNameCallback& callback);
- void addPending(const LLUUID& id, const LLHost& host);
-
- void processPendingAsks();
- void processPendingReplies();
- void sendRequest(const char* msg_name, const AskQueue& queue);
- bool isRequestPending(const LLUUID& id);
-
- // 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);
-};
-
-
-/// --------------------------------------------------------------------------
-/// class LLCacheName
-/// ---------------------------------------------------------------------------
-
-LLCacheName::LLCacheName(LLMessageSystem* msg)
- : impl(* new Impl(msg))
- { }
-
-LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host)
- : impl(* new Impl(msg))
-{
- sCacheName["waiting"] = "(Loading...)";
- sCacheName["nobody"] = "(nobody)";
- sCacheName["none"] = "(none)";
- setUpstream(upstream_host);
-}
-
-LLCacheName::~LLCacheName()
-{
- delete &impl;
-}
-
-LLCacheName::Impl::Impl(LLMessageSystem* msg)
- : mMsg(msg), mUpstreamHost(LLHost())
-{
- 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());
- mCache.clear();
- for_each(mReplyQueue.begin(), mReplyQueue.end(), DeletePointer());
- mReplyQueue.clear();
-}
-
-boost::signals2::connection LLCacheName::Impl::addPending(const LLUUID& id, const LLCacheNameCallback& callback)
-{
- PendingReply* reply = new PendingReply(id, LLHost());
- boost::signals2::connection res = reply->setCallback(callback);
- mReplyQueue.push_back(reply);
- return res;
-}
-
-void LLCacheName::Impl::addPending(const LLUUID& id, const LLHost& host)
-{
- PendingReply* reply = new PendingReply(id, host);
- mReplyQueue.push_back(reply);
-}
-
-void LLCacheName::setUpstream(const LLHost& upstream_host)
-{
- impl.mUpstreamHost = upstream_host;
-}
-
-boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& callback)
-{
- return impl.mSignal.connect(callback);
-}
-
-bool LLCacheName::importFile(std::istream& istr)
-{
- LLSD data;
- if(LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(data, istr))
- {
- return false;
- }
-
- // 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);
-
- // iterate over the agents
- S32 count = 0;
- LLSD agents = data[AGENTS];
- LLSD::map_iterator iter = agents.beginMap();
- LLSD::map_iterator end = agents.endMap();
- for( ; iter != end; ++iter)
- {
- LLUUID id((*iter).first);
- LLSD agent = (*iter).second;
- U32 ctime = (U32)agent[CTIME].asInteger();
- if(ctime < delete_before_time) continue;
-
- LLCacheNameEntry* entry = new LLCacheNameEntry();
- entry->mIsGroup = false;
- entry->mCreateTime = ctime;
- entry->mFirstName = agent[FIRST].asString();
- entry->mLastName = agent[LAST].asString();
- impl.mCache[id] = entry;
- std::string fullname = buildFullName(entry->mFirstName, entry->mLastName);
- impl.mReverseCache[fullname] = id;
-
- ++count;
- }
- LL_INFOS() << "LLCacheName loaded " << count << " agent names" << LL_ENDL;
-
- count = 0;
- LLSD groups = data[GROUPS];
- iter = groups.beginMap();
- end = groups.endMap();
- for( ; iter != end; ++iter)
- {
- LLUUID id((*iter).first);
- LLSD group = (*iter).second;
- U32 ctime = (U32)group[CTIME].asInteger();
- if(ctime < delete_before_time) continue;
-
- LLCacheNameEntry* entry = new LLCacheNameEntry();
- entry->mIsGroup = true;
- entry->mCreateTime = ctime;
- entry->mGroupName = group[NAME].asString();
- impl.mCache[id] = entry;
- impl.mReverseCache[entry->mGroupName] = id;
- ++count;
- }
- LL_INFOS() << "LLCacheName loaded " << count << " group names" << LL_ENDL;
- return true;
-}
-
-void LLCacheName::exportFile(std::ostream& ostr)
-{
- LLSD data;
- Cache::iterator iter = impl.mCache.begin();
- Cache::iterator end = impl.mCache.end();
- for( ; iter != end; ++iter)
- {
- // Only write entries for which we have valid data.
- LLCacheNameEntry* entry = iter->second;
- if(!entry
- || (std::string::npos != entry->mFirstName.find('?'))
- || (std::string::npos != entry->mGroupName.find('?')))
- {
- continue;
- }
-
- // store it
- LLUUID id = iter->first;
- std::string id_str = id.asString();
- // IDEVO TODO: Should we store SLIDs with last name "Resident" or not?
- if(!entry->mFirstName.empty() && !entry->mLastName.empty())
- {
- data[AGENTS][id_str][FIRST] = entry->mFirstName;
- data[AGENTS][id_str][LAST] = entry->mLastName;
- data[AGENTS][id_str][CTIME] = (S32)entry->mCreateTime;
- }
- else if(entry->mIsGroup && !entry->mGroupName.empty())
- {
- data[GROUPS][id_str][NAME] = entry->mGroupName;
- data[GROUPS][id_str][CTIME] = (S32)entry->mCreateTime;
- }
- }
-
- LLSDSerialize::toPrettyXML(data, ostr);
-}
-
-
-bool LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::string& last)
-{
- if(id.isNull())
- {
- first = sCacheName["nobody"];
- last.clear();
- return true;
- }
-
- LLCacheNameEntry* entry = get_ptr_in_map(mCache, id );
- if (entry)
- {
- first = entry->mFirstName;
- last = entry->mLastName;
- return true;
- }
- else
- {
- first = sCacheName["waiting"];
- last.clear();
- if (!isRequestPending(id))
- {
- mAskNameQueue.insert(id);
- }
- return false;
- }
-
-}
-
-// static
-void LLCacheName::localizeCacheName(std::string key, std::string value)
-{
- if (key!="" && value!= "" )
- sCacheName[key]=value;
- else
- LL_WARNS()<< " Error localizing cache key " << key << " To "<< value<<LL_ENDL;
-}
-
-bool LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
-{
- std::string first_name, last_name;
- bool res = impl.getName(id, first_name, last_name);
- fullname = buildFullName(first_name, last_name);
- return res;
-}
-
-
-
-bool LLCacheName::getGroupName(const LLUUID& id, std::string& group)
-{
- if(id.isNull())
- {
- group = sCacheName["none"];
- return true;
- }
-
- LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id);
- if (entry && entry->mGroupName.empty())
- {
- // 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
- LL_DEBUGS() << "LLCacheName queuing HACK group request: " << id << LL_ENDL;
- entry = NULL;
- }
-
- if (entry)
- {
- group = entry->mGroupName;
- return true;
- }
- else
- {
- group = sCacheName["waiting"];
- if (!impl.isRequestPending(id))
- {
- impl.mAskGroupQueue.insert(id);
- }
- return false;
- }
-}
-
-bool LLCacheName::getUUID(const std::string& first, const std::string& last, LLUUID& id)
-{
- std::string full_name = buildFullName(first, last);
- return getUUID(full_name, id);
-}
-
-bool LLCacheName::getUUID(const std::string& full_name, LLUUID& id)
-{
- ReverseCache::iterator iter = impl.mReverseCache.find(full_name);
- if (iter != impl.mReverseCache.end())
- {
- id = iter->second;
- return true;
- }
- else
- {
- return false;
- }
-}
-
-//static
-std::string LLCacheName::buildFullName(const std::string& first, const std::string& last)
-{
- std::string fullname = first;
- if (!last.empty()
- && last != "Resident")
- {
- fullname += ' ';
- fullname += last;
- }
- return fullname;
-}
-
-//static
-std::string LLCacheName::cleanFullName(const std::string& full_name)
-{
- return full_name.substr(0, full_name.find(" Resident"));
-}
-
-//static
-// Transform hard-coded name provided by server to a more legible username
-std::string LLCacheName::buildUsername(const std::string& full_name)
-{
- // rare, but handle hard-coded error names returned from server
- if (full_name == "(\?\?\?) (\?\?\?)")
- {
- return "(\?\?\?)";
- }
-
- std::string::size_type index = full_name.find(' ');
-
- if (index != std::string::npos)
- {
- std::string username;
- username = full_name.substr(0, index);
- std::string lastname = full_name.substr(index+1);
-
- if (lastname != "Resident")
- {
- username = username + "." + lastname;
- }
-
- LLStringUtil::toLower(username);
- return username;
- }
-
- // if the input wasn't a correctly formatted legacy name, just return it
- // cleaned up from a potential terminal "Resident"
- std::string clean_name = cleanFullName(full_name);
- LLStringUtil::toLower(clean_name);
- return clean_name;
-}
-
-//static
-std::string LLCacheName::buildLegacyName(const std::string& complete_name)
-{
- //boost::regexp was showing up in the crashreporter, so doing
- //painfully manual parsing using substr. LF
- S32 open_paren = complete_name.rfind(" (");
- S32 close_paren = complete_name.rfind(')');
-
- if (open_paren != std::string::npos &&
- close_paren == complete_name.length()-1)
- {
- S32 length = close_paren - open_paren - 2;
- std::string legacy_name = complete_name.substr(open_paren+2, length);
-
- if (legacy_name.length() > 0)
- {
- std::string cap_letter = legacy_name.substr(0, 1);
- LLStringUtil::toUpper(cap_letter);
- legacy_name = cap_letter + legacy_name.substr(1);
-
- S32 separator = legacy_name.find('.');
-
- if (separator != std::string::npos)
- {
- std::string last_name = legacy_name.substr(separator+1);
- legacy_name = legacy_name.substr(0, separator);
-
- if (last_name.length() > 0)
- {
- cap_letter = last_name.substr(0, 1);
- LLStringUtil::toUpper(cap_letter);
- legacy_name = legacy_name + " " + cap_letter + last_name.substr(1);
- }
- }
-
- return legacy_name;
- }
- }
-
- return complete_name;
-}
-
-// This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer.
-// The reason it is a slot is so that the legacy get() function below can bind an old callback
-// and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior
-// doesn't get lost. As a result, we have to bind the slot to a signal to call it, even when
-// we call it immediately. -Steve
-// NOTE: Even though passing first and last name is a bit of extra overhead, it eliminates the
-// potential need for any parsing should any code need to handle first and last name independently.
-boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback)
-{
- boost::signals2::connection res;
-
- if(id.isNull())
- {
- LLCacheNameSignal signal;
- signal.connect(callback);
- signal(id, sCacheName["nobody"], is_group);
- return res;
- }
-
- LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
- if (entry)
- {
- LLCacheNameSignal signal;
- signal.connect(callback);
- // id found in map therefore we can call the callback immediately.
- if (entry->mIsGroup)
- {
- signal(id, entry->mGroupName, entry->mIsGroup);
- }
- else
- {
- std::string fullname =
- buildFullName(entry->mFirstName, entry->mLastName);
- signal(id, fullname, entry->mIsGroup);
- }
- }
- else
- {
- // id not found in map so we must queue the callback call until available.
- if (!impl.isRequestPending(id))
- {
- if (is_group)
- {
- impl.mAskGroupQueue.insert(id);
- }
- else
- {
- impl.mAskNameQueue.insert(id);
- }
- }
- res = impl.addPending(id, callback);
- }
- return res;
-}
-
-boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id,
- const LLCacheNameCallback& callback)
-{
- return get(group_id, true, callback);
-}
-
-boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data)
-{
- return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data));
-}
-
-void LLCacheName::processPending()
-{
- const F32 SECS_BETWEEN_PROCESS = 0.1f;
- if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
- {
- return;
- }
-
- if(!impl.mUpstreamHost.isOk())
- {
- LL_DEBUGS() << "LLCacheName::processPending() - bad upstream host."
- << LL_ENDL;
- 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);
- }
- }
-
- // These are pending requests that we never heard back from.
- U32 pending_expire_time = now - PENDING_TIMEOUT_SECS;
- for(PendingQueue::iterator p_iter = impl.mPendingQueue.begin();
- p_iter != impl.mPendingQueue.end(); )
- {
- PendingQueue::iterator p_curitor = p_iter++;
-
- if (p_curitor->second < pending_expire_time)
- {
- impl.mPendingQueue.erase(p_curitor);
- }
- }
-}
-
-
-void LLCacheName::dump()
-{
- for (Cache::iterator iter = impl.mCache.begin(),
- end = impl.mCache.end();
- iter != end; iter++)
- {
- LLCacheNameEntry* entry = iter->second;
- if (entry->mIsGroup)
- {
- LL_INFOS()
- << iter->first << " = (group) "
- << entry->mGroupName
- << " @ " << entry->mCreateTime
- << LL_ENDL;
- }
- else
- {
- LL_INFOS()
- << iter->first << " = "
- << buildFullName(entry->mFirstName, entry->mLastName)
- << " @ " << entry->mCreateTime
- << LL_ENDL;
- }
- }
-}
-
-void LLCacheName::dumpStats()
-{
- LL_INFOS() << "Queue sizes: "
- << " Cache=" << impl.mCache.size()
- << " AskName=" << impl.mAskNameQueue.size()
- << " AskGroup=" << impl.mAskGroupQueue.size()
- << " Pending=" << impl.mPendingQueue.size()
- << " Reply=" << impl.mReplyQueue.size()
-// << " Observers=" << impl.mSignal.size()
- << LL_ENDL;
-}
-
-void LLCacheName::clear()
-{
- for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer());
- impl.mCache.clear();
-}
-
-//static
-std::string LLCacheName::getDefaultName()
-{
- return sCacheName["waiting"];
-}
-
-//static
-std::string LLCacheName::getDefaultLastName()
-{
- return "Resident";
-}
-
-void LLCacheName::Impl::processPendingAsks()
-{
- sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
- sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
- mAskNameQueue.clear();
- mAskGroupQueue.clear();
-}
-
-void LLCacheName::Impl::processPendingReplies()
-{
- // First call all the callbacks, because they might send messages.
- for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
- {
- PendingReply* reply = *it;
- LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
- if(!entry) continue;
-
- if (!entry->mIsGroup)
- {
- std::string fullname =
- LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
- (reply->mSignal)(reply->mID, fullname, false);
- }
- else
- {
- (reply->mSignal)(reply->mID, entry->mGroupName, true);
- }
- }
-
- // Forward on all replies, if needed.
- ReplySender sender(mMsg);
- for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
- {
- PendingReply* reply = *it;
- LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
- if(!entry) continue;
-
- if (reply->mHost.isOk())
- {
- sender.send(reply->mID, *entry, reply->mHost);
- }
-
- reply->done();
- }
-
- for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); )
- {
- ReplyQueue::iterator curit = it++;
- PendingReply* reply = *curit;
- if (reply->isDone())
- {
- delete reply;
- mReplyQueue.erase(curit);
- }
- }
-}
-
-
-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);
- }
-}
-
-bool LLCacheName::Impl::isRequestPending(const LLUUID& id)
-{
- U32 now = (U32)time(NULL);
- U32 expire_time = now - PENDING_TIMEOUT_SECS;
-
- PendingQueue::iterator iter = mPendingQueue.find(id);
-
- if (iter == mPendingQueue.end()
- || (iter->second < expire_time) )
- {
- mPendingQueue[id] = now;
- return false;
- }
-
- return true;
-}
-
-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())
- {
- LL_WARNS() << "LLCacheName - got UUID name/group request, but no upstream provider!" << LL_ENDL;
- 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)
- {
- LL_WARNS() << "LLCacheName - Asked for "
- << (isGroup ? "group" : "user") << " name, "
- << "but found "
- << (entry->mIsGroup ? "group" : "user")
- << ": " << id << LL_ENDL;
- }
- else
- {
- // ...it's in the cache, so send it as the reply
- sender.send(id, *entry, fromHost);
- }
- }
- else
- {
- if (!isRequestPending(id))
- {
- if (isGroup)
- {
- mAskGroupQueue.insert(id);
- }
- else
- {
- mAskNameQueue.insert(id);
- }
- }
-
- addPending(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;
- }
-
- mPendingQueue.erase(id);
-
- entry->mIsGroup = isGroup;
- entry->mCreateTime = (U32)time(NULL);
- if (!isGroup)
- {
- msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, entry->mFirstName, i);
- msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName, entry->mLastName, i);
- }
- else
- { // is group
- msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, entry->mGroupName, i);
- LLStringFn::replace_ascii_controlchars(entry->mGroupName, LL_UNKNOWN_CHAR);
- }
-
- if (!isGroup)
- {
- // NOTE: Very occasionally the server sends down a full name
- // in the first name field with an empty last name, for example,
- // first = "Ladanie1 Resident", last = "".
- // I cannot reproduce this, nor can I find a bug in the server code.
- // Ensure "Resident" does not appear via cleanFullName, because
- // buildFullName only checks last name. JC
- std::string full_name;
- if (entry->mLastName.empty())
- {
- full_name = cleanFullName(entry->mFirstName);
-
- //fix what we are putting in the cache
- entry->mFirstName = full_name;
- entry->mLastName = "Resident";
- }
- else
- {
- full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
- }
- mSignal(id, full_name, false);
- mReverseCache[full_name] = id;
- }
- else
- {
- mSignal(id, entry->mGroupName, true);
- mReverseCache[entry->mGroupName] = id;
- }
- }
-}
-
-
-
-// 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);
-}
+/** + * @file llcachename.cpp + * @brief A hierarchical cache of first and last names queried based on UUID. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llcachename.h" + +// linden library includes +#include "lldbstrings.h" +#include "llframetimer.h" +#include "llhost.h" +#include "llrand.h" +#include "llsdserialize.h" +#include "lluuid.h" +#include "message.h" + +#include <boost/regex.hpp> + +// llsd serialization constants +static const std::string AGENTS("agents"); +static const std::string GROUPS("groups"); +static const std::string CTIME("ctime"); +static const std::string FIRST("first"); +static const std::string LAST("last"); +static const std::string NAME("name"); + +// We track name requests in flight for up to this long. +// We won't re-request a name during this time +const U32 PENDING_TIMEOUT_SECS = 5 * 60; + +// Globals +LLCacheName* gCacheName = NULL; +std::map<std::string, std::string> LLCacheName::sCacheName; + +/// --------------------------------------------------------------------------- +/// class LLCacheNameEntry +/// --------------------------------------------------------------------------- + +class LLCacheNameEntry +{ +public: + LLCacheNameEntry(); + +public: + bool mIsGroup; + U32 mCreateTime; // unix time_t + // IDEVO TODO collapse names to one field, which will eliminate + // many string compares on "Resident" + std::string mFirstName; + std::string mLastName; + std::string mGroupName; +}; + +LLCacheNameEntry::LLCacheNameEntry() + : mIsGroup(false), + mCreateTime(0) +{ +} + + +class PendingReply +{ +public: + LLUUID mID; + LLCacheNameSignal mSignal; + LLHost mHost; + + PendingReply(const LLUUID& id, const LLHost& host) + : mID(id), mHost(host) + { + } + + boost::signals2::connection setCallback(const LLCacheNameCallback& cb) + { + return mSignal.connect(cb); + } + + void done() { mID.setNull(); } + bool isDone() const { return mID.isNull(); } +}; + +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), mCurrIsGroup(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::set<LLUUID> AskQueue; +typedef std::list<PendingReply*> ReplyQueue; +typedef std::map<LLUUID,U32> PendingQueue; +typedef std::map<LLUUID, LLCacheNameEntry*> Cache; +typedef std::map<std::string, LLUUID> ReverseCache; + +class LLCacheName::Impl +{ +public: + LLMessageSystem* mMsg; + LLHost mUpstreamHost; + + Cache mCache; + // the map of UUIDs to names + ReverseCache mReverseCache; + // map of names to UUIDs + + AskQueue mAskNameQueue; + AskQueue mAskGroupQueue; + // UUIDs to ask our upstream host about + + PendingQueue mPendingQueue; + // UUIDs that have been requested but are not in cache yet. + + ReplyQueue mReplyQueue; + // requests awaiting replies from us + + LLCacheNameSignal mSignal; + + LLFrameTimer mProcessTimer; + + Impl(LLMessageSystem* msg); + ~Impl(); + + bool getName(const LLUUID& id, std::string& first, std::string& last); + + boost::signals2::connection addPending(const LLUUID& id, const LLCacheNameCallback& callback); + void addPending(const LLUUID& id, const LLHost& host); + + void processPendingAsks(); + void processPendingReplies(); + void sendRequest(const char* msg_name, const AskQueue& queue); + bool isRequestPending(const LLUUID& id); + + // 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); +}; + + +/// -------------------------------------------------------------------------- +/// class LLCacheName +/// --------------------------------------------------------------------------- + +LLCacheName::LLCacheName(LLMessageSystem* msg) + : impl(* new Impl(msg)) + { } + +LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host) + : impl(* new Impl(msg)) +{ + sCacheName["waiting"] = "(Loading...)"; + sCacheName["nobody"] = "(nobody)"; + sCacheName["none"] = "(none)"; + setUpstream(upstream_host); +} + +LLCacheName::~LLCacheName() +{ + delete &impl; +} + +LLCacheName::Impl::Impl(LLMessageSystem* msg) + : mMsg(msg), mUpstreamHost(LLHost()) +{ + 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()); + mCache.clear(); + for_each(mReplyQueue.begin(), mReplyQueue.end(), DeletePointer()); + mReplyQueue.clear(); +} + +boost::signals2::connection LLCacheName::Impl::addPending(const LLUUID& id, const LLCacheNameCallback& callback) +{ + PendingReply* reply = new PendingReply(id, LLHost()); + boost::signals2::connection res = reply->setCallback(callback); + mReplyQueue.push_back(reply); + return res; +} + +void LLCacheName::Impl::addPending(const LLUUID& id, const LLHost& host) +{ + PendingReply* reply = new PendingReply(id, host); + mReplyQueue.push_back(reply); +} + +void LLCacheName::setUpstream(const LLHost& upstream_host) +{ + impl.mUpstreamHost = upstream_host; +} + +boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& callback) +{ + return impl.mSignal.connect(callback); +} + +bool LLCacheName::importFile(std::istream& istr) +{ + LLSD data; + if(LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(data, istr)) + { + return false; + } + + // 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); + + // iterate over the agents + S32 count = 0; + LLSD agents = data[AGENTS]; + LLSD::map_iterator iter = agents.beginMap(); + LLSD::map_iterator end = agents.endMap(); + for( ; iter != end; ++iter) + { + LLUUID id((*iter).first); + LLSD agent = (*iter).second; + U32 ctime = (U32)agent[CTIME].asInteger(); + if(ctime < delete_before_time) continue; + + LLCacheNameEntry* entry = new LLCacheNameEntry(); + entry->mIsGroup = false; + entry->mCreateTime = ctime; + entry->mFirstName = agent[FIRST].asString(); + entry->mLastName = agent[LAST].asString(); + impl.mCache[id] = entry; + std::string fullname = buildFullName(entry->mFirstName, entry->mLastName); + impl.mReverseCache[fullname] = id; + + ++count; + } + LL_INFOS() << "LLCacheName loaded " << count << " agent names" << LL_ENDL; + + count = 0; + LLSD groups = data[GROUPS]; + iter = groups.beginMap(); + end = groups.endMap(); + for( ; iter != end; ++iter) + { + LLUUID id((*iter).first); + LLSD group = (*iter).second; + U32 ctime = (U32)group[CTIME].asInteger(); + if(ctime < delete_before_time) continue; + + LLCacheNameEntry* entry = new LLCacheNameEntry(); + entry->mIsGroup = true; + entry->mCreateTime = ctime; + entry->mGroupName = group[NAME].asString(); + impl.mCache[id] = entry; + impl.mReverseCache[entry->mGroupName] = id; + ++count; + } + LL_INFOS() << "LLCacheName loaded " << count << " group names" << LL_ENDL; + return true; +} + +void LLCacheName::exportFile(std::ostream& ostr) +{ + LLSD data; + Cache::iterator iter = impl.mCache.begin(); + Cache::iterator end = impl.mCache.end(); + for( ; iter != end; ++iter) + { + // Only write entries for which we have valid data. + LLCacheNameEntry* entry = iter->second; + if(!entry + || (std::string::npos != entry->mFirstName.find('?')) + || (std::string::npos != entry->mGroupName.find('?'))) + { + continue; + } + + // store it + LLUUID id = iter->first; + std::string id_str = id.asString(); + // IDEVO TODO: Should we store SLIDs with last name "Resident" or not? + if(!entry->mFirstName.empty() && !entry->mLastName.empty()) + { + data[AGENTS][id_str][FIRST] = entry->mFirstName; + data[AGENTS][id_str][LAST] = entry->mLastName; + data[AGENTS][id_str][CTIME] = (S32)entry->mCreateTime; + } + else if(entry->mIsGroup && !entry->mGroupName.empty()) + { + data[GROUPS][id_str][NAME] = entry->mGroupName; + data[GROUPS][id_str][CTIME] = (S32)entry->mCreateTime; + } + } + + LLSDSerialize::toPrettyXML(data, ostr); +} + + +bool LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::string& last) +{ + if(id.isNull()) + { + first = sCacheName["nobody"]; + last.clear(); + return true; + } + + LLCacheNameEntry* entry = get_ptr_in_map(mCache, id ); + if (entry) + { + first = entry->mFirstName; + last = entry->mLastName; + return true; + } + else + { + first = sCacheName["waiting"]; + last.clear(); + if (!isRequestPending(id)) + { + mAskNameQueue.insert(id); + } + return false; + } + +} + +// static +void LLCacheName::localizeCacheName(std::string key, std::string value) +{ + if (key!="" && value!= "" ) + sCacheName[key]=value; + else + LL_WARNS()<< " Error localizing cache key " << key << " To "<< value<<LL_ENDL; +} + +bool LLCacheName::getFullName(const LLUUID& id, std::string& fullname) +{ + std::string first_name, last_name; + bool res = impl.getName(id, first_name, last_name); + fullname = buildFullName(first_name, last_name); + return res; +} + + + +bool LLCacheName::getGroupName(const LLUUID& id, std::string& group) +{ + if(id.isNull()) + { + group = sCacheName["none"]; + return true; + } + + LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id); + if (entry && entry->mGroupName.empty()) + { + // 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 + LL_DEBUGS() << "LLCacheName queuing HACK group request: " << id << LL_ENDL; + entry = NULL; + } + + if (entry) + { + group = entry->mGroupName; + return true; + } + else + { + group = sCacheName["waiting"]; + if (!impl.isRequestPending(id)) + { + impl.mAskGroupQueue.insert(id); + } + return false; + } +} + +bool LLCacheName::getUUID(const std::string& first, const std::string& last, LLUUID& id) +{ + std::string full_name = buildFullName(first, last); + return getUUID(full_name, id); +} + +bool LLCacheName::getUUID(const std::string& full_name, LLUUID& id) +{ + ReverseCache::iterator iter = impl.mReverseCache.find(full_name); + if (iter != impl.mReverseCache.end()) + { + id = iter->second; + return true; + } + else + { + return false; + } +} + +//static +std::string LLCacheName::buildFullName(const std::string& first, const std::string& last) +{ + std::string fullname = first; + if (!last.empty() + && last != "Resident") + { + fullname += ' '; + fullname += last; + } + return fullname; +} + +//static +std::string LLCacheName::cleanFullName(const std::string& full_name) +{ + return full_name.substr(0, full_name.find(" Resident")); +} + +//static +// Transform hard-coded name provided by server to a more legible username +std::string LLCacheName::buildUsername(const std::string& full_name) +{ + // rare, but handle hard-coded error names returned from server + if (full_name == "(\?\?\?) (\?\?\?)") + { + return "(\?\?\?)"; + } + + std::string::size_type index = full_name.find(' '); + + if (index != std::string::npos) + { + std::string username; + username = full_name.substr(0, index); + std::string lastname = full_name.substr(index+1); + + if (lastname != "Resident") + { + username = username + "." + lastname; + } + + LLStringUtil::toLower(username); + return username; + } + + // if the input wasn't a correctly formatted legacy name, just return it + // cleaned up from a potential terminal "Resident" + std::string clean_name = cleanFullName(full_name); + LLStringUtil::toLower(clean_name); + return clean_name; +} + +//static +std::string LLCacheName::buildLegacyName(const std::string& complete_name) +{ + //boost::regexp was showing up in the crashreporter, so doing + //painfully manual parsing using substr. LF + S32 open_paren = complete_name.rfind(" ("); + S32 close_paren = complete_name.rfind(')'); + + if (open_paren != std::string::npos && + close_paren == complete_name.length()-1) + { + S32 length = close_paren - open_paren - 2; + std::string legacy_name = complete_name.substr(open_paren+2, length); + + if (legacy_name.length() > 0) + { + std::string cap_letter = legacy_name.substr(0, 1); + LLStringUtil::toUpper(cap_letter); + legacy_name = cap_letter + legacy_name.substr(1); + + S32 separator = legacy_name.find('.'); + + if (separator != std::string::npos) + { + std::string last_name = legacy_name.substr(separator+1); + legacy_name = legacy_name.substr(0, separator); + + if (last_name.length() > 0) + { + cap_letter = last_name.substr(0, 1); + LLStringUtil::toUpper(cap_letter); + legacy_name = legacy_name + " " + cap_letter + last_name.substr(1); + } + } + + return legacy_name; + } + } + + return complete_name; +} + +// This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer. +// The reason it is a slot is so that the legacy get() function below can bind an old callback +// and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior +// doesn't get lost. As a result, we have to bind the slot to a signal to call it, even when +// we call it immediately. -Steve +// NOTE: Even though passing first and last name is a bit of extra overhead, it eliminates the +// potential need for any parsing should any code need to handle first and last name independently. +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback) +{ + boost::signals2::connection res; + + if(id.isNull()) + { + LLCacheNameSignal signal; + signal.connect(callback); + signal(id, sCacheName["nobody"], is_group); + return res; + } + + LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id ); + if (entry) + { + LLCacheNameSignal signal; + signal.connect(callback); + // id found in map therefore we can call the callback immediately. + if (entry->mIsGroup) + { + signal(id, entry->mGroupName, entry->mIsGroup); + } + else + { + std::string fullname = + buildFullName(entry->mFirstName, entry->mLastName); + signal(id, fullname, entry->mIsGroup); + } + } + else + { + // id not found in map so we must queue the callback call until available. + if (!impl.isRequestPending(id)) + { + if (is_group) + { + impl.mAskGroupQueue.insert(id); + } + else + { + impl.mAskNameQueue.insert(id); + } + } + res = impl.addPending(id, callback); + } + return res; +} + +boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id, + const LLCacheNameCallback& callback) +{ + return get(group_id, true, callback); +} + +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data) +{ + return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data)); +} + +void LLCacheName::processPending() +{ + const F32 SECS_BETWEEN_PROCESS = 0.1f; + if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS)) + { + return; + } + + if(!impl.mUpstreamHost.isOk()) + { + LL_DEBUGS() << "LLCacheName::processPending() - bad upstream host." + << LL_ENDL; + 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); + } + } + + // These are pending requests that we never heard back from. + U32 pending_expire_time = now - PENDING_TIMEOUT_SECS; + for(PendingQueue::iterator p_iter = impl.mPendingQueue.begin(); + p_iter != impl.mPendingQueue.end(); ) + { + PendingQueue::iterator p_curitor = p_iter++; + + if (p_curitor->second < pending_expire_time) + { + impl.mPendingQueue.erase(p_curitor); + } + } +} + + +void LLCacheName::dump() +{ + for (Cache::iterator iter = impl.mCache.begin(), + end = impl.mCache.end(); + iter != end; iter++) + { + LLCacheNameEntry* entry = iter->second; + if (entry->mIsGroup) + { + LL_INFOS() + << iter->first << " = (group) " + << entry->mGroupName + << " @ " << entry->mCreateTime + << LL_ENDL; + } + else + { + LL_INFOS() + << iter->first << " = " + << buildFullName(entry->mFirstName, entry->mLastName) + << " @ " << entry->mCreateTime + << LL_ENDL; + } + } +} + +void LLCacheName::dumpStats() +{ + LL_INFOS() << "Queue sizes: " + << " Cache=" << impl.mCache.size() + << " AskName=" << impl.mAskNameQueue.size() + << " AskGroup=" << impl.mAskGroupQueue.size() + << " Pending=" << impl.mPendingQueue.size() + << " Reply=" << impl.mReplyQueue.size() +// << " Observers=" << impl.mSignal.size() + << LL_ENDL; +} + +void LLCacheName::clear() +{ + for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer()); + impl.mCache.clear(); +} + +//static +std::string LLCacheName::getDefaultName() +{ + return sCacheName["waiting"]; +} + +//static +std::string LLCacheName::getDefaultLastName() +{ + return "Resident"; +} + +void LLCacheName::Impl::processPendingAsks() +{ + sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue); + sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue); + mAskNameQueue.clear(); + mAskGroupQueue.clear(); +} + +void LLCacheName::Impl::processPendingReplies() +{ + // First call all the callbacks, because they might send messages. + for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it) + { + PendingReply* reply = *it; + LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID); + if(!entry) continue; + + if (!entry->mIsGroup) + { + std::string fullname = + LLCacheName::buildFullName(entry->mFirstName, entry->mLastName); + (reply->mSignal)(reply->mID, fullname, false); + } + else + { + (reply->mSignal)(reply->mID, entry->mGroupName, true); + } + } + + // Forward on all replies, if needed. + ReplySender sender(mMsg); + for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it) + { + PendingReply* reply = *it; + LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID); + if(!entry) continue; + + if (reply->mHost.isOk()) + { + sender.send(reply->mID, *entry, reply->mHost); + } + + reply->done(); + } + + for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ) + { + ReplyQueue::iterator curit = it++; + PendingReply* reply = *curit; + if (reply->isDone()) + { + delete reply; + mReplyQueue.erase(curit); + } + } +} + + +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); + } +} + +bool LLCacheName::Impl::isRequestPending(const LLUUID& id) +{ + U32 now = (U32)time(NULL); + U32 expire_time = now - PENDING_TIMEOUT_SECS; + + PendingQueue::iterator iter = mPendingQueue.find(id); + + if (iter == mPendingQueue.end() + || (iter->second < expire_time) ) + { + mPendingQueue[id] = now; + return false; + } + + return true; +} + +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()) + { + LL_WARNS() << "LLCacheName - got UUID name/group request, but no upstream provider!" << LL_ENDL; + 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) + { + LL_WARNS() << "LLCacheName - Asked for " + << (isGroup ? "group" : "user") << " name, " + << "but found " + << (entry->mIsGroup ? "group" : "user") + << ": " << id << LL_ENDL; + } + else + { + // ...it's in the cache, so send it as the reply + sender.send(id, *entry, fromHost); + } + } + else + { + if (!isRequestPending(id)) + { + if (isGroup) + { + mAskGroupQueue.insert(id); + } + else + { + mAskNameQueue.insert(id); + } + } + + addPending(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; + } + + mPendingQueue.erase(id); + + entry->mIsGroup = isGroup; + entry->mCreateTime = (U32)time(NULL); + if (!isGroup) + { + msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, entry->mFirstName, i); + msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName, entry->mLastName, i); + } + else + { // is group + msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, entry->mGroupName, i); + LLStringFn::replace_ascii_controlchars(entry->mGroupName, LL_UNKNOWN_CHAR); + } + + if (!isGroup) + { + // NOTE: Very occasionally the server sends down a full name + // in the first name field with an empty last name, for example, + // first = "Ladanie1 Resident", last = "". + // I cannot reproduce this, nor can I find a bug in the server code. + // Ensure "Resident" does not appear via cleanFullName, because + // buildFullName only checks last name. JC + std::string full_name; + if (entry->mLastName.empty()) + { + full_name = cleanFullName(entry->mFirstName); + + //fix what we are putting in the cache + entry->mFirstName = full_name; + entry->mLastName = "Resident"; + } + else + { + full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName); + } + mSignal(id, full_name, false); + mReverseCache[full_name] = id; + } + else + { + mSignal(id, entry->mGroupName, true); + mReverseCache[entry->mGroupName] = id; + } + } +} + + + +// 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 index 958f91d3c0..1df713c7c7 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -1,148 +1,148 @@ -/**
- * @file llcachename.h
- * @brief A cache of names from UUIDs.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLCACHENAME_H
-#define LL_LLCACHENAME_H
-
-#include <boost/bind.hpp>
-#include <boost/signals2.hpp>
-
-class LLMessageSystem;
-class LLHost;
-class LLUUID;
-
-
-typedef boost::signals2::signal<void (const LLUUID& id,
- const std::string& name,
- bool is_group)> LLCacheNameSignal;
-typedef LLCacheNameSignal::slot_type LLCacheNameCallback;
-
-// Old callback with user data for compatibility
-typedef void (*old_callback_t)(const LLUUID&, const std::string&, 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);
-
- boost::signals2::connection addObserver(const LLCacheNameCallback& callback);
-
- // storing cache on disk; for viewer, in name.cache
- bool importFile(std::istream& istr);
- void exportFile(std::ostream& ostr);
-
- // If available, copies name ("bobsmith123" or "James Linden") into string
- // If not available, copies the string "waiting".
- // Returns true if available.
- bool getFullName(const LLUUID& id, std::string& full_name);
-
- // Reverse lookup of UUID from name
- bool getUUID(const std::string& first, const std::string& last, LLUUID& id);
- bool getUUID(const std::string& fullname, LLUUID& id);
-
- // IDEVO Temporary code
- // Clean up new-style "bobsmith123 Resident" names to "bobsmith123" for display
- static std::string buildFullName(const std::string& first, const std::string& last);
-
- // Clean up legacy "bobsmith123 Resident" to "bobsmith123"
- // If name does not contain "Resident" returns it unchanged.
- static std::string cleanFullName(const std::string& full_name);
-
- // Converts a standard legacy name to a username
- // "bobsmith123 Resident" -> "bobsmith"
- // "Random Linden" -> "random.linden"
- static std::string buildUsername(const std::string& name);
-
- // Converts a complete display name to a legacy name
- // if possible, otherwise returns the input
- // "Alias (random.linden)" -> "Random Linden"
- // "Something random" -> "Something random"
- static std::string buildLegacyName(const std::string& name);
-
- // 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 if available.
- bool getGroupName(const LLUUID& id, std::string& 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.
- boost::signals2::connection get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback);
-
- // Convenience method for looking up a group name, so you can
- // tell the difference between avatar lookup and group lookup
- // in global searches
- boost::signals2::connection getGroup(const LLUUID& group_id, const LLCacheNameCallback& callback);
-
- // LEGACY
- boost::signals2::connection get(const LLUUID& id, bool is_group, old_callback_t callback, void* 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(); // Dumps the contents of the cache
- void dumpStats(); // Dumps the sizes of the cache and associated queues.
- void clear(); // Deletes all entries from the cache
-
- static std::string getDefaultName();
-
- // Returns "Resident", the default last name for SLID-based accounts
- // that have no last name.
- static std::string getDefaultLastName();
-
- static void localizeCacheName(std::string key, std::string value);
- static std::map<std::string, std::string> sCacheName;
-private:
-
- class Impl;
- Impl& impl;
-};
-
-
-
-extern LLCacheName* gCacheName;
-
-#endif
+/** + * @file llcachename.h + * @brief A cache of names from UUIDs. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCACHENAME_H +#define LL_LLCACHENAME_H + +#include <boost/bind.hpp> +#include <boost/signals2.hpp> + +class LLMessageSystem; +class LLHost; +class LLUUID; + + +typedef boost::signals2::signal<void (const LLUUID& id, + const std::string& name, + bool is_group)> LLCacheNameSignal; +typedef LLCacheNameSignal::slot_type LLCacheNameCallback; + +// Old callback with user data for compatibility +typedef void (*old_callback_t)(const LLUUID&, const std::string&, 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); + + boost::signals2::connection addObserver(const LLCacheNameCallback& callback); + + // storing cache on disk; for viewer, in name.cache + bool importFile(std::istream& istr); + void exportFile(std::ostream& ostr); + + // If available, copies name ("bobsmith123" or "James Linden") into string + // If not available, copies the string "waiting". + // Returns true if available. + bool getFullName(const LLUUID& id, std::string& full_name); + + // Reverse lookup of UUID from name + bool getUUID(const std::string& first, const std::string& last, LLUUID& id); + bool getUUID(const std::string& fullname, LLUUID& id); + + // IDEVO Temporary code + // Clean up new-style "bobsmith123 Resident" names to "bobsmith123" for display + static std::string buildFullName(const std::string& first, const std::string& last); + + // Clean up legacy "bobsmith123 Resident" to "bobsmith123" + // If name does not contain "Resident" returns it unchanged. + static std::string cleanFullName(const std::string& full_name); + + // Converts a standard legacy name to a username + // "bobsmith123 Resident" -> "bobsmith" + // "Random Linden" -> "random.linden" + static std::string buildUsername(const std::string& name); + + // Converts a complete display name to a legacy name + // if possible, otherwise returns the input + // "Alias (random.linden)" -> "Random Linden" + // "Something random" -> "Something random" + static std::string buildLegacyName(const std::string& name); + + // 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 if available. + bool getGroupName(const LLUUID& id, std::string& 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. + boost::signals2::connection get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback); + + // Convenience method for looking up a group name, so you can + // tell the difference between avatar lookup and group lookup + // in global searches + boost::signals2::connection getGroup(const LLUUID& group_id, const LLCacheNameCallback& callback); + + // LEGACY + boost::signals2::connection get(const LLUUID& id, bool is_group, old_callback_t callback, void* 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(); // Dumps the contents of the cache + void dumpStats(); // Dumps the sizes of the cache and associated queues. + void clear(); // Deletes all entries from the cache + + static std::string getDefaultName(); + + // Returns "Resident", the default last name for SLID-based accounts + // that have no last name. + static std::string getDefaultLastName(); + + static void localizeCacheName(std::string key, std::string value); + static std::map<std::string, std::string> sCacheName; +private: + + class Impl; + Impl& impl; +}; + + + +extern LLCacheName* gCacheName; + +#endif diff --git a/indra/llmessage/llcipher.h b/indra/llmessage/llcipher.h index cbd5d5bde1..bbd7eae1d5 100644 --- a/indra/llmessage/llcipher.h +++ b/indra/llmessage/llcipher.h @@ -1,56 +1,56 @@ -/**
- * @file llcipher.h
- * @brief Abstract base class for encryption ciphers.
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LLCIPHER_H
-#define LLCIPHER_H
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLCipher
-//
-// Abstract base class for a cipher object.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLCipher
-{
-public:
- virtual ~LLCipher() {}
-
- // encrypt src and place result into dst. returns true if
- // Returns number of bytes written into dst, or 0 on error.
- virtual U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0;
-
- // decrypt src and place result into dst.
- // Returns number of bytes written into dst, or 0 on error.
- virtual U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0;
-
- // returns the minimum amount of space required to encrypt for a
- // unencrypted source buffer of length len.
- // *NOTE: This is estimated space and you should check the return value
- // of the encrypt function.
- virtual U32 requiredEncryptionSpace(U32 src_len) const = 0 ;
-};
-
-#endif
+/** + * @file llcipher.h + * @brief Abstract base class for encryption ciphers. + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCIPHER_H +#define LLCIPHER_H + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLCipher +// +// Abstract base class for a cipher object. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLCipher +{ +public: + virtual ~LLCipher() {} + + // encrypt src and place result into dst. returns true if + // Returns number of bytes written into dst, or 0 on error. + virtual U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0; + + // decrypt src and place result into dst. + // Returns number of bytes written into dst, or 0 on error. + virtual U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0; + + // returns the minimum amount of space required to encrypt for a + // unencrypted source buffer of length len. + // *NOTE: This is estimated space and you should check the return value + // of the encrypt function. + virtual U32 requiredEncryptionSpace(U32 src_len) const = 0 ; +}; + +#endif diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp index bd9b3553fe..fa206d9282 100644 --- a/indra/llmessage/llcircuit.cpp +++ b/indra/llmessage/llcircuit.cpp @@ -1,1419 +1,1419 @@ -/**
- * @file llcircuit.cpp
- * @brief Class to track UDP endpoints for the message system.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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"
-#include "llmodularmath.h"
-
-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 F32Seconds TARGET_PERIOD_LENGTH(5.f);
-const F32Seconds LL_DUPLICATE_SUPPRESSION_TIMEOUT(60.f); //this can be long, as time-based cleanup is
- // only done when wrapping packetids, now...
-
-LLCircuitData::LLCircuitData(const LLHost &host, TPACKETID in_id,
- const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout)
-: mHost (host),
- mWrapID(0),
- mPacketsOutID(0),
- mPacketsInID(in_id),
- mHighestPacketID(in_id),
- mTimeoutCallback(NULL),
- mTimeoutUserData(NULL),
- 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(INITIAL_PING_VALUE_MSEC),
- mUnackedPacketCount(0),
- mUnackedPacketBytes(0),
- mLastPacketInTime(0.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.f),
- mPeakBPSOut(0.f),
- mPeriodTime(0.0),
- mExistenceTimer(),
- mAckCreationTime(0.f),
- mCurrentResendCount(0),
- mLastPacketGap(0),
- mHeartbeatInterval(circuit_heartbeat_interval),
- mHeartbeatTimeout(circuit_timeout)
-{
- // 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.
- F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds(true);
- F32 distribution_offset = ll_frand();
-
- mPingTime = mt_sec;
- mLastPingSendTime = mt_sec + mHeartbeatInterval * distribution_offset;
- mLastPingReceivedTime = mt_sec;
- mNextPingSendTime = mLastPingSendTime + 0.95*mHeartbeatInterval + F32Seconds(ll_frand(0.1f*mHeartbeatInterval.value()));
- mPeriodTime = mt_sec;
-
- 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);
- LL_INFOS() << str.str() << LL_ENDL;
- }
-}
-
-
-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;
- LL_INFOS() << str.str() << LL_ENDL;
- }
- if (packetp->mCallback)
- {
- if (packetp->mTimeout < F32Seconds(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;
- // LL_INFOS() << "Packet " << packet_num << " removed from the pending list" << LL_ENDL;
- if(gMessageSystem->mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: <- " << packetp->mHost << "\tRELIABLE ACKED:\t"
- << packetp->mPacketID;
- LL_INFOS() << str.str() << LL_ENDL;
- }
- if (packetp->mCallback)
- {
- if (packetp->mTimeout < F32Seconds(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 F64Seconds now)
-{
- 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.
- LL_WARNS() << mHost << " has " << mUnackedPacketBytes
- << " bytes of reliable messages waiting" << LL_ENDL;
- }
- // 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;
- LL_INFOS() << str.str() << LL_ENDL;
- }
-
- 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, F32Seconds(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;
- }
- }
- 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)
- //LL_INFOS() << "Packet " << packetp->mPacketID << " removed from the pending list: exceeded retry limit" << LL_ENDL;
- //if (packetp->mMessageName)
- //{
- // LL_INFOS() << "Packet name " << packetp->mMessageName << LL_ENDL;
- //}
- gMessageSystem->mFailedResendPackets++;
-
- if(gMessageSystem->mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: -> " << packetp->mHost << "\tABORTING RELIABLE:\t"
- << packetp->mPacketID;
- LL_INFOS() << str.str() << LL_ENDL;
- }
-
- 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(const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout)
-: mLastCircuit(NULL),
- mHeartbeatInterval(circuit_heartbeat_interval),
- mHeartbeatTimeout(circuit_timeout)
-{}
-
-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
- LL_INFOS() << "LLCircuit::addCircuitData for " << host << LL_ENDL;
- LLCircuitData *tempp = new LLCircuitData(host, in_id, mHeartbeatInterval, mHeartbeatTimeout);
- mCircuitData.insert(circuit_data_map::value_type(host, tempp));
- mPingSet.insert(tempp);
-
- mLastCircuit = tempp;
- return tempp;
-}
-
-void LLCircuit::removeCircuitData(const LLHost &host)
-{
- LL_INFOS() << "LLCircuit::removeCircuitData for " << host << LL_ENDL;
- 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
- {
- LL_WARNS() << "Couldn't find entry for next ping in ping set!" << LL_ENDL;
- }
-
- // 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()
-{
- F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds();
- F64Seconds period_length = mt_sec - mPeriodTime;
- if ( period_length > TARGET_PERIOD_LENGTH)
- {
- F32 bps_in = F32Bits(mBytesInThisPeriod).value() / period_length.value();
- if (bps_in > mPeakBPSIn)
- {
- mPeakBPSIn = bps_in;
- }
-
- F32 bps_out = F32Bits(mBytesOutThisPeriod).value() / period_length.value();
- if (bps_out > mPeakBPSOut)
- {
- mPeakBPSOut = bps_out;
- }
-
- mBytesInLastPeriod = mBytesInThisPeriod;
- mBytesOutLastPeriod = mBytesOutThisPeriod;
- mBytesInThisPeriod = S32Bytes(0);
- mBytesOutThisPeriod = S32Bytes(0);
- mLastPeriodLength = F32Seconds::convert(period_length);
-
- mPeriodTime = mt_sec;
- }
-}
-
-
-void LLCircuitData::addBytesIn(S32Bytes bytes)
-{
- mBytesIn += bytes;
- mBytesInThisPeriod += bytes;
-}
-
-
-void LLCircuitData::addBytesOut(S32Bytes 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)
-{
- F64Seconds 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);
- }
-
- // Save packet arrival time
- mLastPacketInTime = LLMessageSystem::getMessageTimeSeconds();
-
- // 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);
-
- mLastPacketGap = 0;
- return;
- }
-
- mPacketsIn++;
-
-
- // now, check to see if we've got a gap
- U32 gap = 0;
- 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
-
- // babbage: all operands in expression are unsigned, so modular
- // arithmetic will always find correct gap, regardless of wrap arounds.
- const U8 width = 24;
- gap = LLModularMath::subtract<width>(mPacketsInID, id);
-
- if (mPotentialLostPackets.find(id) != mPotentialLostPackets.end())
- {
- if(gMessageSystem->mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: <- " << mHost << "\tRECOVERING LOST:\t" << id;
- LL_INFOS() << str.str() << LL_ENDL;
- }
- // LL_INFOS() << "removing potential lost: " << id << LL_ENDL;
- mPotentialLostPackets.erase(id);
- }
- else if (!receive_resent) // don't freak out over out-of-order reliable resends
- {
- U64Microseconds 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;
- LL_INFOS() << str.str() << LL_ENDL;
- }
-
-// LL_INFOS() << "adding potential lost: " << index << LL_ENDL;
- mPotentialLostPackets[index] = time;
- index++;
- index = index % LL_MAX_OUT_PACKET_ID;
- gap_count++;
- }
- }
- else
- {
- LL_INFOS() << "packet_out_of_order - got packet " << id << " expecting " << index << " from " << mHost << LL_ENDL;
- if(gMessageSystem->mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: <- " << mHost << "\tPACKET GAP:\t"
- << id << " expected " << index;
- LL_INFOS() << str.str() << LL_ENDL;
- }
- }
-
- mPacketsInID = id + 1;
- mPacketsInID = (mPacketsInID) % LL_MAX_OUT_PACKET_ID;
-
- if (gap_count > 128)
- {
- LL_WARNS() << "Packet loss gap filler running amok!" << LL_ENDL;
- }
- else if (gap_count > 16)
- {
- LL_WARNS() << "Sustaining large amounts of packet loss!" << LL_ENDL;
- }
-
- }
- }
- mLastPacketGap = gap;
-}
-
-
-void LLCircuit::updateWatchDogTimers(LLMessageSystem *msgsys)
-{
- F64Seconds 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 + mHeartbeatInterval;
- 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
- F64Seconds dt = 0.95f*mHeartbeatInterval + F32Seconds(ll_frand(0.1f*mHeartbeatInterval.value()));
-
- // 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)
-{
- F64Seconds 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;
- }
-
- //LL_INFOS() << mHost << " - unacked count " << mUnackedPackets.size() << LL_ENDL;
- //LL_INFOS() << mHost << " - final count " << mFinalRetryPackets.size() << LL_ENDL;
- 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;
- //LL_INFOS() << mHost << ": nowrapped unacked" << LL_ENDL;
- }
- else
- {
- packet_id = iter_final->first;
- //LL_INFOS() << mHost << ": nowrapped final" << LL_ENDL;
- }
- }
- 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.
- //LL_INFOS() << mHost << ": No unacked!" << LL_ENDL;
- 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;
- //LL_INFOS() << mHost << ": Unacked" << LL_ENDL;
- }
-
- 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);
- //LL_INFOS() << mHost << ": Min of unacked/final" << LL_ENDL;
- }
- else
- {
- // Only the final had a packet, use it.
- packet_id = iter_final->first;
- //LL_INFOS() << mHost << ": Final!" << LL_ENDL;
- }
- }
- }
- }
-
- // 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;
- U64Microseconds timeout = llmin(LL_MAX_LOST_TIMEOUT, F32Seconds(getPingDelayAveraged()) * LL_LOST_TIMEOUT_FACTOR);
-
- U64Microseconds mt_usec = LLMessageSystem::getMessageTimeUsecs();
- for (it = mPotentialLostPackets.begin(); it != mPotentialLostPackets.end(); )
- {
- U64Microseconds 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;
- LL_INFOS() << str.str() << LL_ENDL;
- }
- mPotentialLostPackets.erase(it++);
- }
- 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.
-
- //LL_INFOS() << mHost << ": clearing before oldest " << oldest_id << LL_ENDL;
- //LL_INFOS() << "Recent list before: " << mRecentlyReceivedReliablePackets.size() << LL_ENDL;
- 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.
- U64Microseconds 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)
- {
- LL_WARNS() << "Probably incorrectly timing out non-wrapped packets!" << LL_ENDL;
- }
- U64Microseconds delta_t_usec = mt_usec - (*pit).second;
- F64Seconds delta_t_sec = delta_t_usec;
- if (delta_t_sec > LL_DUPLICATE_SUPPRESSION_TIMEOUT)
- {
- // enough time has elapsed we're not likely to get a duplicate on this one
- LL_INFOS() << "Clearing " << pit->first << " from recent list" << LL_ENDL;
- mRecentlyReceivedReliablePackets.erase(pit++);
- }
- else
- {
- ++pit;
- }
- }
- //LL_INFOS() << "Recent list after: " << mRecentlyReceivedReliablePackets.size() << LL_ENDL;
-}
-
-bool LLCircuitData::checkCircuitTimeout()
-{
- F64Seconds 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 > mHeartbeatTimeout)
- {
- LL_WARNS() << "LLCircuitData::checkCircuitTimeout for " << mHost << " last ping " << time_since_last_ping << " seconds ago." <<LL_ENDL;
- setAlive(false);
- if (mTimeoutCallback)
- {
- LL_WARNS() << "LLCircuitData::checkCircuitTimeout for " << mHost << " calling callback." << LL_ENDL;
- mTimeoutCallback(mHost, mTimeoutUserData);
- }
- if (!isAlive())
- {
- // The callback didn't try and resurrect the circuit. We should kill it.
- LL_WARNS() << "LLCircuitData::checkCircuitTimeout for " << mHost << " still dead, dropping." << LL_ENDL;
- return false;
- }
- }
-
- 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);
- if (mAckCreationTime == 0)
- {
- mAckCreationTime = getAgeInSeconds();
- }
- 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(F32 collect_time)
-{
- collect_time = llclamp(collect_time, 0.f, LL_COLLECT_ACK_TIME_MAX);
- LLCircuitData* cd;
- circuit_data_map::iterator it = mSendAckMap.begin();
- while (it != mSendAckMap.end())
- {
- circuit_data_map::iterator cur_it = it++;
- cd = (*cur_it).second;
- S32 count = (S32)cd->mAcks.size();
- F32 age = cd->getAgeInSeconds() - cd->mAckCreationTime;
- if (age > collect_time || count == 0)
- {
- 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);
- LL_INFOS() << str.str() << LL_ENDL;
- }
-
- // empty out the acks list
- cd->mAcks.clear();
- cd->mAckCreationTime = 0.f;
- }
- // remove data map
- mSendAckMap.erase(cur_it);
- }
- }
-}
-
-
-std::ostream& operator<<(std::ostream& s, LLCircuitData& circuit)
-{
- F32 age = circuit.mExistenceTimer.getElapsedTimeF32();
-
- using namespace std;
- s << "Circuit " << circuit.mHost << " "
- << circuit.mRemoteID << " "
- << (circuit.mbAlive ? "Alive" : "Not Alive") << " "
- << (circuit.mbAllowTimeout ? "Timeout Allowed" : "Timeout Not Allowed")
- << endl;
-
- s << " Packets Lost: " << circuit.mPacketsLost
- << " Measured Ping: " << circuit.mPingDelay
- << " Averaged Ping: " << circuit.mPingDelayAveraged
- << endl;
-
- s << "Global In/Out " << S32(age) << " sec"
- << " KBytes: " << circuit.mBytesIn.valueInUnits<LLUnits::Kilobytes>() << "/" << circuit.mBytesOut.valueInUnits<LLUnits::Kilobytes>()
- << " Kbps: "
- << S32(circuit.mBytesIn.valueInUnits<LLUnits::Kilobits>() / circuit.mExistenceTimer.getElapsedTimeF32().value())
- << "/"
- << S32(circuit.mBytesOut.valueInUnits<LLUnits::Kilobits>() / circuit.mExistenceTimer.getElapsedTimeF32().value())
- << " Packets: " << circuit.mPacketsIn << "/" << circuit.mPacketsOut
- << endl;
-
- s << "Recent In/Out " << circuit.mLastPeriodLength
- << " KBytes: "
- << circuit.mBytesInLastPeriod.valueInUnits<LLUnits::Kilobytes>()
- << "/"
- << circuit.mBytesOutLastPeriod.valueInUnits<LLUnits::Kilobytes>()
- << " Kbps: "
- << (S32)(circuit.mBytesInLastPeriod.valueInUnits<LLUnits::Kilobits>() / circuit.mLastPeriodLength.value())
- << "/"
- << (S32)(circuit.mBytesOutLastPeriod.valueInUnits<LLUnits::Kilobits>() / circuit.mLastPeriodLength.value())
- << " Peak kbps: "
- << S32(circuit.mPeakBPSIn / 1024.f)
- << "/"
- << S32(circuit.mPeakBPSOut / 1024.f)
- << endl;
-
- return s;
-}
-
-void LLCircuitData::getInfo(LLSD& info) const
-{
- info["Host"] = mHost.getIPandPort();
- info["Alive"] = mbAlive;
- info["Age"] = mExistenceTimer.getElapsedTimeF32();
-}
-
-void LLCircuitData::dumpResendCountAndReset()
-{
- if (mCurrentResendCount)
- {
- LL_INFOS() << "Circuit: " << mHost << " resent " << mCurrentResendCount << " packets" << LL_ENDL;
- 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;
-}
-
-void LLCircuit::getInfo(LLSD& info) const
-{
- LLCircuit::circuit_data_map::const_iterator end = mCircuitData.end();
- LLCircuit::circuit_data_map::const_iterator it;
- LLSD circuit_info;
- for(it = mCircuitData.begin(); it != end; ++it)
- {
- (*it).second->getInfo(circuit_info);
- info["Circuits"].append(circuit_info);
- }
-}
-
-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)
-{
- F64Seconds mt_secs = LLMessageSystem::getMessageTimeSeconds();
-
- // Nota Bene: no averaging of ping times until we get a feel for how this works
- F64Seconds time = mt_secs - mPingTime;
- if (time == F32Seconds(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;
- }
-
- U32Milliseconds msec = delta_ping*mHeartbeatInterval + time;
- 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;
-}
-
-
-S32Bytes LLCircuitData::getBytesIn() const
-{
- return mBytesIn;
-}
-
-
-S32Bytes 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;
-}
-
-
-U32Milliseconds LLCircuitData::getPingDelay() const
-{
- return mPingDelay;
-}
-
-
-F32Milliseconds 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
- F32Milliseconds time_since_ping_was_sent(0);
-
- if (mPingsInTransit)
- {
- time_since_ping_was_sent = F32Milliseconds::convert(((mPingsInTransit*mHeartbeatInterval - F32Seconds(1))
- + (LLMessageSystem::getMessageTimeSeconds() - mPingTime)));
- }
-
- return time_since_ping_was_sent;
-}
-
-
-void LLCircuitData::setPingDelay(U32Milliseconds ping)
-{
- mPingDelay = ping;
- mPingDelayAveraged = llmax((F32Milliseconds)ping, getPingDelayAveraged());
- mPingDelayAveraged = ((1.f - LL_AVERAGED_PING_ALPHA) * mPingDelayAveraged)
- + (LL_AVERAGED_PING_ALPHA * (F32Milliseconds) ping);
- mPingDelayAveraged = llclamp(mPingDelayAveraged,
- LL_AVERAGED_PING_MIN,
- LL_AVERAGED_PING_MAX);
-}
-
-
-F32Milliseconds 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();
-}
+/** + * @file llcircuit.cpp + * @brief Class to track UDP endpoints for the message system. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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" +#include "llmodularmath.h" + +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 F32Seconds TARGET_PERIOD_LENGTH(5.f); +const F32Seconds LL_DUPLICATE_SUPPRESSION_TIMEOUT(60.f); //this can be long, as time-based cleanup is + // only done when wrapping packetids, now... + +LLCircuitData::LLCircuitData(const LLHost &host, TPACKETID in_id, + const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout) +: mHost (host), + mWrapID(0), + mPacketsOutID(0), + mPacketsInID(in_id), + mHighestPacketID(in_id), + mTimeoutCallback(NULL), + mTimeoutUserData(NULL), + 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(INITIAL_PING_VALUE_MSEC), + mUnackedPacketCount(0), + mUnackedPacketBytes(0), + mLastPacketInTime(0.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.f), + mPeakBPSOut(0.f), + mPeriodTime(0.0), + mExistenceTimer(), + mAckCreationTime(0.f), + mCurrentResendCount(0), + mLastPacketGap(0), + mHeartbeatInterval(circuit_heartbeat_interval), + mHeartbeatTimeout(circuit_timeout) +{ + // 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. + F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds(true); + F32 distribution_offset = ll_frand(); + + mPingTime = mt_sec; + mLastPingSendTime = mt_sec + mHeartbeatInterval * distribution_offset; + mLastPingReceivedTime = mt_sec; + mNextPingSendTime = mLastPingSendTime + 0.95*mHeartbeatInterval + F32Seconds(ll_frand(0.1f*mHeartbeatInterval.value())); + mPeriodTime = mt_sec; + + 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); + LL_INFOS() << str.str() << LL_ENDL; + } +} + + +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; + LL_INFOS() << str.str() << LL_ENDL; + } + if (packetp->mCallback) + { + if (packetp->mTimeout < F32Seconds(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; + // LL_INFOS() << "Packet " << packet_num << " removed from the pending list" << LL_ENDL; + if(gMessageSystem->mVerboseLog) + { + std::ostringstream str; + str << "MSG: <- " << packetp->mHost << "\tRELIABLE ACKED:\t" + << packetp->mPacketID; + LL_INFOS() << str.str() << LL_ENDL; + } + if (packetp->mCallback) + { + if (packetp->mTimeout < F32Seconds(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 F64Seconds now) +{ + 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. + LL_WARNS() << mHost << " has " << mUnackedPacketBytes + << " bytes of reliable messages waiting" << LL_ENDL; + } + // 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; + LL_INFOS() << str.str() << LL_ENDL; + } + + 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, F32Seconds(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; + } + } + 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) + //LL_INFOS() << "Packet " << packetp->mPacketID << " removed from the pending list: exceeded retry limit" << LL_ENDL; + //if (packetp->mMessageName) + //{ + // LL_INFOS() << "Packet name " << packetp->mMessageName << LL_ENDL; + //} + gMessageSystem->mFailedResendPackets++; + + if(gMessageSystem->mVerboseLog) + { + std::ostringstream str; + str << "MSG: -> " << packetp->mHost << "\tABORTING RELIABLE:\t" + << packetp->mPacketID; + LL_INFOS() << str.str() << LL_ENDL; + } + + 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(const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout) +: mLastCircuit(NULL), + mHeartbeatInterval(circuit_heartbeat_interval), + mHeartbeatTimeout(circuit_timeout) +{} + +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 + LL_INFOS() << "LLCircuit::addCircuitData for " << host << LL_ENDL; + LLCircuitData *tempp = new LLCircuitData(host, in_id, mHeartbeatInterval, mHeartbeatTimeout); + mCircuitData.insert(circuit_data_map::value_type(host, tempp)); + mPingSet.insert(tempp); + + mLastCircuit = tempp; + return tempp; +} + +void LLCircuit::removeCircuitData(const LLHost &host) +{ + LL_INFOS() << "LLCircuit::removeCircuitData for " << host << LL_ENDL; + 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 + { + LL_WARNS() << "Couldn't find entry for next ping in ping set!" << LL_ENDL; + } + + // 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() +{ + F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds(); + F64Seconds period_length = mt_sec - mPeriodTime; + if ( period_length > TARGET_PERIOD_LENGTH) + { + F32 bps_in = F32Bits(mBytesInThisPeriod).value() / period_length.value(); + if (bps_in > mPeakBPSIn) + { + mPeakBPSIn = bps_in; + } + + F32 bps_out = F32Bits(mBytesOutThisPeriod).value() / period_length.value(); + if (bps_out > mPeakBPSOut) + { + mPeakBPSOut = bps_out; + } + + mBytesInLastPeriod = mBytesInThisPeriod; + mBytesOutLastPeriod = mBytesOutThisPeriod; + mBytesInThisPeriod = S32Bytes(0); + mBytesOutThisPeriod = S32Bytes(0); + mLastPeriodLength = F32Seconds::convert(period_length); + + mPeriodTime = mt_sec; + } +} + + +void LLCircuitData::addBytesIn(S32Bytes bytes) +{ + mBytesIn += bytes; + mBytesInThisPeriod += bytes; +} + + +void LLCircuitData::addBytesOut(S32Bytes 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) +{ + F64Seconds 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); + } + + // Save packet arrival time + mLastPacketInTime = LLMessageSystem::getMessageTimeSeconds(); + + // 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); + + mLastPacketGap = 0; + return; + } + + mPacketsIn++; + + + // now, check to see if we've got a gap + U32 gap = 0; + 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 + + // babbage: all operands in expression are unsigned, so modular + // arithmetic will always find correct gap, regardless of wrap arounds. + const U8 width = 24; + gap = LLModularMath::subtract<width>(mPacketsInID, id); + + if (mPotentialLostPackets.find(id) != mPotentialLostPackets.end()) + { + if(gMessageSystem->mVerboseLog) + { + std::ostringstream str; + str << "MSG: <- " << mHost << "\tRECOVERING LOST:\t" << id; + LL_INFOS() << str.str() << LL_ENDL; + } + // LL_INFOS() << "removing potential lost: " << id << LL_ENDL; + mPotentialLostPackets.erase(id); + } + else if (!receive_resent) // don't freak out over out-of-order reliable resends + { + U64Microseconds 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; + LL_INFOS() << str.str() << LL_ENDL; + } + +// LL_INFOS() << "adding potential lost: " << index << LL_ENDL; + mPotentialLostPackets[index] = time; + index++; + index = index % LL_MAX_OUT_PACKET_ID; + gap_count++; + } + } + else + { + LL_INFOS() << "packet_out_of_order - got packet " << id << " expecting " << index << " from " << mHost << LL_ENDL; + if(gMessageSystem->mVerboseLog) + { + std::ostringstream str; + str << "MSG: <- " << mHost << "\tPACKET GAP:\t" + << id << " expected " << index; + LL_INFOS() << str.str() << LL_ENDL; + } + } + + mPacketsInID = id + 1; + mPacketsInID = (mPacketsInID) % LL_MAX_OUT_PACKET_ID; + + if (gap_count > 128) + { + LL_WARNS() << "Packet loss gap filler running amok!" << LL_ENDL; + } + else if (gap_count > 16) + { + LL_WARNS() << "Sustaining large amounts of packet loss!" << LL_ENDL; + } + + } + } + mLastPacketGap = gap; +} + + +void LLCircuit::updateWatchDogTimers(LLMessageSystem *msgsys) +{ + F64Seconds 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 + mHeartbeatInterval; + 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 + F64Seconds dt = 0.95f*mHeartbeatInterval + F32Seconds(ll_frand(0.1f*mHeartbeatInterval.value())); + + // 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) +{ + F64Seconds 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; + } + + //LL_INFOS() << mHost << " - unacked count " << mUnackedPackets.size() << LL_ENDL; + //LL_INFOS() << mHost << " - final count " << mFinalRetryPackets.size() << LL_ENDL; + 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; + //LL_INFOS() << mHost << ": nowrapped unacked" << LL_ENDL; + } + else + { + packet_id = iter_final->first; + //LL_INFOS() << mHost << ": nowrapped final" << LL_ENDL; + } + } + 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. + //LL_INFOS() << mHost << ": No unacked!" << LL_ENDL; + 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; + //LL_INFOS() << mHost << ": Unacked" << LL_ENDL; + } + + 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); + //LL_INFOS() << mHost << ": Min of unacked/final" << LL_ENDL; + } + else + { + // Only the final had a packet, use it. + packet_id = iter_final->first; + //LL_INFOS() << mHost << ": Final!" << LL_ENDL; + } + } + } + } + + // 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; + U64Microseconds timeout = llmin(LL_MAX_LOST_TIMEOUT, F32Seconds(getPingDelayAveraged()) * LL_LOST_TIMEOUT_FACTOR); + + U64Microseconds mt_usec = LLMessageSystem::getMessageTimeUsecs(); + for (it = mPotentialLostPackets.begin(); it != mPotentialLostPackets.end(); ) + { + U64Microseconds 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; + LL_INFOS() << str.str() << LL_ENDL; + } + mPotentialLostPackets.erase(it++); + } + 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. + + //LL_INFOS() << mHost << ": clearing before oldest " << oldest_id << LL_ENDL; + //LL_INFOS() << "Recent list before: " << mRecentlyReceivedReliablePackets.size() << LL_ENDL; + 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. + U64Microseconds 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) + { + LL_WARNS() << "Probably incorrectly timing out non-wrapped packets!" << LL_ENDL; + } + U64Microseconds delta_t_usec = mt_usec - (*pit).second; + F64Seconds delta_t_sec = delta_t_usec; + if (delta_t_sec > LL_DUPLICATE_SUPPRESSION_TIMEOUT) + { + // enough time has elapsed we're not likely to get a duplicate on this one + LL_INFOS() << "Clearing " << pit->first << " from recent list" << LL_ENDL; + mRecentlyReceivedReliablePackets.erase(pit++); + } + else + { + ++pit; + } + } + //LL_INFOS() << "Recent list after: " << mRecentlyReceivedReliablePackets.size() << LL_ENDL; +} + +bool LLCircuitData::checkCircuitTimeout() +{ + F64Seconds 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 > mHeartbeatTimeout) + { + LL_WARNS() << "LLCircuitData::checkCircuitTimeout for " << mHost << " last ping " << time_since_last_ping << " seconds ago." <<LL_ENDL; + setAlive(false); + if (mTimeoutCallback) + { + LL_WARNS() << "LLCircuitData::checkCircuitTimeout for " << mHost << " calling callback." << LL_ENDL; + mTimeoutCallback(mHost, mTimeoutUserData); + } + if (!isAlive()) + { + // The callback didn't try and resurrect the circuit. We should kill it. + LL_WARNS() << "LLCircuitData::checkCircuitTimeout for " << mHost << " still dead, dropping." << LL_ENDL; + return false; + } + } + + 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); + if (mAckCreationTime == 0) + { + mAckCreationTime = getAgeInSeconds(); + } + 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(F32 collect_time) +{ + collect_time = llclamp(collect_time, 0.f, LL_COLLECT_ACK_TIME_MAX); + LLCircuitData* cd; + circuit_data_map::iterator it = mSendAckMap.begin(); + while (it != mSendAckMap.end()) + { + circuit_data_map::iterator cur_it = it++; + cd = (*cur_it).second; + S32 count = (S32)cd->mAcks.size(); + F32 age = cd->getAgeInSeconds() - cd->mAckCreationTime; + if (age > collect_time || count == 0) + { + 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); + LL_INFOS() << str.str() << LL_ENDL; + } + + // empty out the acks list + cd->mAcks.clear(); + cd->mAckCreationTime = 0.f; + } + // remove data map + mSendAckMap.erase(cur_it); + } + } +} + + +std::ostream& operator<<(std::ostream& s, LLCircuitData& circuit) +{ + F32 age = circuit.mExistenceTimer.getElapsedTimeF32(); + + using namespace std; + s << "Circuit " << circuit.mHost << " " + << circuit.mRemoteID << " " + << (circuit.mbAlive ? "Alive" : "Not Alive") << " " + << (circuit.mbAllowTimeout ? "Timeout Allowed" : "Timeout Not Allowed") + << endl; + + s << " Packets Lost: " << circuit.mPacketsLost + << " Measured Ping: " << circuit.mPingDelay + << " Averaged Ping: " << circuit.mPingDelayAveraged + << endl; + + s << "Global In/Out " << S32(age) << " sec" + << " KBytes: " << circuit.mBytesIn.valueInUnits<LLUnits::Kilobytes>() << "/" << circuit.mBytesOut.valueInUnits<LLUnits::Kilobytes>() + << " Kbps: " + << S32(circuit.mBytesIn.valueInUnits<LLUnits::Kilobits>() / circuit.mExistenceTimer.getElapsedTimeF32().value()) + << "/" + << S32(circuit.mBytesOut.valueInUnits<LLUnits::Kilobits>() / circuit.mExistenceTimer.getElapsedTimeF32().value()) + << " Packets: " << circuit.mPacketsIn << "/" << circuit.mPacketsOut + << endl; + + s << "Recent In/Out " << circuit.mLastPeriodLength + << " KBytes: " + << circuit.mBytesInLastPeriod.valueInUnits<LLUnits::Kilobytes>() + << "/" + << circuit.mBytesOutLastPeriod.valueInUnits<LLUnits::Kilobytes>() + << " Kbps: " + << (S32)(circuit.mBytesInLastPeriod.valueInUnits<LLUnits::Kilobits>() / circuit.mLastPeriodLength.value()) + << "/" + << (S32)(circuit.mBytesOutLastPeriod.valueInUnits<LLUnits::Kilobits>() / circuit.mLastPeriodLength.value()) + << " Peak kbps: " + << S32(circuit.mPeakBPSIn / 1024.f) + << "/" + << S32(circuit.mPeakBPSOut / 1024.f) + << endl; + + return s; +} + +void LLCircuitData::getInfo(LLSD& info) const +{ + info["Host"] = mHost.getIPandPort(); + info["Alive"] = mbAlive; + info["Age"] = mExistenceTimer.getElapsedTimeF32(); +} + +void LLCircuitData::dumpResendCountAndReset() +{ + if (mCurrentResendCount) + { + LL_INFOS() << "Circuit: " << mHost << " resent " << mCurrentResendCount << " packets" << LL_ENDL; + 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; +} + +void LLCircuit::getInfo(LLSD& info) const +{ + LLCircuit::circuit_data_map::const_iterator end = mCircuitData.end(); + LLCircuit::circuit_data_map::const_iterator it; + LLSD circuit_info; + for(it = mCircuitData.begin(); it != end; ++it) + { + (*it).second->getInfo(circuit_info); + info["Circuits"].append(circuit_info); + } +} + +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) +{ + F64Seconds mt_secs = LLMessageSystem::getMessageTimeSeconds(); + + // Nota Bene: no averaging of ping times until we get a feel for how this works + F64Seconds time = mt_secs - mPingTime; + if (time == F32Seconds(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; + } + + U32Milliseconds msec = delta_ping*mHeartbeatInterval + time; + 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; +} + + +S32Bytes LLCircuitData::getBytesIn() const +{ + return mBytesIn; +} + + +S32Bytes 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; +} + + +U32Milliseconds LLCircuitData::getPingDelay() const +{ + return mPingDelay; +} + + +F32Milliseconds 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 + F32Milliseconds time_since_ping_was_sent(0); + + if (mPingsInTransit) + { + time_since_ping_was_sent = F32Milliseconds::convert(((mPingsInTransit*mHeartbeatInterval - F32Seconds(1)) + + (LLMessageSystem::getMessageTimeSeconds() - mPingTime))); + } + + return time_since_ping_was_sent; +} + + +void LLCircuitData::setPingDelay(U32Milliseconds ping) +{ + mPingDelay = ping; + mPingDelayAveraged = llmax((F32Milliseconds)ping, getPingDelayAveraged()); + mPingDelayAveraged = ((1.f - LL_AVERAGED_PING_ALPHA) * mPingDelayAveraged) + + (LL_AVERAGED_PING_ALPHA * (F32Milliseconds) ping); + mPingDelayAveraged = llclamp(mPingDelayAveraged, + LL_AVERAGED_PING_MIN, + LL_AVERAGED_PING_MAX); +} + + +F32Milliseconds 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 index ba38849f70..521982d7b1 100644 --- a/indra/llmessage/llcircuit.h +++ b/indra/llmessage/llcircuit.h @@ -1,350 +1,350 @@ -/**
- * @file llcircuit.h
- * @brief Provides a method for tracking network circuit information
- * for the UDP message system
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLCIRCUIT_H
-#define LL_LLCIRCUIT_H
-
-#include <map>
-#include <vector>
-
-#include "llerror.h"
-
-#include "lltimer.h"
-#include "net.h"
-#include "llhost.h"
-#include "llpacketack.h"
-#include "lluuid.h"
-#include "llthrottle.h"
-
-//
-// Constants
-//
-const F32 LL_AVERAGED_PING_ALPHA = 0.2f; // relaxation constant on ping running average
-const F32Milliseconds LL_AVERAGED_PING_MAX(2000);
-const F32Milliseconds LL_AVERAGED_PING_MIN(100); // increased to avoid retransmits when a process is slow
-
-const U32Milliseconds 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 int LL_ERR_CIRCUIT_GONE = -23017;
-const int LL_ERR_TCP_TIMEOUT = -23016;
-
-// 0 - flags
-// [1,4] - packetid
-// 5 - data offset (after message name)
-const U8 LL_PACKET_ID_SIZE = 6;
-
-const S32 LL_MAX_RESENT_PACKETS_PER_FRAME = 100;
-const S32 LL_MAX_ACKED_PACKETS_PER_FRAME = 200;
-const F32 LL_COLLECT_ACK_TIME_MAX = 2.f;
-
-//
-// Prototypes and Predefines
-//
-class LLMessageSystem;
-class LLEncodedDatagramService;
-class LLSD;
-
-//
-// Classes
-//
-
-
-class LLCircuitData
-{
-public:
- LLCircuitData(const LLHost &host, TPACKETID in_id,
- const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout);
- ~LLCircuitData();
-
- S32 resendUnackedPackets(const F64Seconds 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; }
-
- U32Milliseconds getPingDelay() const;
- S32 getPingsInTransit() const { return mPingsInTransit; }
-
- // ACCESSORS
- bool isAlive() const;
- bool isBlocked() const;
- bool getAllowTimeout() const;
- F32Milliseconds getPingDelayAveraged();
- F32Milliseconds getPingInTransitTime();
- U32 getPacketsIn() const;
- S32Bytes getBytesIn() const;
- S32Bytes 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; }
- F64Seconds getNextPingSendTime() const { return mNextPingSendTime; }
- U32 getLastPacketGap() const { return mLastPacketGap; }
- LLHost getHost() const { return mHost; }
- F64Seconds getLastPacketInTime() const { return mLastPacketInTime; }
-
- 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);
- void getInfo(LLSD& info) 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(U32Milliseconds ping);
- bool checkCircuitTimeout(); // Return false if the circuit is dead and should be cleaned up
-
- void addBytesIn(S32Bytes bytes);
- void addBytesOut(S32Bytes 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
- F64Seconds mPingTime; // Time at which a ping was sent.
-
- F64Seconds mLastPingSendTime; // Time we last sent a ping
- F64Seconds mLastPingReceivedTime; // Time we last received a ping
- F64Seconds 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.
- U32Milliseconds mPingDelay; // raw ping delay
- F32Milliseconds mPingDelayAveraged; // averaged ping delay (fast attack/slow decay)
-
- typedef std::map<TPACKETID, U64Microseconds> packet_time_map;
-
- packet_time_map mPotentialLostPackets;
- packet_time_map mRecentlyReceivedReliablePackets;
- std::vector<TPACKETID> mAcks;
- F32 mAckCreationTime; // first ack creation time
-
- typedef std::map<TPACKETID, LLReliablePacket *> reliable_map;
- typedef reliable_map::iterator reliable_iter;
-
- reliable_map mUnackedPackets;
- reliable_map mFinalRetryPackets;
-
- S32 mUnackedPacketCount;
- S32 mUnackedPacketBytes;
-
- F64Seconds mLastPacketInTime; // Time of last packet arrival
-
- 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;
- S32Bytes mBytesIn,
- mBytesOut;
-
- F32Seconds mLastPeriodLength;
- S32Bytes mBytesInLastPeriod;
- S32Bytes mBytesOutLastPeriod;
- S32Bytes mBytesInThisPeriod;
- S32Bytes mBytesOutThisPeriod;
- F32 mPeakBPSIn; // bits per second, max of all period bps
- F32 mPeakBPSOut; // bits per second, max of all period bps
- F64Seconds mPeriodTime;
- LLTimer mExistenceTimer; // initialized when circuit created, used to track bandwidth numbers
-
- S32 mCurrentResendCount; // Number of resent packets since last spam
- U32 mLastPacketGap; // Gap in sequence number of last packet.
-
- const F32Seconds mHeartbeatInterval;
- const F32Seconds mHeartbeatTimeout;
-};
-
-
-// Actually a singleton class -- the global messagesystem
-// has a single LLCircuit member.
-class LLCircuit
-{
-public:
- // CREATORS
- LLCircuit(const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout);
- ~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(F32 collect_time);
-
- friend std::ostream& operator<<(std::ostream& s, LLCircuit &circuit);
- void getInfo(LLSD& info) 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;
-
-private:
- const F32Seconds mHeartbeatInterval;
- const F32Seconds mHeartbeatTimeout;
-};
-#endif
+/** + * @file llcircuit.h + * @brief Provides a method for tracking network circuit information + * for the UDP message system + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCIRCUIT_H +#define LL_LLCIRCUIT_H + +#include <map> +#include <vector> + +#include "llerror.h" + +#include "lltimer.h" +#include "net.h" +#include "llhost.h" +#include "llpacketack.h" +#include "lluuid.h" +#include "llthrottle.h" + +// +// Constants +// +const F32 LL_AVERAGED_PING_ALPHA = 0.2f; // relaxation constant on ping running average +const F32Milliseconds LL_AVERAGED_PING_MAX(2000); +const F32Milliseconds LL_AVERAGED_PING_MIN(100); // increased to avoid retransmits when a process is slow + +const U32Milliseconds 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 int LL_ERR_CIRCUIT_GONE = -23017; +const int LL_ERR_TCP_TIMEOUT = -23016; + +// 0 - flags +// [1,4] - packetid +// 5 - data offset (after message name) +const U8 LL_PACKET_ID_SIZE = 6; + +const S32 LL_MAX_RESENT_PACKETS_PER_FRAME = 100; +const S32 LL_MAX_ACKED_PACKETS_PER_FRAME = 200; +const F32 LL_COLLECT_ACK_TIME_MAX = 2.f; + +// +// Prototypes and Predefines +// +class LLMessageSystem; +class LLEncodedDatagramService; +class LLSD; + +// +// Classes +// + + +class LLCircuitData +{ +public: + LLCircuitData(const LLHost &host, TPACKETID in_id, + const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout); + ~LLCircuitData(); + + S32 resendUnackedPackets(const F64Seconds 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; } + + U32Milliseconds getPingDelay() const; + S32 getPingsInTransit() const { return mPingsInTransit; } + + // ACCESSORS + bool isAlive() const; + bool isBlocked() const; + bool getAllowTimeout() const; + F32Milliseconds getPingDelayAveraged(); + F32Milliseconds getPingInTransitTime(); + U32 getPacketsIn() const; + S32Bytes getBytesIn() const; + S32Bytes 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; } + F64Seconds getNextPingSendTime() const { return mNextPingSendTime; } + U32 getLastPacketGap() const { return mLastPacketGap; } + LLHost getHost() const { return mHost; } + F64Seconds getLastPacketInTime() const { return mLastPacketInTime; } + + 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); + void getInfo(LLSD& info) 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(U32Milliseconds ping); + bool checkCircuitTimeout(); // Return false if the circuit is dead and should be cleaned up + + void addBytesIn(S32Bytes bytes); + void addBytesOut(S32Bytes 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 + F64Seconds mPingTime; // Time at which a ping was sent. + + F64Seconds mLastPingSendTime; // Time we last sent a ping + F64Seconds mLastPingReceivedTime; // Time we last received a ping + F64Seconds 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. + U32Milliseconds mPingDelay; // raw ping delay + F32Milliseconds mPingDelayAveraged; // averaged ping delay (fast attack/slow decay) + + typedef std::map<TPACKETID, U64Microseconds> packet_time_map; + + packet_time_map mPotentialLostPackets; + packet_time_map mRecentlyReceivedReliablePackets; + std::vector<TPACKETID> mAcks; + F32 mAckCreationTime; // first ack creation time + + typedef std::map<TPACKETID, LLReliablePacket *> reliable_map; + typedef reliable_map::iterator reliable_iter; + + reliable_map mUnackedPackets; + reliable_map mFinalRetryPackets; + + S32 mUnackedPacketCount; + S32 mUnackedPacketBytes; + + F64Seconds mLastPacketInTime; // Time of last packet arrival + + 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; + S32Bytes mBytesIn, + mBytesOut; + + F32Seconds mLastPeriodLength; + S32Bytes mBytesInLastPeriod; + S32Bytes mBytesOutLastPeriod; + S32Bytes mBytesInThisPeriod; + S32Bytes mBytesOutThisPeriod; + F32 mPeakBPSIn; // bits per second, max of all period bps + F32 mPeakBPSOut; // bits per second, max of all period bps + F64Seconds mPeriodTime; + LLTimer mExistenceTimer; // initialized when circuit created, used to track bandwidth numbers + + S32 mCurrentResendCount; // Number of resent packets since last spam + U32 mLastPacketGap; // Gap in sequence number of last packet. + + const F32Seconds mHeartbeatInterval; + const F32Seconds mHeartbeatTimeout; +}; + + +// Actually a singleton class -- the global messagesystem +// has a single LLCircuit member. +class LLCircuit +{ +public: + // CREATORS + LLCircuit(const F32Seconds circuit_heartbeat_interval, const F32Seconds circuit_timeout); + ~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(F32 collect_time); + + friend std::ostream& operator<<(std::ostream& s, LLCircuit &circuit); + void getInfo(LLSD& info) 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; + +private: + const F32Seconds mHeartbeatInterval; + const F32Seconds mHeartbeatTimeout; +}; +#endif diff --git a/indra/llmessage/llclassifiedflags.cpp b/indra/llmessage/llclassifiedflags.cpp index 345942955c..0df249d035 100644 --- a/indra/llmessage/llclassifiedflags.cpp +++ b/indra/llmessage/llclassifiedflags.cpp @@ -1,84 +1,84 @@ -/**
- * @file llclassifiedflags.cpp
- * @brief
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-//*****************************************************************************
-// 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_request(bool auto_renew, bool inc_pg, bool inc_mature, bool inc_adult)
-{
- U8 rv = 0;
- if(inc_pg) rv |= CLASSIFIED_QUERY_INC_PG;
- if(inc_mature) rv |= CLASSIFIED_QUERY_INC_MATURE;
- if (inc_pg && !inc_mature) rv |= CLASSIFIED_FLAG_MATURE;
- if(inc_adult) rv |= CLASSIFIED_QUERY_INC_ADULT;
- if(auto_renew) rv |= CLASSIFIED_FLAG_AUTO_RENEW;
- return rv;
-}
-
-ClassifiedFlags pack_classified_flags(bool auto_renew, bool inc_pg, bool inc_mature, bool inc_adult)
-{
- U8 rv = 0;
- if(inc_pg) rv |= CLASSIFIED_QUERY_INC_PG;
- if(inc_mature)
- {
- rv |= CLASSIFIED_QUERY_INC_MATURE;
- rv |= CLASSIFIED_FLAG_MATURE;
- }
- if(inc_adult) rv |= CLASSIFIED_QUERY_INC_ADULT;
- if(auto_renew) rv |= CLASSIFIED_FLAG_AUTO_RENEW;
- return rv;
-}
-
-bool is_cf_mature(ClassifiedFlags flags)
-{
- return ((flags & CLASSIFIED_FLAG_MATURE) != 0) || ((flags & CLASSIFIED_QUERY_INC_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);
-}
+/** + * @file llclassifiedflags.cpp + * @brief + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +//***************************************************************************** +// 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_request(bool auto_renew, bool inc_pg, bool inc_mature, bool inc_adult) +{ + U8 rv = 0; + if(inc_pg) rv |= CLASSIFIED_QUERY_INC_PG; + if(inc_mature) rv |= CLASSIFIED_QUERY_INC_MATURE; + if (inc_pg && !inc_mature) rv |= CLASSIFIED_FLAG_MATURE; + if(inc_adult) rv |= CLASSIFIED_QUERY_INC_ADULT; + if(auto_renew) rv |= CLASSIFIED_FLAG_AUTO_RENEW; + return rv; +} + +ClassifiedFlags pack_classified_flags(bool auto_renew, bool inc_pg, bool inc_mature, bool inc_adult) +{ + U8 rv = 0; + if(inc_pg) rv |= CLASSIFIED_QUERY_INC_PG; + if(inc_mature) + { + rv |= CLASSIFIED_QUERY_INC_MATURE; + rv |= CLASSIFIED_FLAG_MATURE; + } + if(inc_adult) rv |= CLASSIFIED_QUERY_INC_ADULT; + if(auto_renew) rv |= CLASSIFIED_FLAG_AUTO_RENEW; + return rv; +} + +bool is_cf_mature(ClassifiedFlags flags) +{ + return ((flags & CLASSIFIED_FLAG_MATURE) != 0) || ((flags & CLASSIFIED_QUERY_INC_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 index 126b23340f..ee29e4ed13 100644 --- a/indra/llmessage/llclassifiedflags.h +++ b/indra/llmessage/llclassifiedflags.h @@ -1,62 +1,62 @@ -/**
- * @file llclassifiedflags.h
- * @brief Flags used in the classifieds.
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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;
-
-// These are new with Adult-enabled viewers (1.23 and later)
-const U8 CLASSIFIED_QUERY_INC_PG = 1 << 2;
-const U8 CLASSIFIED_QUERY_INC_MATURE = 1 << 3;
-const U8 CLASSIFIED_QUERY_INC_ADULT = 1 << 6;
-const U8 CLASSIFIED_QUERY_INC_NEW_VIEWER = (CLASSIFIED_QUERY_INC_PG | CLASSIFIED_QUERY_INC_MATURE | CLASSIFIED_QUERY_INC_ADULT);
-
-const S32 MAX_CLASSIFIEDS = 100;
-
-// This function is used in AO viewers to pack old query flags into the request
-// so that they can talk to old dataservers properly. When the AO servers are deployed on agni
-// we can revert back to ClassifiedFlags pack_classified_flags and get rider of this one.
-ClassifiedFlags pack_classified_flags_request(bool auto_renew, bool is_pg, bool is_mature, bool is_adult);
-
-ClassifiedFlags pack_classified_flags(bool auto_renew, bool is_pg, bool is_mature, bool is_adult);
-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
+/** + * @file llclassifiedflags.h + * @brief Flags used in the classifieds. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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; + +// These are new with Adult-enabled viewers (1.23 and later) +const U8 CLASSIFIED_QUERY_INC_PG = 1 << 2; +const U8 CLASSIFIED_QUERY_INC_MATURE = 1 << 3; +const U8 CLASSIFIED_QUERY_INC_ADULT = 1 << 6; +const U8 CLASSIFIED_QUERY_INC_NEW_VIEWER = (CLASSIFIED_QUERY_INC_PG | CLASSIFIED_QUERY_INC_MATURE | CLASSIFIED_QUERY_INC_ADULT); + +const S32 MAX_CLASSIFIEDS = 100; + +// This function is used in AO viewers to pack old query flags into the request +// so that they can talk to old dataservers properly. When the AO servers are deployed on agni +// we can revert back to ClassifiedFlags pack_classified_flags and get rider of this one. +ClassifiedFlags pack_classified_flags_request(bool auto_renew, bool is_pg, bool is_mature, bool is_adult); + +ClassifiedFlags pack_classified_flags(bool auto_renew, bool is_pg, bool is_mature, bool is_adult); +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 index e8cd11c0a0..134f34aafa 100644 --- a/indra/llmessage/lldatapacker.cpp +++ b/indra/llmessage/lldatapacker.cpp @@ -1,2180 +1,2180 @@ -/**
- * @file lldatapacker.cpp
- * @brief Data packer implementation.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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)
-{
-}
-
-//virtual
-void LLDataPacker::reset()
-{
- LL_ERRS() << "Using unimplemented datapacker reset!" << LL_ENDL;
-}
-
-//virtual
-void LLDataPacker::dumpBufferToLog()
-{
- LL_ERRS() << "dumpBufferToLog not implemented for this type!" << LL_ENDL;
-}
-
-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
- {
- LL_ERRS() << "Using fixed-point packing of " << total_bits << " bits, why?!" << LL_ENDL;
- }
- 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;
- //LL_INFOS() << "unpackFixed:" << name << " int:" << int_bits << " frac:" << frac_bits << LL_ENDL;
- S32 unsigned_bits = int_bits + frac_bits;
- S32 total_bits = unsigned_bits;
-
- if (is_signed)
- {
- total_bits++;
- }
-
- U32 max_val;
- max_val = 1 << int_bits;
-
- F32 fixed_val;
- if (total_bits <= 8)
- {
- U8 fixed_8;
- success = unpackU8(fixed_8, name);
- fixed_val = (F32)fixed_8;
- }
- else if (total_bits <= 16)
- {
- U16 fixed_16;
- success = unpackU16(fixed_16, name);
- fixed_val = (F32)fixed_16;
- }
- else if (total_bits <= 31)
- {
- U32 fixed_32;
- success = unpackU32(fixed_32, name);
- fixed_val = (F32)fixed_32;
- }
- else
- {
- fixed_val = 0;
- LL_ERRS() << "Bad bit count: " << total_bits << LL_ENDL;
- }
-
- //LL_INFOS() << "Fixed_val:" << fixed_val << LL_ENDL;
-
- fixed_val /= (F32)(1 << frac_bits);
- if (is_signed)
- {
- fixed_val -= max_val;
- }
- value = fixed_val;
- //LL_INFOS() << "Value: " << value << LL_ENDL;
- return success;
-}
-
-bool LLDataPacker::unpackU16s(U16 *values, S32 count, const char *name)
-{
- for (S32 idx = 0; idx < count; ++idx)
- {
- if (!unpackU16(values[idx], name))
- {
- LL_WARNS("DATAPACKER") << "Buffer overflow reading Unsigned 16s \"" << name << "\" at index " << idx << "!" << LL_ENDL;
- return false;
- }
- }
- return true;
-}
-
-bool LLDataPacker::unpackS16s(S16 *values, S32 count, const char *name)
-{
- for (S32 idx = 0; idx < count; ++idx)
- {
- if (!unpackS16(values[idx], name))
- {
- LL_WARNS("DATAPACKER") << "Buffer overflow reading Signed 16s \"" << name << "\" at index " << idx << "!" << LL_ENDL;
- return false;
- }
- }
- return true;
-}
-
-bool LLDataPacker::unpackF32s(F32 *values, S32 count, const char *name)
-{
- for (S32 idx = 0; idx < count; ++idx)
- {
- if (!unpackF32(values[idx], name))
- {
- LL_WARNS("DATAPACKER") << "Buffer overflow reading Float 32s \"" << name << "\" at index " << idx << "!" << LL_ENDL;
- return false;
- }
- }
- return true;
-}
-
-bool LLDataPacker::unpackColor4Us(LLColor4U *values, S32 count, const char *name)
-{
- for (S32 idx = 0; idx < count; ++idx)
- {
- if (!unpackColor4U(values[idx], name))
- {
- LL_WARNS("DATAPACKER") << "Buffer overflow reading Float 32s \"" << name << "\" at index " << idx << "!" << LL_ENDL;
- return false;
- }
- }
- return true;
-}
-
-bool LLDataPacker::unpackUUIDs(LLUUID *values, S32 count, const char *name)
-{
- for (S32 idx = 0; idx < count; ++idx)
- {
- if (!unpackUUID(values[idx], name))
- {
- LL_WARNS("DATAPACKER") << "Buffer overflow reading UUIDs \"" << name << "\" at index " << idx << "!" << LL_ENDL;
- return false;
- }
- }
- return true;
-}
-
-//---------------------------------------------------------------------------
-// LLDataPackerBinaryBuffer implementation
-//---------------------------------------------------------------------------
-
-bool LLDataPackerBinaryBuffer::packString(const std::string& value, const char *name)
-{
- S32 length = value.length()+1;
-
- if (!verifyLength(length, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value.c_str(), MVT_VARIABLE, length);
- }
- mCurBufferp += length;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackString(std::string& value, const char *name)
-{
- S32 length = (S32)strlen((char *)mCurBufferp) + 1; /*Flawfinder: ignore*/
-
- if (!verifyLength(length, name))
- {
- return false;
- }
-
- value = std::string((char*)mCurBufferp); // We already assume NULL termination calling strlen()
-
- mCurBufferp += length;
- return true;
-}
-
-bool LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const char *name)
-{
- if (!verifyLength(size + 4, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, &size, MVT_S32, 4);
- }
- mCurBufferp += 4;
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value, MVT_VARIABLE, size);
- }
- mCurBufferp += size;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name)
-{
- if (!verifyLength(4, name))
- {
- LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << LL_ENDL;
- return false;
- }
-
- htolememcpy(&size, mCurBufferp, MVT_S32, 4);
-
- if (size < 0)
- {
- LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData unpacked invalid size, aborting!" << LL_ENDL;
- return false;
- }
-
- mCurBufferp += 4;
-
- if (!verifyLength(size, name))
- {
- LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << LL_ENDL;
- return false;
- }
-
- htolememcpy(value, mCurBufferp, MVT_VARIABLE, size);
- mCurBufferp += size;
-
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
-{
- if (!verifyLength(size, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value, MVT_VARIABLE, size);
- }
- mCurBufferp += size;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
-{
- if (!verifyLength(size, name))
- {
- return false;
- }
- htolememcpy(value, mCurBufferp, MVT_VARIABLE, size);
- mCurBufferp += size;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packU8(const U8 value, const char *name)
-{
- if (!verifyLength(sizeof(U8), name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- *mCurBufferp = value;
- }
- mCurBufferp++;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackU8(U8 &value, const char *name)
-{
- if (!verifyLength(sizeof(U8), name))
- {
- return false;
- }
-
- value = *mCurBufferp;
- mCurBufferp++;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packU16(const U16 value, const char *name)
-{
- if (!verifyLength(sizeof(U16), name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, &value, MVT_U16, 2);
- }
- mCurBufferp += 2;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackU16(U16 &value, const char *name)
-{
- if (!verifyLength(sizeof(U16), name))
- {
- return false;
- }
-
- htolememcpy(&value, mCurBufferp, MVT_U16, 2);
- mCurBufferp += 2;
- return true;
-}
-
-bool LLDataPackerBinaryBuffer::packS16(const S16 value, const char *name)
-{
- bool success = verifyLength(sizeof(S16), name);
-
- if (mWriteEnabled && success)
- {
- htolememcpy(mCurBufferp, &value, MVT_S16, 2);
- }
- mCurBufferp += 2;
- return success;
-}
-
-bool LLDataPackerBinaryBuffer::unpackS16(S16 &value, const char *name)
-{
- bool success = verifyLength(sizeof(S16), name);
-
- if (success)
- {
- htolememcpy(&value, mCurBufferp, MVT_S16, 2);
- }
- mCurBufferp += 2;
- return success;
-}
-
-bool LLDataPackerBinaryBuffer::packU32(const U32 value, const char *name)
-{
- if (!verifyLength(sizeof(U32), name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, &value, MVT_U32, 4);
- }
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackU32(U32 &value, const char *name)
-{
- if (!verifyLength(sizeof(U32), name))
- {
- return false;
- }
-
- htolememcpy(&value, mCurBufferp, MVT_U32, 4);
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packS32(const S32 value, const char *name)
-{
- if (!verifyLength(sizeof(S32), name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, &value, MVT_S32, 4);
- }
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackS32(S32 &value, const char *name)
-{
- if(!verifyLength(sizeof(S32), name))
- {
- return false;
- }
-
- htolememcpy(&value, mCurBufferp, MVT_S32, 4);
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packF32(const F32 value, const char *name)
-{
- if (!verifyLength(sizeof(F32), name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, &value, MVT_F32, 4);
- }
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackF32(F32 &value, const char *name)
-{
- if (!verifyLength(sizeof(F32), name))
- {
- return false;
- }
-
- htolememcpy(&value, mCurBufferp, MVT_F32, 4);
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packColor4(const LLColor4 &value, const char *name)
-{
- if (!verifyLength(16, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);
- }
- mCurBufferp += 16;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackColor4(LLColor4 &value, const char *name)
-{
- if (!verifyLength(16, name))
- {
- return false;
- }
-
- htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
- mCurBufferp += 16;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packColor4U(const LLColor4U &value, const char *name)
-{
- if (!verifyLength(4, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4);
- }
- mCurBufferp += 4;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackColor4U(LLColor4U &value, const char *name)
-{
- if (!verifyLength(4, name))
- {
- return false;
- }
-
- htolememcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4);
- mCurBufferp += 4;
- return true;
-}
-
-
-
-bool LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *name)
-{
- if (!verifyLength(8, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, &value.mV[0], MVT_F32, 4);
- htolememcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4);
- }
- mCurBufferp += 8;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackVector2(LLVector2 &value, const char *name)
-{
- if (!verifyLength(8, name))
- {
- return false;
- }
-
- htolememcpy(&value.mV[0], mCurBufferp, MVT_F32, 4);
- htolememcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4);
- mCurBufferp += 8;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::packVector3(const LLVector3 &value, const char *name)
-{
- if (!verifyLength(12, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value.mV, MVT_LLVector3, 12);
- }
- mCurBufferp += 12;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackVector3(LLVector3 &value, const char *name)
-{
- if (!verifyLength(12, name))
- {
- return false;
- }
-
- htolememcpy(value.mV, mCurBufferp, MVT_LLVector3, 12);
- mCurBufferp += 12;
- return true;
-}
-
-bool LLDataPackerBinaryBuffer::packVector4(const LLVector4 &value, const char *name)
-{
- if (!verifyLength(16, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);
- }
- mCurBufferp += 16;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackVector4(LLVector4 &value, const char *name)
-{
- if (!verifyLength(16, name))
- {
- return false;
- }
-
- htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
- mCurBufferp += 16;
- return true;
-}
-
-bool LLDataPackerBinaryBuffer::packUUID(const LLUUID &value, const char *name)
-{
- if (!verifyLength(16, name))
- {
- return false;
- }
-
- if (mWriteEnabled)
- {
- htolememcpy(mCurBufferp, value.mData, MVT_LLUUID, 16);
- }
- mCurBufferp += 16;
- return true;
-}
-
-
-bool LLDataPackerBinaryBuffer::unpackUUID(LLUUID &value, const char *name)
-{
- if (!verifyLength(16, name))
- {
- return false;
- }
-
- htolememcpy(value.mData, mCurBufferp, MVT_LLUUID, 16);
- mCurBufferp += 16;
- return true;
-}
-
-const LLDataPackerBinaryBuffer& LLDataPackerBinaryBuffer::operator=(const LLDataPackerBinaryBuffer &a)
-{
- if (a.getBufferSize() > getBufferSize())
- {
- // We've got problems, ack!
- LL_ERRS() << "Trying to do an assignment with not enough room in the target." << LL_ENDL;
- }
- memcpy(mBufferp, a.mBufferp, a.getBufferSize()); /*Flawfinder: ignore*/
- return *this;
-}
-
-void LLDataPackerBinaryBuffer::dumpBufferToLog()
-{
- LL_WARNS() << "Binary Buffer Dump, size: " << mBufferSize << LL_ENDL;
- 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;
- LL_WARNS() << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << LL_ENDL;
- cur_line++;
- }
- }
- if (cur_line_pos)
- {
- LL_WARNS() << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << LL_ENDL;
- }
-}
-
-//---------------------------------------------------------------------------
-// LLDataPackerAsciiBuffer implementation
-//---------------------------------------------------------------------------
-bool LLDataPackerAsciiBuffer::packString(const std::string& value, const char *name)
-{
- bool success = true;
- writeIndentedName(name);
- int numCopied = 0;
- if (mWriteEnabled)
- {
- numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", value.c_str()); /* Flawfinder: ignore */
- }
- else
- {
- numCopied = value.length() + 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 either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- // *NOTE: I believe we need to mark a failure bit at this point.
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packString: string truncated: " << value << LL_ENDL;
- }
- mCurBufferp += numCopied;
- return success;
-}
-
-bool LLDataPackerAsciiBuffer::unpackString(std::string& value, const char *name)
-{
- char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
- if (!getValueStr(name, valuestr, DP_BUFSIZE)) // NULL terminated
- {
- return false;
- }
- value = valuestr;
- return true;
-}
-
-
-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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryData: number truncated: " << size << LL_ENDL;
- }
- 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryData: data truncated: " << LL_ENDL;
- bBufferFull = true;
- }
- mCurBufferp += numCopied;
- }
-
- if (!bBufferFull)
- {
- numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */
- if (numCopied < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryData: newline truncated: " << LL_ENDL;
- }
- 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryDataFixed: data truncated: " << LL_ENDL;
- bBufferFull = true;
- }
- mCurBufferp += numCopied;
-
- }
- if (!bBufferFull)
- {
- numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */
- if (numCopied < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryDataFixed: newline truncated: " << LL_ENDL;
- }
-
- 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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packU8: val truncated: " << LL_ENDL;
- }
-
- 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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packU16: val truncated: " << LL_ENDL;
- }
-
- 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::packS16(const S16 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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize() - getCurrentSize())
- {
- numCopied = getBufferSize() - getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packS16: val truncated: " << LL_ENDL;
- }
-
- mCurBufferp += numCopied;
-
- return success;
-}
-
-
-bool LLDataPackerAsciiBuffer::unpackS16(S16 &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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packU32: val truncated: " << LL_ENDL;
- }
-
- 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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packS32: val truncated: " << LL_ENDL;
- }
-
- 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(),"%f\n", value); /* Flawfinder: ignore */
- }
- else
- {
- numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%f\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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packF32: val truncated: " << LL_ENDL;
- }
-
- 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,"%f", &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(),"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
- }
- else
- {
- numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f %f %f\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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packColor4: truncated: " << LL_ENDL;
- }
-
- 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,"%f %f %f %f", &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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packColor4U: truncated: " << LL_ENDL;
- }
-
- 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(),"%f %f\n", value.mV[0], value.mV[1]); /* Flawfinder: ignore */
- }
- else
- {
- numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f\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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packVector2: truncated: " << LL_ENDL;
- }
-
- 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,"%f %f", &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(),"%f %f %f\n", value.mV[0], value.mV[1], value.mV[2]); /* Flawfinder: ignore */
- }
- else
- {
- numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f %f\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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packVector3: truncated: " << LL_ENDL;
- }
-
- 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,"%f %f %f", &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(),"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
- }
- else
- {
- numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f %f %f\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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packVector4: truncated: " << LL_ENDL;
- }
-
- 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,"%f %f %f %f", &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)
- {
- std::string tmp_str;
- value.toString(tmp_str);
- numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", tmp_str.c_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
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::packUUID: truncated: " << LL_ENDL;
- 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); /* Flawfinder: ignore */
- value.set(tmp_str);
-
- return success;
-}
-
-void LLDataPackerAsciiBuffer::dump()
-{
- LL_INFOS() << "Buffer: " << mBufferp << LL_ENDL;
-}
-
-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; /* Flawfinder: ignore */ //name + tab
- }
-
- // snprintf returns number of bytes that would have been written
- // had the output not being truncated. In that case, it will
- // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize())
- {
- numCopied = getBufferSize()-getCurrentSize();
- LL_WARNS() << "LLDataPackerAsciiBuffer::writeIndentedName: truncated: " << LL_ENDL;
- }
-
- mCurBufferp += numCopied;
- }
-}
-
-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; /* Flawfinder: ignore */
-
- sscanf(buffer, "%511s %511[^\n]", keyword, value); /* Flawfinder: ignore */
-
- if (strcmp(keyword, name))
- {
- LL_WARNS() << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << LL_ENDL;
- 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;
-}
-
-// helper function used by LLDataPackerAsciiFile
-// to convert F32 into a string. This is to avoid
-// << operator writing F32 value into a stream
-// since it does not seem to preserve the float value
-std::string convertF32ToString(F32 val)
-{
- std::string str;
- char buf[20];
- snprintf(buf, 20, "%f", val);
- str = buf;
- return str;
-}
-
-//---------------------------------------------------------------------------
-// LLDataPackerAsciiFile implementation
-//---------------------------------------------------------------------------
-bool LLDataPackerAsciiFile::packString(const std::string& value, const char *name)
-{
- bool success = true;
- writeIndentedName(name);
- if (mFP)
- {
- fprintf(mFP,"%s\n", value.c_str());
- }
- else if (mOutputStream)
- {
- *mOutputStream << value << "\n";
- }
- return success;
-}
-
-bool LLDataPackerAsciiFile::unpackString(std::string& value, const char *name)
-{
- bool success = true;
- char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
- if (!getValueStr(name, valuestr, DP_BUFSIZE))
- {
- return false;
- }
- value = valuestr;
- 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::packS16(const S16 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::unpackS16(S16 &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,"%f\n", value);
- }
- else if (mOutputStream)
- {
- *mOutputStream <<"" << convertF32ToString(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,"%f", &value);
- return success;
-}
-
-
-bool LLDataPackerAsciiFile::packColor4(const LLColor4 &value, const char *name)
-{
- bool success = true;
- writeIndentedName(name);
- if (mFP)
- {
- fprintf(mFP,"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
- }
- else if (mOutputStream)
- {
- *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(value.mV[1]) << " " << convertF32ToString(value.mV[2]) << " " << convertF32ToString(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,"%f %f %f %f", &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,"%f %f\n", value.mV[0], value.mV[1]);
- }
- else if (mOutputStream)
- {
- *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(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,"%f %f", &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,"%f %f %f\n", value.mV[0], value.mV[1], value.mV[2]);
- }
- else if (mOutputStream)
- {
- *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(value.mV[1]) << " " << convertF32ToString(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,"%f %f %f", &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,"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
- }
- else if (mOutputStream)
- {
- *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(value.mV[1]) << " " << convertF32ToString(value.mV[2]) << " " << convertF32ToString(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,"%f %f %f %f", &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);
- std::string tmp_str;
- value.toString(tmp_str);
- if (mFP)
- {
- fprintf(mFP,"%s\n", tmp_str.c_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); /* Flawfinder: ignore */
- value.set(tmp_str);
-
- return success;
-}
-
-
-void LLDataPackerAsciiFile::writeIndentedName(const char *name)
-{
- std::string indent_buf;
- indent_buf.reserve(mIndent+1);
-
- 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.c_str(), 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;
- if (0 != fgetpos(mFP, &last_pos)) // 0==success for fgetpos
- {
- LL_WARNS() << "Data packer failed to fgetpos" << LL_ENDL;
- return false;
- }
-
- if (fgets(buffer, DP_BUFSIZE, mFP) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf(buffer, "%511s %511[^\n]", keyword, value); /* Flawfinder: ignore */
-
- if (!keyword[0])
- {
- LL_WARNS() << "Data packer could not get the keyword!" << LL_ENDL;
- fsetpos(mFP, &last_pos);
- return false;
- }
- if (strcmp(keyword, name))
- {
- LL_WARNS() << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << LL_ENDL;
- 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); /* Flawfinder: ignore */
- if (!keyword[0])
- {
- LL_WARNS() << "Data packer could not get the keyword!" << LL_ENDL;
- return false;
- }
- if (strcmp(keyword, name))
- {
- LL_WARNS() << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << LL_ENDL;
- 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;
-}
+/** + * @file lldatapacker.cpp + * @brief Data packer implementation. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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) +{ +} + +//virtual +void LLDataPacker::reset() +{ + LL_ERRS() << "Using unimplemented datapacker reset!" << LL_ENDL; +} + +//virtual +void LLDataPacker::dumpBufferToLog() +{ + LL_ERRS() << "dumpBufferToLog not implemented for this type!" << LL_ENDL; +} + +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 + { + LL_ERRS() << "Using fixed-point packing of " << total_bits << " bits, why?!" << LL_ENDL; + } + 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; + //LL_INFOS() << "unpackFixed:" << name << " int:" << int_bits << " frac:" << frac_bits << LL_ENDL; + S32 unsigned_bits = int_bits + frac_bits; + S32 total_bits = unsigned_bits; + + if (is_signed) + { + total_bits++; + } + + U32 max_val; + max_val = 1 << int_bits; + + F32 fixed_val; + if (total_bits <= 8) + { + U8 fixed_8; + success = unpackU8(fixed_8, name); + fixed_val = (F32)fixed_8; + } + else if (total_bits <= 16) + { + U16 fixed_16; + success = unpackU16(fixed_16, name); + fixed_val = (F32)fixed_16; + } + else if (total_bits <= 31) + { + U32 fixed_32; + success = unpackU32(fixed_32, name); + fixed_val = (F32)fixed_32; + } + else + { + fixed_val = 0; + LL_ERRS() << "Bad bit count: " << total_bits << LL_ENDL; + } + + //LL_INFOS() << "Fixed_val:" << fixed_val << LL_ENDL; + + fixed_val /= (F32)(1 << frac_bits); + if (is_signed) + { + fixed_val -= max_val; + } + value = fixed_val; + //LL_INFOS() << "Value: " << value << LL_ENDL; + return success; +} + +bool LLDataPacker::unpackU16s(U16 *values, S32 count, const char *name) +{ + for (S32 idx = 0; idx < count; ++idx) + { + if (!unpackU16(values[idx], name)) + { + LL_WARNS("DATAPACKER") << "Buffer overflow reading Unsigned 16s \"" << name << "\" at index " << idx << "!" << LL_ENDL; + return false; + } + } + return true; +} + +bool LLDataPacker::unpackS16s(S16 *values, S32 count, const char *name) +{ + for (S32 idx = 0; idx < count; ++idx) + { + if (!unpackS16(values[idx], name)) + { + LL_WARNS("DATAPACKER") << "Buffer overflow reading Signed 16s \"" << name << "\" at index " << idx << "!" << LL_ENDL; + return false; + } + } + return true; +} + +bool LLDataPacker::unpackF32s(F32 *values, S32 count, const char *name) +{ + for (S32 idx = 0; idx < count; ++idx) + { + if (!unpackF32(values[idx], name)) + { + LL_WARNS("DATAPACKER") << "Buffer overflow reading Float 32s \"" << name << "\" at index " << idx << "!" << LL_ENDL; + return false; + } + } + return true; +} + +bool LLDataPacker::unpackColor4Us(LLColor4U *values, S32 count, const char *name) +{ + for (S32 idx = 0; idx < count; ++idx) + { + if (!unpackColor4U(values[idx], name)) + { + LL_WARNS("DATAPACKER") << "Buffer overflow reading Float 32s \"" << name << "\" at index " << idx << "!" << LL_ENDL; + return false; + } + } + return true; +} + +bool LLDataPacker::unpackUUIDs(LLUUID *values, S32 count, const char *name) +{ + for (S32 idx = 0; idx < count; ++idx) + { + if (!unpackUUID(values[idx], name)) + { + LL_WARNS("DATAPACKER") << "Buffer overflow reading UUIDs \"" << name << "\" at index " << idx << "!" << LL_ENDL; + return false; + } + } + return true; +} + +//--------------------------------------------------------------------------- +// LLDataPackerBinaryBuffer implementation +//--------------------------------------------------------------------------- + +bool LLDataPackerBinaryBuffer::packString(const std::string& value, const char *name) +{ + S32 length = value.length()+1; + + if (!verifyLength(length, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value.c_str(), MVT_VARIABLE, length); + } + mCurBufferp += length; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackString(std::string& value, const char *name) +{ + S32 length = (S32)strlen((char *)mCurBufferp) + 1; /*Flawfinder: ignore*/ + + if (!verifyLength(length, name)) + { + return false; + } + + value = std::string((char*)mCurBufferp); // We already assume NULL termination calling strlen() + + mCurBufferp += length; + return true; +} + +bool LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const char *name) +{ + if (!verifyLength(size + 4, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, &size, MVT_S32, 4); + } + mCurBufferp += 4; + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value, MVT_VARIABLE, size); + } + mCurBufferp += size; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name) +{ + if (!verifyLength(4, name)) + { + LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << LL_ENDL; + return false; + } + + htolememcpy(&size, mCurBufferp, MVT_S32, 4); + + if (size < 0) + { + LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData unpacked invalid size, aborting!" << LL_ENDL; + return false; + } + + mCurBufferp += 4; + + if (!verifyLength(size, name)) + { + LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << LL_ENDL; + return false; + } + + htolememcpy(value, mCurBufferp, MVT_VARIABLE, size); + mCurBufferp += size; + + return true; +} + + +bool LLDataPackerBinaryBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name) +{ + if (!verifyLength(size, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value, MVT_VARIABLE, size); + } + mCurBufferp += size; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name) +{ + if (!verifyLength(size, name)) + { + return false; + } + htolememcpy(value, mCurBufferp, MVT_VARIABLE, size); + mCurBufferp += size; + return true; +} + + +bool LLDataPackerBinaryBuffer::packU8(const U8 value, const char *name) +{ + if (!verifyLength(sizeof(U8), name)) + { + return false; + } + + if (mWriteEnabled) + { + *mCurBufferp = value; + } + mCurBufferp++; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackU8(U8 &value, const char *name) +{ + if (!verifyLength(sizeof(U8), name)) + { + return false; + } + + value = *mCurBufferp; + mCurBufferp++; + return true; +} + + +bool LLDataPackerBinaryBuffer::packU16(const U16 value, const char *name) +{ + if (!verifyLength(sizeof(U16), name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, &value, MVT_U16, 2); + } + mCurBufferp += 2; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackU16(U16 &value, const char *name) +{ + if (!verifyLength(sizeof(U16), name)) + { + return false; + } + + htolememcpy(&value, mCurBufferp, MVT_U16, 2); + mCurBufferp += 2; + return true; +} + +bool LLDataPackerBinaryBuffer::packS16(const S16 value, const char *name) +{ + bool success = verifyLength(sizeof(S16), name); + + if (mWriteEnabled && success) + { + htolememcpy(mCurBufferp, &value, MVT_S16, 2); + } + mCurBufferp += 2; + return success; +} + +bool LLDataPackerBinaryBuffer::unpackS16(S16 &value, const char *name) +{ + bool success = verifyLength(sizeof(S16), name); + + if (success) + { + htolememcpy(&value, mCurBufferp, MVT_S16, 2); + } + mCurBufferp += 2; + return success; +} + +bool LLDataPackerBinaryBuffer::packU32(const U32 value, const char *name) +{ + if (!verifyLength(sizeof(U32), name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, &value, MVT_U32, 4); + } + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackU32(U32 &value, const char *name) +{ + if (!verifyLength(sizeof(U32), name)) + { + return false; + } + + htolememcpy(&value, mCurBufferp, MVT_U32, 4); + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::packS32(const S32 value, const char *name) +{ + if (!verifyLength(sizeof(S32), name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, &value, MVT_S32, 4); + } + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackS32(S32 &value, const char *name) +{ + if(!verifyLength(sizeof(S32), name)) + { + return false; + } + + htolememcpy(&value, mCurBufferp, MVT_S32, 4); + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::packF32(const F32 value, const char *name) +{ + if (!verifyLength(sizeof(F32), name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, &value, MVT_F32, 4); + } + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackF32(F32 &value, const char *name) +{ + if (!verifyLength(sizeof(F32), name)) + { + return false; + } + + htolememcpy(&value, mCurBufferp, MVT_F32, 4); + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::packColor4(const LLColor4 &value, const char *name) +{ + if (!verifyLength(16, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); + } + mCurBufferp += 16; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackColor4(LLColor4 &value, const char *name) +{ + if (!verifyLength(16, name)) + { + return false; + } + + htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16); + mCurBufferp += 16; + return true; +} + + +bool LLDataPackerBinaryBuffer::packColor4U(const LLColor4U &value, const char *name) +{ + if (!verifyLength(4, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4); + } + mCurBufferp += 4; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackColor4U(LLColor4U &value, const char *name) +{ + if (!verifyLength(4, name)) + { + return false; + } + + htolememcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4); + mCurBufferp += 4; + return true; +} + + + +bool LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *name) +{ + if (!verifyLength(8, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, &value.mV[0], MVT_F32, 4); + htolememcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4); + } + mCurBufferp += 8; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackVector2(LLVector2 &value, const char *name) +{ + if (!verifyLength(8, name)) + { + return false; + } + + htolememcpy(&value.mV[0], mCurBufferp, MVT_F32, 4); + htolememcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4); + mCurBufferp += 8; + return true; +} + + +bool LLDataPackerBinaryBuffer::packVector3(const LLVector3 &value, const char *name) +{ + if (!verifyLength(12, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value.mV, MVT_LLVector3, 12); + } + mCurBufferp += 12; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackVector3(LLVector3 &value, const char *name) +{ + if (!verifyLength(12, name)) + { + return false; + } + + htolememcpy(value.mV, mCurBufferp, MVT_LLVector3, 12); + mCurBufferp += 12; + return true; +} + +bool LLDataPackerBinaryBuffer::packVector4(const LLVector4 &value, const char *name) +{ + if (!verifyLength(16, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); + } + mCurBufferp += 16; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackVector4(LLVector4 &value, const char *name) +{ + if (!verifyLength(16, name)) + { + return false; + } + + htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16); + mCurBufferp += 16; + return true; +} + +bool LLDataPackerBinaryBuffer::packUUID(const LLUUID &value, const char *name) +{ + if (!verifyLength(16, name)) + { + return false; + } + + if (mWriteEnabled) + { + htolememcpy(mCurBufferp, value.mData, MVT_LLUUID, 16); + } + mCurBufferp += 16; + return true; +} + + +bool LLDataPackerBinaryBuffer::unpackUUID(LLUUID &value, const char *name) +{ + if (!verifyLength(16, name)) + { + return false; + } + + htolememcpy(value.mData, mCurBufferp, MVT_LLUUID, 16); + mCurBufferp += 16; + return true; +} + +const LLDataPackerBinaryBuffer& LLDataPackerBinaryBuffer::operator=(const LLDataPackerBinaryBuffer &a) +{ + if (a.getBufferSize() > getBufferSize()) + { + // We've got problems, ack! + LL_ERRS() << "Trying to do an assignment with not enough room in the target." << LL_ENDL; + } + memcpy(mBufferp, a.mBufferp, a.getBufferSize()); /*Flawfinder: ignore*/ + return *this; +} + +void LLDataPackerBinaryBuffer::dumpBufferToLog() +{ + LL_WARNS() << "Binary Buffer Dump, size: " << mBufferSize << LL_ENDL; + 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; + LL_WARNS() << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << LL_ENDL; + cur_line++; + } + } + if (cur_line_pos) + { + LL_WARNS() << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << LL_ENDL; + } +} + +//--------------------------------------------------------------------------- +// LLDataPackerAsciiBuffer implementation +//--------------------------------------------------------------------------- +bool LLDataPackerAsciiBuffer::packString(const std::string& value, const char *name) +{ + bool success = true; + writeIndentedName(name); + int numCopied = 0; + if (mWriteEnabled) + { + numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", value.c_str()); /* Flawfinder: ignore */ + } + else + { + numCopied = value.length() + 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 either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + // *NOTE: I believe we need to mark a failure bit at this point. + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packString: string truncated: " << value << LL_ENDL; + } + mCurBufferp += numCopied; + return success; +} + +bool LLDataPackerAsciiBuffer::unpackString(std::string& value, const char *name) +{ + char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/ + if (!getValueStr(name, valuestr, DP_BUFSIZE)) // NULL terminated + { + return false; + } + value = valuestr; + return true; +} + + +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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryData: number truncated: " << size << LL_ENDL; + } + 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryData: data truncated: " << LL_ENDL; + bBufferFull = true; + } + mCurBufferp += numCopied; + } + + if (!bBufferFull) + { + numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */ + if (numCopied < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryData: newline truncated: " << LL_ENDL; + } + 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryDataFixed: data truncated: " << LL_ENDL; + bBufferFull = true; + } + mCurBufferp += numCopied; + + } + if (!bBufferFull) + { + numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */ + if (numCopied < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packBinaryDataFixed: newline truncated: " << LL_ENDL; + } + + 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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packU8: val truncated: " << LL_ENDL; + } + + 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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packU16: val truncated: " << LL_ENDL; + } + + 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::packS16(const S16 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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize() - getCurrentSize()) + { + numCopied = getBufferSize() - getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packS16: val truncated: " << LL_ENDL; + } + + mCurBufferp += numCopied; + + return success; +} + + +bool LLDataPackerAsciiBuffer::unpackS16(S16 &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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packU32: val truncated: " << LL_ENDL; + } + + 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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packS32: val truncated: " << LL_ENDL; + } + + 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(),"%f\n", value); /* Flawfinder: ignore */ + } + else + { + numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%f\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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packF32: val truncated: " << LL_ENDL; + } + + 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,"%f", &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(),"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */ + } + else + { + numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f %f %f\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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packColor4: truncated: " << LL_ENDL; + } + + 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,"%f %f %f %f", &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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packColor4U: truncated: " << LL_ENDL; + } + + 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(),"%f %f\n", value.mV[0], value.mV[1]); /* Flawfinder: ignore */ + } + else + { + numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f\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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packVector2: truncated: " << LL_ENDL; + } + + 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,"%f %f", &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(),"%f %f %f\n", value.mV[0], value.mV[1], value.mV[2]); /* Flawfinder: ignore */ + } + else + { + numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f %f\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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packVector3: truncated: " << LL_ENDL; + } + + 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,"%f %f %f", &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(),"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */ + } + else + { + numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%f %f %f %f\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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packVector4: truncated: " << LL_ENDL; + } + + 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,"%f %f %f %f", &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) + { + std::string tmp_str; + value.toString(tmp_str); + numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", tmp_str.c_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 + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::packUUID: truncated: " << LL_ENDL; + 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); /* Flawfinder: ignore */ + value.set(tmp_str); + + return success; +} + +void LLDataPackerAsciiBuffer::dump() +{ + LL_INFOS() << "Buffer: " << mBufferp << LL_ENDL; +} + +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; /* Flawfinder: ignore */ //name + tab + } + + // snprintf returns number of bytes that would have been written + // had the output not being truncated. In that case, it will + // return either -1 or value >= 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 < 0 || numCopied > getBufferSize()-getCurrentSize()) + { + numCopied = getBufferSize()-getCurrentSize(); + LL_WARNS() << "LLDataPackerAsciiBuffer::writeIndentedName: truncated: " << LL_ENDL; + } + + mCurBufferp += numCopied; + } +} + +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; /* Flawfinder: ignore */ + + sscanf(buffer, "%511s %511[^\n]", keyword, value); /* Flawfinder: ignore */ + + if (strcmp(keyword, name)) + { + LL_WARNS() << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << LL_ENDL; + 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; +} + +// helper function used by LLDataPackerAsciiFile +// to convert F32 into a string. This is to avoid +// << operator writing F32 value into a stream +// since it does not seem to preserve the float value +std::string convertF32ToString(F32 val) +{ + std::string str; + char buf[20]; + snprintf(buf, 20, "%f", val); + str = buf; + return str; +} + +//--------------------------------------------------------------------------- +// LLDataPackerAsciiFile implementation +//--------------------------------------------------------------------------- +bool LLDataPackerAsciiFile::packString(const std::string& value, const char *name) +{ + bool success = true; + writeIndentedName(name); + if (mFP) + { + fprintf(mFP,"%s\n", value.c_str()); + } + else if (mOutputStream) + { + *mOutputStream << value << "\n"; + } + return success; +} + +bool LLDataPackerAsciiFile::unpackString(std::string& value, const char *name) +{ + bool success = true; + char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */ + if (!getValueStr(name, valuestr, DP_BUFSIZE)) + { + return false; + } + value = valuestr; + 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::packS16(const S16 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::unpackS16(S16 &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,"%f\n", value); + } + else if (mOutputStream) + { + *mOutputStream <<"" << convertF32ToString(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,"%f", &value); + return success; +} + + +bool LLDataPackerAsciiFile::packColor4(const LLColor4 &value, const char *name) +{ + bool success = true; + writeIndentedName(name); + if (mFP) + { + fprintf(mFP,"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); + } + else if (mOutputStream) + { + *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(value.mV[1]) << " " << convertF32ToString(value.mV[2]) << " " << convertF32ToString(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,"%f %f %f %f", &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,"%f %f\n", value.mV[0], value.mV[1]); + } + else if (mOutputStream) + { + *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(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,"%f %f", &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,"%f %f %f\n", value.mV[0], value.mV[1], value.mV[2]); + } + else if (mOutputStream) + { + *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(value.mV[1]) << " " << convertF32ToString(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,"%f %f %f", &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,"%f %f %f %f\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); + } + else if (mOutputStream) + { + *mOutputStream << convertF32ToString(value.mV[0]) << " " << convertF32ToString(value.mV[1]) << " " << convertF32ToString(value.mV[2]) << " " << convertF32ToString(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,"%f %f %f %f", &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); + std::string tmp_str; + value.toString(tmp_str); + if (mFP) + { + fprintf(mFP,"%s\n", tmp_str.c_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); /* Flawfinder: ignore */ + value.set(tmp_str); + + return success; +} + + +void LLDataPackerAsciiFile::writeIndentedName(const char *name) +{ + std::string indent_buf; + indent_buf.reserve(mIndent+1); + + 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.c_str(), 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; + if (0 != fgetpos(mFP, &last_pos)) // 0==success for fgetpos + { + LL_WARNS() << "Data packer failed to fgetpos" << LL_ENDL; + return false; + } + + if (fgets(buffer, DP_BUFSIZE, mFP) == NULL) + { + buffer[0] = '\0'; + } + + sscanf(buffer, "%511s %511[^\n]", keyword, value); /* Flawfinder: ignore */ + + if (!keyword[0]) + { + LL_WARNS() << "Data packer could not get the keyword!" << LL_ENDL; + fsetpos(mFP, &last_pos); + return false; + } + if (strcmp(keyword, name)) + { + LL_WARNS() << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << LL_ENDL; + 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); /* Flawfinder: ignore */ + if (!keyword[0]) + { + LL_WARNS() << "Data packer could not get the keyword!" << LL_ENDL; + return false; + } + if (strcmp(keyword, name)) + { + LL_WARNS() << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << LL_ENDL; + 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 index 5c896d28fc..167c102b43 100644 --- a/indra/llmessage/lldatapacker.h +++ b/indra/llmessage/lldatapacker.h @@ -1,435 +1,435 @@ -/**
- * @file lldatapacker.h
- * @brief Data packer declaration for tightly storing binary data.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLDATAPACKER_H
-#define LL_LLDATAPACKER_H
-
-class LLColor4;
-class LLColor4U;
-class LLVector2;
-class LLVector3;
-class LLVector4;
-class LLUUID;
-
-class LLDataPacker
-{
-public:
- virtual ~LLDataPacker() {}
-
- // Not required to override, but error to call?
- virtual void reset();
- virtual void dumpBufferToLog();
-
- virtual bool hasNext() const = 0;
-
- virtual bool packString(const std::string& value, const char *name) = 0;
- virtual bool unpackString(std::string& 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;
- bool unpackU16s(U16 *value, S32 count, const char *name);
-
- virtual bool packS16(const S16 value, const char *name) = 0;
- virtual bool unpackS16(S16 &value, const char *name) = 0;
- bool unpackS16s(S16 *value, S32 count, const char *name);
-
- 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;
- bool unpackF32s(F32 *values, S32 count, const char *name);
-
- // 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;
- bool unpackColor4Us(LLColor4U *values, S32 count, const char *name);
-
- 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;
- bool unpackUUIDs(LLUUID *values, S32 count, const char *name);
- 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 std::string& value, const char *name);
- /*virtual*/ bool unpackString(std::string& 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 packS16(const S16 value, const char *name);
- /*virtual*/ bool unpackS16(S16 &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; }
- const U8* getBuffer() const { return mBufferp; }
- void reset() { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }
- void shift(S32 offset) { reset(); mCurBufferp += offset;}
- void freeBuffer() { delete [] mBufferp; mBufferp = mCurBufferp = NULL; mBufferSize = 0; mWriteEnabled = false; }
- void assignBuffer(U8 *bufferp, S32 size)
- {
- if(mBufferp && mBufferp != bufferp)
- {
- freeBuffer() ;
- }
- 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)
- {
- LL_WARNS() << "Buffer overflow in BinaryBuffer length verify, field name " << name << "!" << LL_ENDL;
- LL_WARNS() << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << LL_ENDL;
- 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 std::string& value, const char *name);
- /*virtual*/ bool unpackString(std::string& 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 packS16(const S16 value, const char *name);
- /*virtual*/ bool unpackS16(S16 &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)
- {
- LL_WARNS() << "Buffer overflow in AsciiBuffer length verify, field name " << name << "!" << LL_ENDL;
- LL_WARNS() << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << LL_ENDL;
- return false;
- }
-
- return true;
-}
-
-class LLDataPackerAsciiFile : public LLDataPacker
-{
-public:
- LLDataPackerAsciiFile(LLFILE *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 std::string& value, const char *name);
- /*virtual*/ bool unpackString(std::string& 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 packS16(const S16 value, const char *name);
- /*virtual*/ bool unpackS16(S16 &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;
- LLFILE *mFP;
- std::ostream* mOutputStream;
- std::istream* mInputStream;
-};
-
-#endif // LL_LLDATAPACKER
-
+/** + * @file lldatapacker.h + * @brief Data packer declaration for tightly storing binary data. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLDATAPACKER_H +#define LL_LLDATAPACKER_H + +class LLColor4; +class LLColor4U; +class LLVector2; +class LLVector3; +class LLVector4; +class LLUUID; + +class LLDataPacker +{ +public: + virtual ~LLDataPacker() {} + + // Not required to override, but error to call? + virtual void reset(); + virtual void dumpBufferToLog(); + + virtual bool hasNext() const = 0; + + virtual bool packString(const std::string& value, const char *name) = 0; + virtual bool unpackString(std::string& 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; + bool unpackU16s(U16 *value, S32 count, const char *name); + + virtual bool packS16(const S16 value, const char *name) = 0; + virtual bool unpackS16(S16 &value, const char *name) = 0; + bool unpackS16s(S16 *value, S32 count, const char *name); + + 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; + bool unpackF32s(F32 *values, S32 count, const char *name); + + // 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; + bool unpackColor4Us(LLColor4U *values, S32 count, const char *name); + + 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; + bool unpackUUIDs(LLUUID *values, S32 count, const char *name); + 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 std::string& value, const char *name); + /*virtual*/ bool unpackString(std::string& 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 packS16(const S16 value, const char *name); + /*virtual*/ bool unpackS16(S16 &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; } + const U8* getBuffer() const { return mBufferp; } + void reset() { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); } + void shift(S32 offset) { reset(); mCurBufferp += offset;} + void freeBuffer() { delete [] mBufferp; mBufferp = mCurBufferp = NULL; mBufferSize = 0; mWriteEnabled = false; } + void assignBuffer(U8 *bufferp, S32 size) + { + if(mBufferp && mBufferp != bufferp) + { + freeBuffer() ; + } + 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) + { + LL_WARNS() << "Buffer overflow in BinaryBuffer length verify, field name " << name << "!" << LL_ENDL; + LL_WARNS() << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << LL_ENDL; + 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 std::string& value, const char *name); + /*virtual*/ bool unpackString(std::string& 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 packS16(const S16 value, const char *name); + /*virtual*/ bool unpackS16(S16 &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) + { + LL_WARNS() << "Buffer overflow in AsciiBuffer length verify, field name " << name << "!" << LL_ENDL; + LL_WARNS() << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << LL_ENDL; + return false; + } + + return true; +} + +class LLDataPackerAsciiFile : public LLDataPacker +{ +public: + LLDataPackerAsciiFile(LLFILE *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 std::string& value, const char *name); + /*virtual*/ bool unpackString(std::string& 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 packS16(const S16 value, const char *name); + /*virtual*/ bool unpackS16(S16 &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; + LLFILE *mFP; + std::ostream* mOutputStream; + std::istream* mInputStream; +}; + +#endif // LL_LLDATAPACKER + diff --git a/indra/llmessage/llhost.cpp b/indra/llmessage/llhost.cpp index 264d64bb50..ace316512f 100644 --- a/indra/llmessage/llhost.cpp +++ b/indra/llmessage/llhost.cpp @@ -1,176 +1,176 @@ -/**
- * @file llhost.cpp
- * @brief Encapsulates an IP address and a port.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llhost.h"
-
-#include "llerror.h"
-
-#if LL_WINDOWS
- #define WIN32_LEAN_AND_MEAN
- #include <winsock2.h>
-#else
- #include <netdb.h>
- #include <netinet/in.h> // ntonl()
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
-#endif
-
-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());
- }
-}
-
-std::string LLHost::getString() const
-{
- return llformat("%s:%u", u32_to_ip_string(mIP), mPort);
-}
-
-
-std::string LLHost::getIPandPort() const
-{
- return getString();
-}
-
-
-std::string LLHost::getIPString() const
-{
- return std::string( u32_to_ip_string( mIP ) );
-}
-
-
-std::string LLHost::getHostName() const
-{
- hostent* he;
- if (INVALID_HOST_IP_ADDRESS == mIP)
- {
- LL_WARNS() << "LLHost::getHostName() : Invalid IP address" << LL_ENDL;
- return std::string();
- }
- he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET);
- if (!he)
- {
-#if LL_WINDOWS
- LL_WARNS() << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
- << WSAGetLastError() << LL_ENDL;
-#else
- LL_WARNS() << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
- << h_errno << LL_ENDL;
-#endif
- return std::string();
- }
- else
- {
- return ll_safe_string(he->h_name);
- }
-}
-
-bool LLHost::setHostByName(const std::string& hostname)
-{
- hostent *he;
- std::string local_name(hostname);
-
-#if LL_WINDOWS
- // We may need an equivalent for Linux, but not sure - djs
- LLStringUtil::toUpper(local_name);
-#endif
-
- he = gethostbyname(local_name.c_str());
- if(!he)
- {
- U32 ip_address = ip_string_to_u32(hostname.c_str());
- 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?
- LL_WARNS() << "LLHost::setAddress(): try again" << LL_ENDL;
- break;
- case HOST_NOT_FOUND:
- case NO_ADDRESS: // NO_DATA
- LL_WARNS() << "LLHost::setAddress(): host not found" << LL_ENDL;
- break;
- case NO_RECOVERY:
- LL_WARNS() << "LLHost::setAddress(): unrecoverable error" << LL_ENDL;
- break;
- default:
- LL_WARNS() << "LLHost::setAddress(): unknown error - " << error_number << LL_ENDL;
- 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;
-//}
+/** + * @file llhost.cpp + * @brief Encapsulates an IP address and a port. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llhost.h" + +#include "llerror.h" + +#if LL_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include <winsock2.h> +#else + #include <netdb.h> + #include <netinet/in.h> // ntonl() + #include <sys/types.h> + #include <sys/socket.h> + #include <arpa/inet.h> +#endif + +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()); + } +} + +std::string LLHost::getString() const +{ + return llformat("%s:%u", u32_to_ip_string(mIP), mPort); +} + + +std::string LLHost::getIPandPort() const +{ + return getString(); +} + + +std::string LLHost::getIPString() const +{ + return std::string( u32_to_ip_string( mIP ) ); +} + + +std::string LLHost::getHostName() const +{ + hostent* he; + if (INVALID_HOST_IP_ADDRESS == mIP) + { + LL_WARNS() << "LLHost::getHostName() : Invalid IP address" << LL_ENDL; + return std::string(); + } + he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET); + if (!he) + { +#if LL_WINDOWS + LL_WARNS() << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: " + << WSAGetLastError() << LL_ENDL; +#else + LL_WARNS() << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: " + << h_errno << LL_ENDL; +#endif + return std::string(); + } + else + { + return ll_safe_string(he->h_name); + } +} + +bool LLHost::setHostByName(const std::string& hostname) +{ + hostent *he; + std::string local_name(hostname); + +#if LL_WINDOWS + // We may need an equivalent for Linux, but not sure - djs + LLStringUtil::toUpper(local_name); +#endif + + he = gethostbyname(local_name.c_str()); + if(!he) + { + U32 ip_address = ip_string_to_u32(hostname.c_str()); + 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? + LL_WARNS() << "LLHost::setAddress(): try again" << LL_ENDL; + break; + case HOST_NOT_FOUND: + case NO_ADDRESS: // NO_DATA + LL_WARNS() << "LLHost::setAddress(): host not found" << LL_ENDL; + break; + case NO_RECOVERY: + LL_WARNS() << "LLHost::setAddress(): unrecoverable error" << LL_ENDL; + break; + default: + LL_WARNS() << "LLHost::setAddress(): unknown error - " << error_number << LL_ENDL; + 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 index 1a3a48eede..6824fa3d78 100644 --- a/indra/llmessage/llhost.h +++ b/indra/llmessage/llhost.h @@ -1,154 +1,154 @@ -/**
- * @file llhost.h
- * @brief a LLHost uniquely defines a host (Simulator, Proxy or other)
- * across the network
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLHOST_H
-#define LL_LLHOST_H
-
-#include <iostream>
-#include <string>
-
-#include "net.h"
-
-const U32 INVALID_PORT = 0;
-const U32 INVALID_HOST_IP_ADDRESS = 0x0;
-
-class LLHost {
-protected:
- U32 mPort;
- U32 mIP;
- std::string mUntrustedSimCap;
-public:
-
- // 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 std::string& ipv4_addr, U32 port )
- : mPort( port )
- {
- mIP = ip_string_to_u32(ipv4_addr.c_str());
- }
-
- 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 std::string& ipstr, U32 port ) { mIP = ip_string_to_u32(ipstr.c_str()); mPort = port; }
- void setAddress( const std::string& ipstr ) { mIP = ip_string_to_u32(ipstr.c_str()); }
- void setAddress( U32 ip ) { mIP = ip; }
- void setPort( U32 port ) { mPort = port; }
- bool setHostByName(const std::string& 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); }
- bool isInvalid() { return (mIP == INVALID_HOST_IP_ADDRESS) || (mPort == INVALID_PORT); }
- size_t hash() const { return (mIP << 16) | (mPort & 0xffff); }
- std::string getString() const;
- std::string getIPString() const;
- std::string getHostName() const;
- std::string getIPandPort() const;
-
- std::string getUntrustedSimulatorCap() const { return mUntrustedSimCap; }
- void setUntrustedSimulatorCap(const std::string &capurl) { mUntrustedSimCap = capurl; }
-
- friend std::ostream& operator<< (std::ostream& os, const LLHost &hh);
-
- // This operator is not well defined. does it expect a
- // "192.168.1.1:80" notation or "int int" format? Phoenix 2007-05-18
- //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
+/** + * @file llhost.h + * @brief a LLHost uniquely defines a host (Simulator, Proxy or other) + * across the network + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLHOST_H +#define LL_LLHOST_H + +#include <iostream> +#include <string> + +#include "net.h" + +const U32 INVALID_PORT = 0; +const U32 INVALID_HOST_IP_ADDRESS = 0x0; + +class LLHost { +protected: + U32 mPort; + U32 mIP; + std::string mUntrustedSimCap; +public: + + // 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 std::string& ipv4_addr, U32 port ) + : mPort( port ) + { + mIP = ip_string_to_u32(ipv4_addr.c_str()); + } + + 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 std::string& ipstr, U32 port ) { mIP = ip_string_to_u32(ipstr.c_str()); mPort = port; } + void setAddress( const std::string& ipstr ) { mIP = ip_string_to_u32(ipstr.c_str()); } + void setAddress( U32 ip ) { mIP = ip; } + void setPort( U32 port ) { mPort = port; } + bool setHostByName(const std::string& 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); } + bool isInvalid() { return (mIP == INVALID_HOST_IP_ADDRESS) || (mPort == INVALID_PORT); } + size_t hash() const { return (mIP << 16) | (mPort & 0xffff); } + std::string getString() const; + std::string getIPString() const; + std::string getHostName() const; + std::string getIPandPort() const; + + std::string getUntrustedSimulatorCap() const { return mUntrustedSimCap; } + void setUntrustedSimulatorCap(const std::string &capurl) { mUntrustedSimCap = capurl; } + + friend std::ostream& operator<< (std::ostream& os, const LLHost &hh); + + // This operator is not well defined. does it expect a + // "192.168.1.1:80" notation or "int int" format? Phoenix 2007-05-18 + //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/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp index 0563613cfa..4c62c07fcb 100644 --- a/indra/llmessage/llinstantmessage.cpp +++ b/indra/llmessage/llinstantmessage.cpp @@ -1,164 +1,164 @@ -/**
- * @file llinstantmessage.cpp
- * @author Phoenix
- * @date 2005-08-29
- * @brief Constants and functions used in IM.
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lldbstrings.h"
-#include "llinstantmessage.h"
-#include "llhost.h"
-#include "lluuid.h"
-#include "llsd.h"
-#include "llsdserialize.h"
-#include "llsdutil_math.h"
-#include "llpointer.h"
-#include "message.h"
-
-#include "message.h"
-
-const U8 IM_ONLINE = 0;
-const U8 IM_OFFLINE = 1;
-
-const char EMPTY_BINARY_BUCKET[] = "";
-const S32 EMPTY_BINARY_BUCKET_SIZE = 1;
-const U32 NO_TIMESTAMP = 0;
-const std::string SYSTEM_FROM("Second Life");
-const std::string INTERACTIVE_SYSTEM_FROM("F387446C-37C4-45f2-A438-D99CBDBB563B");
-const S32 IM_TTL = 1;
-
-
-void pack_instant_message(
- LLMessageSystem* msg,
- const LLUUID& from_id,
- bool from_group,
- const LLUUID& session_id,
- const LLUUID& to_id,
- const std::string& name,
- const std::string& 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)
-{
- LL_DEBUGS() << "pack_instant_message()" << LL_ENDL;
- 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 std::string& name,
- const std::string& 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.empty())
- {
- char buffer[MTUBYTES];
- int num_written = snprintf(buffer, MTUBYTES, "%s", message.c_str()); /* Flawfinder: ignore */
- // snprintf returns number of bytes that would have been written
- // had the output not being truncated. In that case, it will
- // return either -1 or value >= 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 (num_written < 0 || num_written >= MTUBYTES)
- {
- num_written = MTUBYTES - 1;
- LL_WARNS() << "pack_instant_message_block: message truncated: " << message << LL_ENDL;
- }
-
- bytes_left -= num_written;
- 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);
-}
-
-
+/** + * @file llinstantmessage.cpp + * @author Phoenix + * @date 2005-08-29 + * @brief Constants and functions used in IM. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lldbstrings.h" +#include "llinstantmessage.h" +#include "llhost.h" +#include "lluuid.h" +#include "llsd.h" +#include "llsdserialize.h" +#include "llsdutil_math.h" +#include "llpointer.h" +#include "message.h" + +#include "message.h" + +const U8 IM_ONLINE = 0; +const U8 IM_OFFLINE = 1; + +const char EMPTY_BINARY_BUCKET[] = ""; +const S32 EMPTY_BINARY_BUCKET_SIZE = 1; +const U32 NO_TIMESTAMP = 0; +const std::string SYSTEM_FROM("Second Life"); +const std::string INTERACTIVE_SYSTEM_FROM("F387446C-37C4-45f2-A438-D99CBDBB563B"); +const S32 IM_TTL = 1; + + +void pack_instant_message( + LLMessageSystem* msg, + const LLUUID& from_id, + bool from_group, + const LLUUID& session_id, + const LLUUID& to_id, + const std::string& name, + const std::string& 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) +{ + LL_DEBUGS() << "pack_instant_message()" << LL_ENDL; + 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 std::string& name, + const std::string& 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.empty()) + { + char buffer[MTUBYTES]; + int num_written = snprintf(buffer, MTUBYTES, "%s", message.c_str()); /* Flawfinder: ignore */ + // snprintf returns number of bytes that would have been written + // had the output not being truncated. In that case, it will + // return either -1 or value >= 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 (num_written < 0 || num_written >= MTUBYTES) + { + num_written = MTUBYTES - 1; + LL_WARNS() << "pack_instant_message_block: message truncated: " << message << LL_ENDL; + } + + bytes_left -= num_written; + 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); +} + + diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h index d1f5eef548..d413e81c90 100644 --- a/indra/llmessage/llinstantmessage.h +++ b/indra/llmessage/llinstantmessage.h @@ -1,218 +1,218 @@ -/**
- * @file llinstantmessage.h
- * @brief Constants and declarations used by instant messages.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLINSTANTMESSAGE_H
-#define LL_LLINSTANTMESSAGE_H
-
-#include "llhost.h"
-#include "lluuid.h"
-#include "llsd.h"
-#include "llrefcount.h"
-#include "llpointer.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
- // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856
- 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.
- //
-
- // Invite users to a session.
- IM_SESSION_INVITE = 13,
-
- IM_SESSION_P2P_INVITE = 14,
-
- // start a session with your gruop
- IM_SESSION_GROUP_START = 15,
-
- // start a session without a calling card (finder or objects)
- IM_SESSION_CONFERENCE_START = 16,
-
- // send a message to a session.
- IM_SESSION_SEND = 17,
-
- // leave a session
- IM_SESSION_LEAVE = 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 do not disturb user, this is the auto response
- IM_DO_NOT_DISTURB_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_TELEPORT_REQUEST = 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,
-
- // 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_DEPRECATED = 40,
-
- IM_TYPING_START = 41,
- IM_TYPING_STOP = 42,
-
- IM_COUNT
-};
-
-
-extern const U8 IM_ONLINE;
-extern const U8 IM_OFFLINE;
-
-extern const char EMPTY_BINARY_BUCKET[];
-extern const S32 EMPTY_BINARY_BUCKET_SIZE;
-
-extern const U32 NO_TIMESTAMP;
-extern const std::string SYSTEM_FROM;
-extern const std::string INTERACTIVE_SYSTEM_FROM;
-
-// Number of retry attempts on sending the im.
-extern const S32 IM_TTL;
-
-void pack_instant_message(
- LLMessageSystem* msgsystem,
- const LLUUID& from_id,
- bool from_group,
- const LLUUID& session_id,
- const LLUUID& to_id,
- const std::string& name,
- const std::string& 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 std::string& name,
- const std::string& 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
-
+/** + * @file llinstantmessage.h + * @brief Constants and declarations used by instant messages. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLINSTANTMESSAGE_H +#define LL_LLINSTANTMESSAGE_H + +#include "llhost.h" +#include "lluuid.h" +#include "llsd.h" +#include "llrefcount.h" +#include "llpointer.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 + // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856 + 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. + // + + // Invite users to a session. + IM_SESSION_INVITE = 13, + + IM_SESSION_P2P_INVITE = 14, + + // start a session with your gruop + IM_SESSION_GROUP_START = 15, + + // start a session without a calling card (finder or objects) + IM_SESSION_CONFERENCE_START = 16, + + // send a message to a session. + IM_SESSION_SEND = 17, + + // leave a session + IM_SESSION_LEAVE = 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 do not disturb user, this is the auto response + IM_DO_NOT_DISTURB_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_TELEPORT_REQUEST = 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, + + // 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_DEPRECATED = 40, + + IM_TYPING_START = 41, + IM_TYPING_STOP = 42, + + IM_COUNT +}; + + +extern const U8 IM_ONLINE; +extern const U8 IM_OFFLINE; + +extern const char EMPTY_BINARY_BUCKET[]; +extern const S32 EMPTY_BINARY_BUCKET_SIZE; + +extern const U32 NO_TIMESTAMP; +extern const std::string SYSTEM_FROM; +extern const std::string INTERACTIVE_SYSTEM_FROM; + +// Number of retry attempts on sending the im. +extern const S32 IM_TTL; + +void pack_instant_message( + LLMessageSystem* msgsystem, + const LLUUID& from_id, + bool from_group, + const LLUUID& session_id, + const LLUUID& to_id, + const std::string& name, + const std::string& 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 std::string& name, + const std::string& 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/llmail.cpp b/indra/llmessage/llmail.cpp index fe01f2d4a4..ca027d7675 100644 --- a/indra/llmessage/llmail.cpp +++ b/indra/llmessage/llmail.cpp @@ -1,396 +1,396 @@ -/**
- * @file llmail.cpp
- * @brief smtp helper functions.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llmail.h"
-
-// APR on Windows needs full windows headers
-#include "llwin32headers.h"
-#include <string>
-#include <sstream>
-
-#include "apr_pools.h"
-#include "apr_network_io.h"
-
-#include "llapr.h"
-#include "llbase32.h" // IM-to-email address
-#include "llblowfishcipher.h"
-#include "llerror.h"
-#include "llhost.h"
-#include "llsd.h"
-#include "llstring.h"
-#include "lluuid.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;
-
-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.
-// static
-bool LLMail::send(
- const char* from_name,
- const char* from_address,
- const char* to_name,
- const char* to_address,
- const char* subject,
- const char* message,
- const LLSD& headers)
-{
- std::string header = buildSMTPTransaction(
- from_name,
- from_address,
- to_name,
- to_address,
- subject,
- headers);
- if(header.empty())
- {
- return false;
- }
-
- std::string message_str;
- if(message)
- {
- message_str = message;
- }
- bool rv = send(header, message_str, to_address, from_address);
- if(rv) return true;
- return false;
-}
-
-// static
-void LLMail::init(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);
- }
-}
-
-// static
-void LLMail::enable(bool mail_enabled)
-{
- gMailEnabled = mail_enabled;
-}
-
-// Test a subject line for RFC2822 compliance.
-static bool valid_subject_chars(const char *subject)
-{
- for (; *subject != '\0'; subject++)
- {
- unsigned char c = *subject;
-
- if (c == '\xa' || c == '\xd' || c > '\x7f')
- {
- return false;
- }
- }
-
- return true;
-}
-
-// static
-std::string LLMail::buildSMTPTransaction(
- const char* from_name,
- const char* from_address,
- const char* to_name,
- const char* to_address,
- const char* subject,
- const LLSD& headers)
-{
- if(!from_address || !to_address)
- {
- LL_INFOS() << "send_mail build_smtp_transaction reject: missing to and/or"
- << " from address." << LL_ENDL;
- return std::string();
- }
- if(!valid_subject_chars(subject))
- {
- LL_INFOS() << "send_mail build_smtp_transaction reject: bad subject header: "
- << "to=<" << to_address
- << ">, from=<" << from_address << ">"
- << LL_ENDL;
- 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";
-
- if(headers.isMap())
- {
- LLSD::map_const_iterator iter = headers.beginMap();
- LLSD::map_const_iterator end = headers.endMap();
- for(; iter != end; ++iter)
- {
- header << (*iter).first << ": " << ((*iter).second).asString()
- << "\r\n";
- }
- }
-
- header << "\r\n";
- return header.str();
-}
-
-// static
-bool LLMail::send(
- const std::string& header,
- const std::string& raw_message,
- const char* from_address,
- const char* to_address)
-{
- if(!from_address || !to_address)
- {
- LL_INFOS() << "send_mail reject: missing to and/or from address."
- << LL_ENDL;
- return false;
- }
-
- // remove any "." SMTP commands to prevent injection (DEV-35777)
- // we don't need to worry about "\r\n.\r\n" because of the
- // "\n" --> "\n\n" conversion going into rfc2822_msg below
- std::string message = raw_message;
- std::string bad_string = "\n.\n";
- std::string good_string = "\n..\n";
- while (1)
- {
- int index = message.find(bad_string);
- if (index == std::string::npos) break;
- message.replace(index, bad_string.size(), good_string);
- }
-
- // convert all "\n" into "\r\n"
- 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)
- {
- LL_INFOS() << "send_mail reject: mail system is disabled: to=<"
- << to_address << ">, from=<" << from_address
- << ">" << LL_ENDL;
- // Any future interface to SMTP should return this as an
- // error. --mark
- return true;
- }
- if(!gSockAddr)
- {
- LL_WARNS() << "send_mail reject: mail system not initialized: to=<"
- << to_address << ">, from=<" << from_address
- << ">" << LL_ENDL;
- return false;
- }
-
- if(!connect_smtp())
- {
- LL_WARNS() << "send_mail reject: SMTP connect failure: to=<"
- << to_address << ">, from=<" << from_address
- << ">" << LL_ENDL;
- 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))
- {
- LL_WARNS() << "send_mail socket failure: unable to write "
- << "to=<" << to_address
- << ">, from=<" << from_address << ">"
- << ", bytes=" << original_size
- << ", sent=" << send_size << LL_ENDL;
- return false;
- }
- if(send_size >= LL_MAX_KNOWN_GOOD_MAIL_SIZE)
- {
- LL_WARNS() << "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." << LL_ENDL;
- }
- LL_DEBUGS() << "send_mail success: "
- << "to=<" << to_address
- << ">, from=<" << from_address << ">"
- << ", bytes=" << original_size
- << ", sent=" << send_size << LL_ENDL;
-
-#if LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND
- LL_INFOS() << rfc2822_msg.str() << LL_ENDL;
-#endif
- return true;
-}
-
-
-// static
-std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id,
- const LLUUID& to_agent_id,
- U32 time,
- const U8* secret,
- size_t secret_size)
-{
-#if LL_WINDOWS
- return "blowfish-not-supported-on-windows";
-#else
- size_t data_size = 4 + UUID_BYTES + UUID_BYTES;
- // Convert input data into a binary blob
- std::vector<U8> data;
- data.resize(data_size);
- // *NOTE: This may suffer from endian issues. Could be htolememcpy.
- memcpy(&data[0], &time, 4);
- memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES);
- memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES);
-
- // Encrypt the blob
- LLBlowfishCipher cipher(secret, secret_size);
- size_t encrypted_size = cipher.requiredEncryptionSpace(data.size());
- U8* encrypted = new U8[encrypted_size];
- cipher.encrypt(&data[0], data_size, encrypted, encrypted_size);
-
- std::string address = LLBase32::encode(encrypted, encrypted_size);
-
- // Make it more pretty for humans.
- LLStringUtil::toLower(address);
-
- delete [] encrypted;
-
- return address;
-#endif
-}
+/** + * @file llmail.cpp + * @brief smtp helper functions. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llmail.h" + +// APR on Windows needs full windows headers +#include "llwin32headers.h" +#include <string> +#include <sstream> + +#include "apr_pools.h" +#include "apr_network_io.h" + +#include "llapr.h" +#include "llbase32.h" // IM-to-email address +#include "llblowfishcipher.h" +#include "llerror.h" +#include "llhost.h" +#include "llsd.h" +#include "llstring.h" +#include "lluuid.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; + +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. +// static +bool LLMail::send( + const char* from_name, + const char* from_address, + const char* to_name, + const char* to_address, + const char* subject, + const char* message, + const LLSD& headers) +{ + std::string header = buildSMTPTransaction( + from_name, + from_address, + to_name, + to_address, + subject, + headers); + if(header.empty()) + { + return false; + } + + std::string message_str; + if(message) + { + message_str = message; + } + bool rv = send(header, message_str, to_address, from_address); + if(rv) return true; + return false; +} + +// static +void LLMail::init(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); + } +} + +// static +void LLMail::enable(bool mail_enabled) +{ + gMailEnabled = mail_enabled; +} + +// Test a subject line for RFC2822 compliance. +static bool valid_subject_chars(const char *subject) +{ + for (; *subject != '\0'; subject++) + { + unsigned char c = *subject; + + if (c == '\xa' || c == '\xd' || c > '\x7f') + { + return false; + } + } + + return true; +} + +// static +std::string LLMail::buildSMTPTransaction( + const char* from_name, + const char* from_address, + const char* to_name, + const char* to_address, + const char* subject, + const LLSD& headers) +{ + if(!from_address || !to_address) + { + LL_INFOS() << "send_mail build_smtp_transaction reject: missing to and/or" + << " from address." << LL_ENDL; + return std::string(); + } + if(!valid_subject_chars(subject)) + { + LL_INFOS() << "send_mail build_smtp_transaction reject: bad subject header: " + << "to=<" << to_address + << ">, from=<" << from_address << ">" + << LL_ENDL; + 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"; + + if(headers.isMap()) + { + LLSD::map_const_iterator iter = headers.beginMap(); + LLSD::map_const_iterator end = headers.endMap(); + for(; iter != end; ++iter) + { + header << (*iter).first << ": " << ((*iter).second).asString() + << "\r\n"; + } + } + + header << "\r\n"; + return header.str(); +} + +// static +bool LLMail::send( + const std::string& header, + const std::string& raw_message, + const char* from_address, + const char* to_address) +{ + if(!from_address || !to_address) + { + LL_INFOS() << "send_mail reject: missing to and/or from address." + << LL_ENDL; + return false; + } + + // remove any "." SMTP commands to prevent injection (DEV-35777) + // we don't need to worry about "\r\n.\r\n" because of the + // "\n" --> "\n\n" conversion going into rfc2822_msg below + std::string message = raw_message; + std::string bad_string = "\n.\n"; + std::string good_string = "\n..\n"; + while (1) + { + int index = message.find(bad_string); + if (index == std::string::npos) break; + message.replace(index, bad_string.size(), good_string); + } + + // convert all "\n" into "\r\n" + 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) + { + LL_INFOS() << "send_mail reject: mail system is disabled: to=<" + << to_address << ">, from=<" << from_address + << ">" << LL_ENDL; + // Any future interface to SMTP should return this as an + // error. --mark + return true; + } + if(!gSockAddr) + { + LL_WARNS() << "send_mail reject: mail system not initialized: to=<" + << to_address << ">, from=<" << from_address + << ">" << LL_ENDL; + return false; + } + + if(!connect_smtp()) + { + LL_WARNS() << "send_mail reject: SMTP connect failure: to=<" + << to_address << ">, from=<" << from_address + << ">" << LL_ENDL; + 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)) + { + LL_WARNS() << "send_mail socket failure: unable to write " + << "to=<" << to_address + << ">, from=<" << from_address << ">" + << ", bytes=" << original_size + << ", sent=" << send_size << LL_ENDL; + return false; + } + if(send_size >= LL_MAX_KNOWN_GOOD_MAIL_SIZE) + { + LL_WARNS() << "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." << LL_ENDL; + } + LL_DEBUGS() << "send_mail success: " + << "to=<" << to_address + << ">, from=<" << from_address << ">" + << ", bytes=" << original_size + << ", sent=" << send_size << LL_ENDL; + +#if LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND + LL_INFOS() << rfc2822_msg.str() << LL_ENDL; +#endif + return true; +} + + +// static +std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id, + const LLUUID& to_agent_id, + U32 time, + const U8* secret, + size_t secret_size) +{ +#if LL_WINDOWS + return "blowfish-not-supported-on-windows"; +#else + size_t data_size = 4 + UUID_BYTES + UUID_BYTES; + // Convert input data into a binary blob + std::vector<U8> data; + data.resize(data_size); + // *NOTE: This may suffer from endian issues. Could be htolememcpy. + memcpy(&data[0], &time, 4); + memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES); + memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES); + + // Encrypt the blob + LLBlowfishCipher cipher(secret, secret_size); + size_t encrypted_size = cipher.requiredEncryptionSpace(data.size()); + U8* encrypted = new U8[encrypted_size]; + cipher.encrypt(&data[0], data_size, encrypted, encrypted_size); + + std::string address = LLBase32::encode(encrypted, encrypted_size); + + // Make it more pretty for humans. + LLStringUtil::toLower(address); + + delete [] encrypted; + + return address; +#endif +} diff --git a/indra/llmessage/llmail.h b/indra/llmessage/llmail.h index 1523e7b5c6..d67b89d1ea 100644 --- a/indra/llmessage/llmail.h +++ b/indra/llmessage/llmail.h @@ -1,130 +1,130 @@ -/**
- * @file llmail.h
- * @brief smtp helper functions.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLMAIL_H
-#define LL_LLMAIL_H
-
-typedef struct apr_pool_t apr_pool_t;
-
-#include "llsd.h"
-
-class LLMail
-{
-public:
- // if hostname is NULL, then the host is resolved as 'mail'
- static void init(const std::string& hostname, apr_pool_t* pool);
-
- // Allow all email transmission to be disabled/enabled.
- static void enable(bool mail_enabled);
-
- /**
- * @brief send an email
- * @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_address The email recipient address
- * @param subject The subject of the email
- * @param headers optional X-Foo headers in an llsd map.
- * @return Returns true if the call succeeds, false otherwise.
- *
- * Results in:
- * From: "from_name" <from_address>
- * To: "to_name" <to_address>
- * Subject: subject
- *
- * message
- */
- static bool send(
- const char* from_name,
- const char* from_address,
- const char* to_name,
- const char* to_address,
- const char* subject,
- const char* message,
- const LLSD& headers = LLSD());
-
- /**
- * @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_address The email recipient address
- * @param subject The subject of the email
- * @param headers optional X-Foo headers in an llsd map.
- * @return Returns the complete SMTP transaction mail header.
- */
- static std::string buildSMTPTransaction(
- const char* from_name,
- const char* from_address,
- const char* to_name,
- const char* to_address,
- const char* subject,
- const LLSD& headers = LLSD());
-
- /**
- * @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.
- */
- static bool send(
- const std::string& header,
- const std::string& message,
- const char* from_address,
- const char* to_address);
-
- // IM-to-email sessions use a "session id" based on an encrypted
- // combination of from agent_id, to agent_id, and timestamp. When
- // a user replies to an email we use the from_id to determine the
- // sender's name and the to_id to route the message. The address
- // is encrypted to prevent users from building addresses to spoof
- // IMs from other users. The timestamps allow the "sessions" to
- // expire, in case one of the sessions is stolen/hijacked.
- //
- // indra/tools/mailglue is responsible for parsing the inbound mail.
- //
- // secret: binary blob passed to blowfish, max length 56 bytes
- // secret_size: length of blob, in bytes
- //
- // Returns: "base64" encoded email local-part, with _ and - as the
- // non-alphanumeric characters. This allows better compatibility
- // with email systems than the default / and + extra chars. JC
- static std::string encryptIMEmailAddress(
- const LLUUID& from_agent_id,
- const LLUUID& to_agent_id,
- U32 time,
- const U8* secret,
- size_t secret_size);
-};
-
-extern const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE;
-
-#endif
+/** + * @file llmail.h + * @brief smtp helper functions. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMAIL_H +#define LL_LLMAIL_H + +typedef struct apr_pool_t apr_pool_t; + +#include "llsd.h" + +class LLMail +{ +public: + // if hostname is NULL, then the host is resolved as 'mail' + static void init(const std::string& hostname, apr_pool_t* pool); + + // Allow all email transmission to be disabled/enabled. + static void enable(bool mail_enabled); + + /** + * @brief send an email + * @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_address The email recipient address + * @param subject The subject of the email + * @param headers optional X-Foo headers in an llsd map. + * @return Returns true if the call succeeds, false otherwise. + * + * Results in: + * From: "from_name" <from_address> + * To: "to_name" <to_address> + * Subject: subject + * + * message + */ + static bool send( + const char* from_name, + const char* from_address, + const char* to_name, + const char* to_address, + const char* subject, + const char* message, + const LLSD& headers = LLSD()); + + /** + * @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_address The email recipient address + * @param subject The subject of the email + * @param headers optional X-Foo headers in an llsd map. + * @return Returns the complete SMTP transaction mail header. + */ + static std::string buildSMTPTransaction( + const char* from_name, + const char* from_address, + const char* to_name, + const char* to_address, + const char* subject, + const LLSD& headers = LLSD()); + + /** + * @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. + */ + static bool send( + const std::string& header, + const std::string& message, + const char* from_address, + const char* to_address); + + // IM-to-email sessions use a "session id" based on an encrypted + // combination of from agent_id, to agent_id, and timestamp. When + // a user replies to an email we use the from_id to determine the + // sender's name and the to_id to route the message. The address + // is encrypted to prevent users from building addresses to spoof + // IMs from other users. The timestamps allow the "sessions" to + // expire, in case one of the sessions is stolen/hijacked. + // + // indra/tools/mailglue is responsible for parsing the inbound mail. + // + // secret: binary blob passed to blowfish, max length 56 bytes + // secret_size: length of blob, in bytes + // + // Returns: "base64" encoded email local-part, with _ and - as the + // non-alphanumeric characters. This allows better compatibility + // with email systems than the default / and + extra chars. JC + static std::string encryptIMEmailAddress( + const LLUUID& from_agent_id, + const LLUUID& to_agent_id, + U32 time, + const U8* secret, + size_t secret_size); +}; + +extern const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE; + +#endif diff --git a/indra/llmessage/llmessagebuilder.h b/indra/llmessage/llmessagebuilder.h index 2acb47c238..3f04857945 100644 --- a/indra/llmessage/llmessagebuilder.h +++ b/indra/llmessage/llmessagebuilder.h @@ -1,101 +1,101 @@ -/**
- * @file llmessagebuilder.h
- * @brief Declaration of LLMessageBuilder class.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLMESSAGEBUILDER_H
-#define LL_LLMESSAGEBUILDER_H
-
-#include <string>
-
-#include "stdtypes.h"
-
-class LLMsgData;
-class LLQuaternion;
-class LLSD;
-class LLUUID;
-class LLVector3;
-class LLVector3d;
-class LLVector4;
-
-class LLMessageBuilder
-{
-public:
-
- //CLASS_LOG_TYPE(LLMessageBuilder);
-
- virtual ~LLMessageBuilder();
- virtual void newMessage(const char* name) = 0;
-
- virtual void nextBlock(const char* blockname) = 0;
- virtual bool removeLastBlock() = 0; // TODO: babbage: remove this horror
-
- /** All add* methods expect pointers to canonical strings. */
- virtual void addBinaryData(
- const char* varname,
- const void* data,
- S32 size) = 0;
- virtual void addBOOL(const char* varname, bool b) = 0;
- virtual void addS8(const char* varname, S8 s) = 0;
- virtual void addU8(const char* varname, U8 u) = 0;
- virtual void addS16(const char* varname, S16 i) = 0;
- virtual void addU16(const char* varname, U16 i) = 0;
- virtual void addF32(const char* varname, F32 f) = 0;
- virtual void addS32(const char* varname, S32 s) = 0;
- virtual void addU32(const char* varname, U32 u) = 0;
- virtual void addU64(const char* varname, U64 lu) = 0;
- virtual void addF64(const char* varname, F64 d) = 0;
- virtual void addVector3(const char* varname, const LLVector3& vec) = 0;
- virtual void addVector4(const char* varname, const LLVector4& vec) = 0;
- virtual void addVector3d(const char* varname, const LLVector3d& vec) = 0;
- virtual void addQuat(const char* varname, const LLQuaternion& quat) = 0;
- virtual void addUUID(const char* varname, const LLUUID& uuid) = 0;
- virtual void addIPAddr(const char* varname, const U32 ip) = 0;
- virtual void addIPPort(const char* varname, const U16 port) = 0;
- virtual void addString(const char* varname, const char* s) = 0;
- virtual void addString(const char* varname, const std::string& s) = 0;
-
- virtual bool isMessageFull(const char* blockname) const = 0;
- virtual void compressMessage(U8*& buf_ptr, U32& buffer_length) = 0;
- virtual S32 getMessageSize() = 0;
-
- virtual bool isBuilt() const = 0;
- virtual bool isClear() const = 0;
- virtual U32 buildMessage(
- U8* buffer,
- U32 buffer_size,
- U8 offset_to_data) = 0;
- /**< Return built message size */
- virtual void clearMessage() = 0;
-
- // TODO: babbage: remove this horror
- virtual void setBuilt(bool b) = 0;
-
- virtual const char* getMessageName() const = 0;
-
- virtual void copyFromMessageData(const LLMsgData& data) = 0;
- virtual void copyFromLLSD(const LLSD& data) = 0;
-};
-
-#endif // LL_LLMESSAGEBUILDER_H
+/** + * @file llmessagebuilder.h + * @brief Declaration of LLMessageBuilder class. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMESSAGEBUILDER_H +#define LL_LLMESSAGEBUILDER_H + +#include <string> + +#include "stdtypes.h" + +class LLMsgData; +class LLQuaternion; +class LLSD; +class LLUUID; +class LLVector3; +class LLVector3d; +class LLVector4; + +class LLMessageBuilder +{ +public: + + //CLASS_LOG_TYPE(LLMessageBuilder); + + virtual ~LLMessageBuilder(); + virtual void newMessage(const char* name) = 0; + + virtual void nextBlock(const char* blockname) = 0; + virtual bool removeLastBlock() = 0; // TODO: babbage: remove this horror + + /** All add* methods expect pointers to canonical strings. */ + virtual void addBinaryData( + const char* varname, + const void* data, + S32 size) = 0; + virtual void addBOOL(const char* varname, bool b) = 0; + virtual void addS8(const char* varname, S8 s) = 0; + virtual void addU8(const char* varname, U8 u) = 0; + virtual void addS16(const char* varname, S16 i) = 0; + virtual void addU16(const char* varname, U16 i) = 0; + virtual void addF32(const char* varname, F32 f) = 0; + virtual void addS32(const char* varname, S32 s) = 0; + virtual void addU32(const char* varname, U32 u) = 0; + virtual void addU64(const char* varname, U64 lu) = 0; + virtual void addF64(const char* varname, F64 d) = 0; + virtual void addVector3(const char* varname, const LLVector3& vec) = 0; + virtual void addVector4(const char* varname, const LLVector4& vec) = 0; + virtual void addVector3d(const char* varname, const LLVector3d& vec) = 0; + virtual void addQuat(const char* varname, const LLQuaternion& quat) = 0; + virtual void addUUID(const char* varname, const LLUUID& uuid) = 0; + virtual void addIPAddr(const char* varname, const U32 ip) = 0; + virtual void addIPPort(const char* varname, const U16 port) = 0; + virtual void addString(const char* varname, const char* s) = 0; + virtual void addString(const char* varname, const std::string& s) = 0; + + virtual bool isMessageFull(const char* blockname) const = 0; + virtual void compressMessage(U8*& buf_ptr, U32& buffer_length) = 0; + virtual S32 getMessageSize() = 0; + + virtual bool isBuilt() const = 0; + virtual bool isClear() const = 0; + virtual U32 buildMessage( + U8* buffer, + U32 buffer_size, + U8 offset_to_data) = 0; + /**< Return built message size */ + virtual void clearMessage() = 0; + + // TODO: babbage: remove this horror + virtual void setBuilt(bool b) = 0; + + virtual const char* getMessageName() const = 0; + + virtual void copyFromMessageData(const LLMsgData& data) = 0; + virtual void copyFromLLSD(const LLSD& data) = 0; +}; + +#endif // LL_LLMESSAGEBUILDER_H diff --git a/indra/llmessage/llmessagereader.cpp b/indra/llmessage/llmessagereader.cpp index 80c4379549..4d4bf679ed 100644 --- a/indra/llmessage/llmessagereader.cpp +++ b/indra/llmessage/llmessagereader.cpp @@ -1,62 +1,62 @@ -/**
- * @file llmessagereader.cpp
- * @brief LLMessageReader class implementation
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llmessagereader.h"
-
-static bool sTimeDecodes = false;
-
-static F32 sTimeDecodesSpamThreshold = 0.05f;
-
-//virtual
-LLMessageReader::~LLMessageReader()
-{
- // even abstract base classes need a concrete destructor
-}
-
-//static
-void LLMessageReader::setTimeDecodes(bool b)
-{
- sTimeDecodes = b;
-}
-
-//static
-void LLMessageReader::setTimeDecodesSpamThreshold(F32 seconds)
-{
- sTimeDecodesSpamThreshold = seconds;
-}
-
-//static
-bool LLMessageReader::getTimeDecodes()
-{
- return sTimeDecodes;
-}
-
-//static
-F32 LLMessageReader::getTimeDecodesSpamThreshold()
-{
- return sTimeDecodesSpamThreshold;
-}
+/** + * @file llmessagereader.cpp + * @brief LLMessageReader class implementation + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llmessagereader.h" + +static bool sTimeDecodes = false; + +static F32 sTimeDecodesSpamThreshold = 0.05f; + +//virtual +LLMessageReader::~LLMessageReader() +{ + // even abstract base classes need a concrete destructor +} + +//static +void LLMessageReader::setTimeDecodes(bool b) +{ + sTimeDecodes = b; +} + +//static +void LLMessageReader::setTimeDecodesSpamThreshold(F32 seconds) +{ + sTimeDecodesSpamThreshold = seconds; +} + +//static +bool LLMessageReader::getTimeDecodes() +{ + return sTimeDecodes; +} + +//static +F32 LLMessageReader::getTimeDecodesSpamThreshold() +{ + return sTimeDecodesSpamThreshold; +} diff --git a/indra/llmessage/llmessagereader.h b/indra/llmessage/llmessagereader.h index 42d8906e16..5bc9aea5e4 100644 --- a/indra/llmessage/llmessagereader.h +++ b/indra/llmessage/llmessagereader.h @@ -1,94 +1,94 @@ -/**
- * @file llmessagereader.h
- * @brief Declaration of LLMessageReader class.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLMESSAGEREADER_H
-#define LL_LLMESSAGEREADER_H
-
-#include "stdtypes.h"
-
-class LLHost;
-class LLMessageBuilder;
-class LLMsgData;
-class LLQuaternion;
-class LLUUID;
-class LLVector3;
-class LLVector3d;
-class LLVector4;
-
-// Error return values for getSize() functions
-const S32 LL_BLOCK_NOT_IN_MESSAGE = -1;
-const S32 LL_VARIABLE_NOT_IN_BLOCK = -2;
-const S32 LL_MESSAGE_ERROR = -3;
-
-
-class LLMessageReader
-{
- public:
-
- virtual ~LLMessageReader();
-
- /** All get* methods expect pointers to canonical strings. */
- virtual void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX) = 0;
- virtual void getBOOL(const char *block, const char *var, bool &data, S32 blocknum = 0) = 0;
- virtual void getS8(const char *block, const char *var, S8 &data, S32 blocknum = 0) = 0;
- virtual void getU8(const char *block, const char *var, U8 &data, S32 blocknum = 0) = 0;
- virtual void getS16(const char *block, const char *var, S16 &data, S32 blocknum = 0) = 0;
- virtual void getU16(const char *block, const char *var, U16 &data, S32 blocknum = 0) = 0;
- virtual void getS32(const char *block, const char *var, S32 &data, S32 blocknum = 0) = 0;
- virtual void getF32(const char *block, const char *var, F32 &data, S32 blocknum = 0) = 0;
- virtual void getU32(const char *block, const char *var, U32 &data, S32 blocknum = 0) = 0;
- virtual void getU64(const char *block, const char *var, U64 &data, S32 blocknum = 0) = 0;
- virtual void getF64(const char *block, const char *var, F64 &data, S32 blocknum = 0) = 0;
- virtual void getVector3(const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0) = 0;
- virtual void getVector4(const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0) = 0;
- virtual void getVector3d(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0) = 0;
- virtual void getQuat(const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0) = 0;
- virtual void getUUID(const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0) = 0;
- virtual void getIPAddr(const char *block, const char *var, U32 &ip, S32 blocknum = 0) = 0;
- virtual void getIPPort(const char *block, const char *var, U16 &port, S32 blocknum = 0) = 0;
- virtual void getString(const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0) = 0;
- virtual void getString(const char *block, const char *var, std::string& outstr, S32 blocknum = 0) = 0;
-
- virtual S32 getNumberOfBlocks(const char *blockname) = 0;
- virtual S32 getSize(const char *blockname, const char *varname) = 0;
- virtual S32 getSize(const char *blockname, S32 blocknum, const char *varname) = 0;
-
- virtual void clearMessage() = 0;
-
- /** Returns pointer to canonical (prehashed) string. */
- virtual const char* getMessageName() const = 0;
- virtual S32 getMessageSize() const = 0;
-
- virtual void copyToBuilder(LLMessageBuilder&) const = 0;
-
-
- static void setTimeDecodes(bool b);
- static bool getTimeDecodes();
- static void setTimeDecodesSpamThreshold(F32 seconds);
- static F32 getTimeDecodesSpamThreshold();
-};
-
-#endif // LL_LLMESSAGEREADER_H
+/** + * @file llmessagereader.h + * @brief Declaration of LLMessageReader class. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMESSAGEREADER_H +#define LL_LLMESSAGEREADER_H + +#include "stdtypes.h" + +class LLHost; +class LLMessageBuilder; +class LLMsgData; +class LLQuaternion; +class LLUUID; +class LLVector3; +class LLVector3d; +class LLVector4; + +// Error return values for getSize() functions +const S32 LL_BLOCK_NOT_IN_MESSAGE = -1; +const S32 LL_VARIABLE_NOT_IN_BLOCK = -2; +const S32 LL_MESSAGE_ERROR = -3; + + +class LLMessageReader +{ + public: + + virtual ~LLMessageReader(); + + /** All get* methods expect pointers to canonical strings. */ + virtual void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX) = 0; + virtual void getBOOL(const char *block, const char *var, bool &data, S32 blocknum = 0) = 0; + virtual void getS8(const char *block, const char *var, S8 &data, S32 blocknum = 0) = 0; + virtual void getU8(const char *block, const char *var, U8 &data, S32 blocknum = 0) = 0; + virtual void getS16(const char *block, const char *var, S16 &data, S32 blocknum = 0) = 0; + virtual void getU16(const char *block, const char *var, U16 &data, S32 blocknum = 0) = 0; + virtual void getS32(const char *block, const char *var, S32 &data, S32 blocknum = 0) = 0; + virtual void getF32(const char *block, const char *var, F32 &data, S32 blocknum = 0) = 0; + virtual void getU32(const char *block, const char *var, U32 &data, S32 blocknum = 0) = 0; + virtual void getU64(const char *block, const char *var, U64 &data, S32 blocknum = 0) = 0; + virtual void getF64(const char *block, const char *var, F64 &data, S32 blocknum = 0) = 0; + virtual void getVector3(const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0) = 0; + virtual void getVector4(const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0) = 0; + virtual void getVector3d(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0) = 0; + virtual void getQuat(const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0) = 0; + virtual void getUUID(const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0) = 0; + virtual void getIPAddr(const char *block, const char *var, U32 &ip, S32 blocknum = 0) = 0; + virtual void getIPPort(const char *block, const char *var, U16 &port, S32 blocknum = 0) = 0; + virtual void getString(const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0) = 0; + virtual void getString(const char *block, const char *var, std::string& outstr, S32 blocknum = 0) = 0; + + virtual S32 getNumberOfBlocks(const char *blockname) = 0; + virtual S32 getSize(const char *blockname, const char *varname) = 0; + virtual S32 getSize(const char *blockname, S32 blocknum, const char *varname) = 0; + + virtual void clearMessage() = 0; + + /** Returns pointer to canonical (prehashed) string. */ + virtual const char* getMessageName() const = 0; + virtual S32 getMessageSize() const = 0; + + virtual void copyToBuilder(LLMessageBuilder&) const = 0; + + + static void setTimeDecodes(bool b); + static bool getTimeDecodes(); + static void setTimeDecodesSpamThreshold(F32 seconds); + static F32 getTimeDecodesSpamThreshold(); +}; + +#endif // LL_LLMESSAGEREADER_H diff --git a/indra/llmessage/llmessagetemplate.h b/indra/llmessage/llmessagetemplate.h index 454bae7238..e91d4346b6 100644 --- a/indra/llmessage/llmessagetemplate.h +++ b/indra/llmessage/llmessagetemplate.h @@ -1,421 +1,421 @@ -/**
- * @file llmessagetemplate.h
- * @brief Declaration of the message template classes.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLMESSAGETEMPLATE_H
-#define LL_LLMESSAGETEMPLATE_H
-
-#include "message.h" // TODO: babbage: Remove...
-#include "llstl.h"
-#include "llindexedvector.h"
-
-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; }
- const void *getData() const { return (const 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) : 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 mBlockNumber;
- typedef LLIndexedVector<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());
- mMemberBlocks.clear();
- }
-
- 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:
- typedef std::map<char*, LLMsgBlkData*> msg_blk_data_map_t;
- msg_blk_data_map_t mMemberBlocks;
- char *mName;
- S32 mTotalSize;
-};
-
-// 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(const char *name, const EMsgVariableType type, const S32 size) : mType(type), mSize(size)
- {
- mName = LLMessageStringTable::getInstance()->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(const char *name, EMsgBlockType type, S32 number = 1) : mType(type), mNumber(number), mTotalSize(0)
- {
- mName = LLMessageStringTable::getInstance()->getString(name);
- }
-
- ~LLMessageBlock()
- {
- for_each(mMemberVariables.begin(), mMemberVariables.end(), DeletePointer());
- }
-
- void addVariable(char *name, const EMsgVariableType type, const S32 size)
- {
- LLMessageVariable** varp = &mMemberVariables[name];
- if (*varp != NULL)
- {
- LL_ERRS() << name << " has already been used as a variable name!" << LL_ENDL;
- }
- *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();
- }
-
- const LLMessageVariable* getVariable(char* name) const
- {
- message_variable_map_t::const_iterator iter = mMemberVariables.find(name);
- return iter != mMemberVariables.end()? *iter : NULL;
- }
-
- friend std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg);
-
- typedef LLIndexedVector<LLMessageVariable*, const char *, 8> 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
-};
-
-enum EMsgDeprecation
-{
- MD_NOTDEPRECATED,
- MD_UDPDEPRECATED,
- MD_UDPBLACKLISTED,
- MD_DEPRECATED
-};
-
-
-class LLMessageTemplate
-{
-public:
- LLMessageTemplate(const char *name, U32 message_number, EMsgFrequency freq)
- :
- //mMemberBlocks(),
- mName(NULL),
- mFrequency(freq),
- mTrust(MT_NOTRUST),
- mEncoding(ME_ZEROCODED),
- mDeprecation(MD_NOTDEPRECATED),
- 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 = LLMessageStringTable::getInstance()->getString(name);
- }
-
- ~LLMessageTemplate()
- {
- for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePointer());
- }
-
- void addBlock(LLMessageBlock *blockp)
- {
- LLMessageBlock** member_blockp = &mMemberBlocks[blockp->mName];
- if (*member_blockp != NULL)
- {
- LL_ERRS() << "Block " << blockp->mName
- << "has already been used as a block name!" << LL_ENDL;
- }
- *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) const
- {
- return mTrust;
- }
-
- // controls for how the message should be encoded
- void setEncoding(EMsgEncoding e)
- {
- mEncoding = e;
- }
- EMsgEncoding getEncoding() const
- {
- return mEncoding;
- }
-
- void setDeprecation(EMsgDeprecation d)
- {
- mDeprecation = d;
- }
-
- EMsgDeprecation getDeprecation() const
- {
- return mDeprecation;
- }
-
- void setHandlerFunc(void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
- {
- mHandlerFunc = handler_func;
- mUserData = user_data;
- }
-
- bool callHandlerFunc(LLMessageSystem *msgsystem) const
- {
- if (mHandlerFunc)
- {
- mHandlerFunc(msgsystem, mUserData);
- return true;
- }
- return false;
- }
-
- bool isUdpBanned() const
- {
- return mDeprecation == MD_UDPBLACKLISTED;
- }
-
- void banUdp();
-
- bool isBanned(bool trustedSource) const
- {
- return trustedSource ? mBanFromTrusted : mBanFromUntrusted;
- }
-
- friend std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg);
-
- const LLMessageBlock* getBlock(char* name) const
- {
- message_block_map_t::const_iterator iter = mMemberBlocks.find(name);
- return iter != mMemberBlocks.end()? *iter : NULL;
- }
-
-public:
- typedef LLIndexedVector<LLMessageBlock*, char*, 8> message_block_map_t;
- message_block_map_t mMemberBlocks;
- char *mName;
- EMsgFrequency mFrequency;
- EMsgTrust mTrust;
- EMsgEncoding mEncoding;
- EMsgDeprecation mDeprecation;
- 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;
-};
-
-#endif // LL_LLMESSAGETEMPLATE_H
+/** + * @file llmessagetemplate.h + * @brief Declaration of the message template classes. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMESSAGETEMPLATE_H +#define LL_LLMESSAGETEMPLATE_H + +#include "message.h" // TODO: babbage: Remove... +#include "llstl.h" +#include "llindexedvector.h" + +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; } + const void *getData() const { return (const 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) : 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 mBlockNumber; + typedef LLIndexedVector<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()); + mMemberBlocks.clear(); + } + + 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: + typedef std::map<char*, LLMsgBlkData*> msg_blk_data_map_t; + msg_blk_data_map_t mMemberBlocks; + char *mName; + S32 mTotalSize; +}; + +// 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(const char *name, const EMsgVariableType type, const S32 size) : mType(type), mSize(size) + { + mName = LLMessageStringTable::getInstance()->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(const char *name, EMsgBlockType type, S32 number = 1) : mType(type), mNumber(number), mTotalSize(0) + { + mName = LLMessageStringTable::getInstance()->getString(name); + } + + ~LLMessageBlock() + { + for_each(mMemberVariables.begin(), mMemberVariables.end(), DeletePointer()); + } + + void addVariable(char *name, const EMsgVariableType type, const S32 size) + { + LLMessageVariable** varp = &mMemberVariables[name]; + if (*varp != NULL) + { + LL_ERRS() << name << " has already been used as a variable name!" << LL_ENDL; + } + *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(); + } + + const LLMessageVariable* getVariable(char* name) const + { + message_variable_map_t::const_iterator iter = mMemberVariables.find(name); + return iter != mMemberVariables.end()? *iter : NULL; + } + + friend std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg); + + typedef LLIndexedVector<LLMessageVariable*, const char *, 8> 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 +}; + +enum EMsgDeprecation +{ + MD_NOTDEPRECATED, + MD_UDPDEPRECATED, + MD_UDPBLACKLISTED, + MD_DEPRECATED +}; + + +class LLMessageTemplate +{ +public: + LLMessageTemplate(const char *name, U32 message_number, EMsgFrequency freq) + : + //mMemberBlocks(), + mName(NULL), + mFrequency(freq), + mTrust(MT_NOTRUST), + mEncoding(ME_ZEROCODED), + mDeprecation(MD_NOTDEPRECATED), + 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 = LLMessageStringTable::getInstance()->getString(name); + } + + ~LLMessageTemplate() + { + for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePointer()); + } + + void addBlock(LLMessageBlock *blockp) + { + LLMessageBlock** member_blockp = &mMemberBlocks[blockp->mName]; + if (*member_blockp != NULL) + { + LL_ERRS() << "Block " << blockp->mName + << "has already been used as a block name!" << LL_ENDL; + } + *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) const + { + return mTrust; + } + + // controls for how the message should be encoded + void setEncoding(EMsgEncoding e) + { + mEncoding = e; + } + EMsgEncoding getEncoding() const + { + return mEncoding; + } + + void setDeprecation(EMsgDeprecation d) + { + mDeprecation = d; + } + + EMsgDeprecation getDeprecation() const + { + return mDeprecation; + } + + void setHandlerFunc(void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data) + { + mHandlerFunc = handler_func; + mUserData = user_data; + } + + bool callHandlerFunc(LLMessageSystem *msgsystem) const + { + if (mHandlerFunc) + { + mHandlerFunc(msgsystem, mUserData); + return true; + } + return false; + } + + bool isUdpBanned() const + { + return mDeprecation == MD_UDPBLACKLISTED; + } + + void banUdp(); + + bool isBanned(bool trustedSource) const + { + return trustedSource ? mBanFromTrusted : mBanFromUntrusted; + } + + friend std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg); + + const LLMessageBlock* getBlock(char* name) const + { + message_block_map_t::const_iterator iter = mMemberBlocks.find(name); + return iter != mMemberBlocks.end()? *iter : NULL; + } + +public: + typedef LLIndexedVector<LLMessageBlock*, char*, 8> message_block_map_t; + message_block_map_t mMemberBlocks; + char *mName; + EMsgFrequency mFrequency; + EMsgTrust mTrust; + EMsgEncoding mEncoding; + EMsgDeprecation mDeprecation; + 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; +}; + +#endif // LL_LLMESSAGETEMPLATE_H diff --git a/indra/llmessage/llmessagetemplateparser.cpp b/indra/llmessage/llmessagetemplateparser.cpp index 274ec3e273..cf1f49116e 100644 --- a/indra/llmessage/llmessagetemplateparser.cpp +++ b/indra/llmessage/llmessagetemplateparser.cpp @@ -1,761 +1,761 @@ -/**
- * @file llmessagetemplateparser.cpp
- * @brief LLMessageTemplateParser implementation
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llmessagetemplateparser.h"
-#include <boost/tokenizer.hpp>
-
-
-// What follows is a bunch of C functions to do validation.
-
-// 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(const char *token, const char *regexp)
-{
- S32 tptr, rptr = 0;
- S32 current_checker, next_checker = 0;
-
- current_checker = get_checker_number(regexp[rptr++]);
-
- if (current_checker == -1)
- {
- LL_ERRS() << "Invalid regular expression value!" << LL_ENDL;
- return false;
- }
-
- if (current_checker == 9999)
- {
- LL_ERRS() << "Regular expression can't start with *!" << LL_ENDL;
- return false;
- }
-
- for (tptr = 0; token[tptr]; tptr++)
- {
- if (current_checker == -1)
- {
- LL_ERRS() << "Input exceeds regular expression!\nDid you forget a *?" << LL_ENDL;
- 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(const char *token)
-{
- if (!b_check_token(token, "fv*"))
- {
- LL_WARNS() << "Token '" << token << "' isn't a variable!" << LL_ENDL;
- return false;
- }
- return true;
-}
-
-// An integer is made up of the digits 0-9 and may be preceded by a '-'
-bool b_integer_ok(const char *token)
-{
- if (!b_check_token(token, "sd*"))
- {
- LL_WARNS() << "Token isn't an integer!" << LL_ENDL;
- return false;
- }
- return true;
-}
-
-// An integer is made up of the digits 0-9
-bool b_positive_integer_ok(const char *token)
-{
- if (!b_check_token(token, "d*"))
- {
- LL_WARNS() << "Token isn't an integer!" << LL_ENDL;
- return false;
- }
- return true;
-}
-
-
-// Done with C functions, here's the tokenizer.
-
-typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
-
-LLTemplateTokenizer::LLTemplateTokenizer(const std::string & contents) : mStarted(false), mTokens()
-{
- boost::char_separator<char> newline("\r\n", "", boost::keep_empty_tokens);
- boost::char_separator<char> spaces(" \t");
- U32 line_counter = 1;
-
- tokenizer line_tokens(contents, newline);
- for(tokenizer::iterator line_iter = line_tokens.begin();
- line_iter != line_tokens.end();
- ++line_iter, ++line_counter)
- {
- tokenizer word_tokens(*line_iter, spaces);
- for(tokenizer::iterator word_iter = word_tokens.begin();
- word_iter != word_tokens.end();
- ++word_iter)
- {
- if((*word_iter)[0] == '/')
- {
- break; // skip to end of line on comments
- }
- positioned_token pt;// = new positioned_token();
- pt.str = std::string(*word_iter);
- pt.line = line_counter;
- mTokens.push_back(pt);
- }
- }
- mCurrent = mTokens.begin();
-}
-void LLTemplateTokenizer::inc()
-{
- if(atEOF())
- {
- error("trying to increment token of EOF");
- }
- else if(mStarted)
- {
- ++mCurrent;
- }
- else
- {
- mStarted = true;
- mCurrent = mTokens.begin();
- }
-}
-void LLTemplateTokenizer::dec()
-{
- if(mCurrent == mTokens.begin())
- {
- if(mStarted)
- {
- mStarted = false;
- }
- else
- {
- error("trying to decrement past beginning of file");
- }
- }
- else
- {
- mCurrent--;
- }
-}
-
-std::string LLTemplateTokenizer::get() const
-{
- if(atEOF())
- {
- error("trying to get EOF");
- }
- return mCurrent->str;
-}
-
-U32 LLTemplateTokenizer::line() const
-{
- if(atEOF())
- {
- return 0;
- }
- return mCurrent->line;
-}
-
-bool LLTemplateTokenizer::atEOF() const
-{
- return mCurrent == mTokens.end();
-}
-
-std::string LLTemplateTokenizer::next()
-{
- inc();
- return get();
-}
-
-bool LLTemplateTokenizer::want(const std::string & token)
-{
- if(atEOF()) return false;
- inc();
- if(atEOF()) return false;
- if(get() != token)
- {
- dec(); // back up a step
- return false;
- }
- return true;
-}
-
-bool LLTemplateTokenizer::wantEOF()
-{
- // see if the next token is EOF
- if(atEOF()) return true;
- inc();
- if(!atEOF())
- {
- dec(); // back up a step
- return false;
- }
- return true;
-}
-
-void LLTemplateTokenizer::error(std::string message) const
-{
- if(atEOF())
- {
- LL_ERRS() << "Unexpected end of file: " << message << LL_ENDL;
- }
- else
- {
- LL_ERRS() << "Problem parsing message template at line "
- << line() << ", with token '" << get() << "' : "
- << message << LL_ENDL;
- }
-}
-
-
-// Done with tokenizer, next is the parser.
-
-LLTemplateParser::LLTemplateParser(LLTemplateTokenizer & tokens):
- mVersion(0.f),
- mMessages()
-{
- // the version number should be the first thing in the file
- if (tokens.want("version"))
- {
- // version number
- std::string vers_string = tokens.next();
- mVersion = (F32)atof(vers_string.c_str());
-
- LL_INFOS() << "### Message template version " << mVersion << " ###" << LL_ENDL;
- }
- else
- {
- LL_ERRS() << "Version must be first in the message template, found "
- << tokens.next() << LL_ENDL;
- }
-
- while(LLMessageTemplate * templatep = parseMessage(tokens))
- {
- if (templatep->getDeprecation() != MD_DEPRECATED)
- {
- mMessages.push_back(templatep);
- }
- else
- {
- delete templatep;
- }
- }
-
- if(!tokens.wantEOF())
- {
- LL_ERRS() << "Expected end of template or a message, instead found: "
- << tokens.next() << " at " << tokens.line() << LL_ENDL;
- }
-}
-
-F32 LLTemplateParser::getVersion() const
-{
- return mVersion;
-}
-
-LLTemplateParser::message_iterator LLTemplateParser::getMessagesBegin() const
-{
- return mMessages.begin();
-}
-
-LLTemplateParser::message_iterator LLTemplateParser::getMessagesEnd() const
-{
- return mMessages.end();
-}
-
-
-// static
-LLMessageTemplate * LLTemplateParser::parseMessage(LLTemplateTokenizer & tokens)
-{
- LLMessageTemplate *templatep = NULL;
- if(!tokens.want("{"))
- {
- return NULL;
- }
-
- // name first
- std::string template_name = tokens.next();
-
- // is name a legit C variable name
- if (!b_variable_ok(template_name.c_str()))
- {
- LL_ERRS() << "Not legit variable name: " << template_name << " at " << tokens.line() << LL_ENDL;
- }
-
- // ok, now get Frequency ("High", "Medium", or "Low")
- EMsgFrequency frequency = MFT_LOW;
- std::string freq_string = tokens.next();
- if (freq_string == "High")
- {
- frequency = MFT_HIGH;
- }
- else if (freq_string == "Medium")
- {
- frequency = MFT_MEDIUM;
- }
- else if (freq_string == "Low" || freq_string == "Fixed")
- {
- frequency = MFT_LOW;
- }
- else
- {
- LL_ERRS() << "Expected frequency, got " << freq_string << " at " << tokens.line() << LL_ENDL;
- }
-
- // TODO more explicit checking here pls
- U32 message_number = strtoul(tokens.next().c_str(),NULL,0);
-
- switch (frequency) {
- case MFT_HIGH:
- break;
- case MFT_MEDIUM:
- message_number = (255 << 8) | message_number;
- break;
- case MFT_LOW:
- message_number = (255 << 24) | (255 << 16) | message_number;
- break;
- default:
- LL_ERRS() << "Unknown frequency enum: " << frequency << LL_ENDL;
- }
-
- templatep = new LLMessageTemplate(
- template_name.c_str(),
- message_number,
- frequency);
-
- // Now get trust ("Trusted", "NotTrusted")
- std::string trust = tokens.next();
- if (trust == "Trusted")
- {
- templatep->setTrust(MT_TRUST);
- }
- else if (trust == "NotTrusted")
- {
- templatep->setTrust(MT_NOTRUST);
- }
- else
- {
- LL_ERRS() << "Bad trust " << trust << " at " << tokens.line() << LL_ENDL;
- }
-
- // get encoding
- std::string encoding = tokens.next();
- if(encoding == "Unencoded")
- {
- templatep->setEncoding(ME_UNENCODED);
- }
- else if(encoding == "Zerocoded")
- {
- templatep->setEncoding(ME_ZEROCODED);
- }
- else
- {
- LL_ERRS() << "Bad encoding " << encoding << " at " << tokens.line() << LL_ENDL;
- }
-
- // get deprecation
- if(tokens.want("Deprecated"))
- {
- templatep->setDeprecation(MD_DEPRECATED);
- }
- else if (tokens.want("UDPDeprecated"))
- {
- templatep->setDeprecation(MD_UDPDEPRECATED);
- }
- else if (tokens.want("UDPBlackListed"))
- {
- templatep->setDeprecation(MD_UDPBLACKLISTED);
- }
- else if (tokens.want("NotDeprecated"))
- {
- // this is the default value, but it can't hurt to set it twice
- templatep->setDeprecation(MD_NOTDEPRECATED);
- }
- else {
- // It's probably a brace, let's just start block processing
- }
-
- while(LLMessageBlock * blockp = parseBlock(tokens))
- {
- templatep->addBlock(blockp);
- }
-
- if(!tokens.want("}"))
- {
- LL_ERRS() << "Expecting closing } for message " << template_name
- << " at " << tokens.line() << LL_ENDL;
- }
- return templatep;
-}
-
-// static
-LLMessageBlock * LLTemplateParser::parseBlock(LLTemplateTokenizer & tokens)
-{
- LLMessageBlock * blockp = NULL;
-
- if(!tokens.want("{"))
- {
- return NULL;
- }
-
- // name first
- std::string block_name = tokens.next();
-
- // is name a legit C variable name
- if (!b_variable_ok(block_name.c_str()))
- {
- LL_ERRS() << "not a legal block name: " << block_name
- << " at " << tokens.line() << LL_ENDL;
- }
-
- // now, block type ("Single", "Multiple", or "Variable")
- std::string block_type = tokens.next();
- // which one is it?
- if (block_type == "Single")
- {
- // ok, we can create a block
- blockp = new LLMessageBlock(block_name.c_str(), MBT_SINGLE);
- }
- else if (block_type == "Multiple")
- {
- // need to get the number of repeats
- std::string repeats = tokens.next();
-
- // is it a legal integer
- if (!b_positive_integer_ok(repeats.c_str()))
- {
- LL_ERRS() << "not a legal integer for block multiple count: "
- << repeats << " at " << tokens.line() << LL_ENDL;
- }
-
- // ok, we can create a block
- blockp = new LLMessageBlock(block_name.c_str(),
- MBT_MULTIPLE,
- atoi(repeats.c_str()));
- }
- else if (block_type == "Variable")
- {
- // ok, we can create a block
- blockp = new LLMessageBlock(block_name.c_str(), MBT_VARIABLE);
- }
- else
- {
- LL_ERRS() << "bad block type: " << block_type
- << " at " << tokens.line() << LL_ENDL;
- }
-
-
- while(LLMessageVariable * varp = parseVariable(tokens))
- {
- blockp->addVariable(varp->getName(),
- varp->getType(),
- varp->getSize());
- delete varp;
- }
-
- if(!tokens.want("}"))
- {
- LL_ERRS() << "Expecting closing } for block " << block_name
- << " at " << tokens.line() << LL_ENDL;
- }
- return blockp;
-
-}
-
-// static
-LLMessageVariable * LLTemplateParser::parseVariable(LLTemplateTokenizer & tokens)
-{
- LLMessageVariable * varp = NULL;
- if(!tokens.want("{"))
- {
- return NULL;
- }
-
- std::string var_name = tokens.next();
-
- if (!b_variable_ok(var_name.c_str()))
- {
- LL_ERRS() << "Not a legit variable name: " << var_name
- << " at " << tokens.line() << LL_ENDL;
- }
-
- std::string var_type = tokens.next();
-
- if (var_type == "U8")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_U8, 1);
- }
- else if (var_type == "U16")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_U16, 2);
- }
- else if (var_type == "U32")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_U32, 4);
- }
- else if (var_type == "U64")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_U64, 8);
- }
- else if (var_type == "S8")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_S8, 1);
- }
- else if (var_type == "S16")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_S16, 2);
- }
- else if (var_type == "S32")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_S32, 4);
- }
- else if (var_type == "S64")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_S64, 8);
- }
- else if (var_type == "F32")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_F32, 4);
- }
- else if (var_type == "F64")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_F64, 8);
- }
- else if (var_type == "LLVector3")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector3, 12);
- }
- else if (var_type == "LLVector3d")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector3d, 24);
- }
- else if (var_type == "LLVector4")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector4, 16);
- }
- else if (var_type == "LLQuaternion")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_LLQuaternion, 12);
- }
- else if (var_type == "LLUUID")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_LLUUID, 16);
- }
- else if (var_type == "BOOL")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_BOOL, 1);
- }
- else if (var_type == "IPADDR")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_IP_ADDR, 4);
- }
- else if (var_type == "IPPORT")
- {
- varp = new LLMessageVariable(var_name.c_str(), MVT_IP_PORT, 2);
- }
- else if (var_type == "Fixed" || var_type == "Variable")
- {
- std::string variable_size = tokens.next();
-
- if (!b_positive_integer_ok(variable_size.c_str()))
- {
- LL_ERRS() << "not a legal integer variable size: " << variable_size
- << " at " << tokens.line() << LL_ENDL;
- }
-
- EMsgVariableType type_enum;
- if(var_type == "Variable")
- {
- type_enum = MVT_VARIABLE;
- }
- else if(var_type == "Fixed")
- {
- type_enum = MVT_FIXED;
- }
- else
- {
- type_enum = MVT_FIXED; // removes a warning
- LL_ERRS() << "bad variable type: " << var_type
- << " at " << tokens.line() << LL_ENDL;
- }
-
- varp = new LLMessageVariable(
- var_name.c_str(),
- type_enum,
- atoi(variable_size.c_str()));
- }
- else
- {
- LL_ERRS() << "bad variable type:" << var_type
- << " at " << tokens.line() << LL_ENDL;
- }
-
- if(!tokens.want("}"))
- {
- LL_ERRS() << "Expecting closing } for variable " << var_name
- << " at " << tokens.line() << LL_ENDL;
- }
- return varp;
-}
+/** + * @file llmessagetemplateparser.cpp + * @brief LLMessageTemplateParser implementation + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llmessagetemplateparser.h" +#include <boost/tokenizer.hpp> + + +// What follows is a bunch of C functions to do validation. + +// 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(const char *token, const char *regexp) +{ + S32 tptr, rptr = 0; + S32 current_checker, next_checker = 0; + + current_checker = get_checker_number(regexp[rptr++]); + + if (current_checker == -1) + { + LL_ERRS() << "Invalid regular expression value!" << LL_ENDL; + return false; + } + + if (current_checker == 9999) + { + LL_ERRS() << "Regular expression can't start with *!" << LL_ENDL; + return false; + } + + for (tptr = 0; token[tptr]; tptr++) + { + if (current_checker == -1) + { + LL_ERRS() << "Input exceeds regular expression!\nDid you forget a *?" << LL_ENDL; + 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(const char *token) +{ + if (!b_check_token(token, "fv*")) + { + LL_WARNS() << "Token '" << token << "' isn't a variable!" << LL_ENDL; + return false; + } + return true; +} + +// An integer is made up of the digits 0-9 and may be preceded by a '-' +bool b_integer_ok(const char *token) +{ + if (!b_check_token(token, "sd*")) + { + LL_WARNS() << "Token isn't an integer!" << LL_ENDL; + return false; + } + return true; +} + +// An integer is made up of the digits 0-9 +bool b_positive_integer_ok(const char *token) +{ + if (!b_check_token(token, "d*")) + { + LL_WARNS() << "Token isn't an integer!" << LL_ENDL; + return false; + } + return true; +} + + +// Done with C functions, here's the tokenizer. + +typedef boost::tokenizer< boost::char_separator<char> > tokenizer; + +LLTemplateTokenizer::LLTemplateTokenizer(const std::string & contents) : mStarted(false), mTokens() +{ + boost::char_separator<char> newline("\r\n", "", boost::keep_empty_tokens); + boost::char_separator<char> spaces(" \t"); + U32 line_counter = 1; + + tokenizer line_tokens(contents, newline); + for(tokenizer::iterator line_iter = line_tokens.begin(); + line_iter != line_tokens.end(); + ++line_iter, ++line_counter) + { + tokenizer word_tokens(*line_iter, spaces); + for(tokenizer::iterator word_iter = word_tokens.begin(); + word_iter != word_tokens.end(); + ++word_iter) + { + if((*word_iter)[0] == '/') + { + break; // skip to end of line on comments + } + positioned_token pt;// = new positioned_token(); + pt.str = std::string(*word_iter); + pt.line = line_counter; + mTokens.push_back(pt); + } + } + mCurrent = mTokens.begin(); +} +void LLTemplateTokenizer::inc() +{ + if(atEOF()) + { + error("trying to increment token of EOF"); + } + else if(mStarted) + { + ++mCurrent; + } + else + { + mStarted = true; + mCurrent = mTokens.begin(); + } +} +void LLTemplateTokenizer::dec() +{ + if(mCurrent == mTokens.begin()) + { + if(mStarted) + { + mStarted = false; + } + else + { + error("trying to decrement past beginning of file"); + } + } + else + { + mCurrent--; + } +} + +std::string LLTemplateTokenizer::get() const +{ + if(atEOF()) + { + error("trying to get EOF"); + } + return mCurrent->str; +} + +U32 LLTemplateTokenizer::line() const +{ + if(atEOF()) + { + return 0; + } + return mCurrent->line; +} + +bool LLTemplateTokenizer::atEOF() const +{ + return mCurrent == mTokens.end(); +} + +std::string LLTemplateTokenizer::next() +{ + inc(); + return get(); +} + +bool LLTemplateTokenizer::want(const std::string & token) +{ + if(atEOF()) return false; + inc(); + if(atEOF()) return false; + if(get() != token) + { + dec(); // back up a step + return false; + } + return true; +} + +bool LLTemplateTokenizer::wantEOF() +{ + // see if the next token is EOF + if(atEOF()) return true; + inc(); + if(!atEOF()) + { + dec(); // back up a step + return false; + } + return true; +} + +void LLTemplateTokenizer::error(std::string message) const +{ + if(atEOF()) + { + LL_ERRS() << "Unexpected end of file: " << message << LL_ENDL; + } + else + { + LL_ERRS() << "Problem parsing message template at line " + << line() << ", with token '" << get() << "' : " + << message << LL_ENDL; + } +} + + +// Done with tokenizer, next is the parser. + +LLTemplateParser::LLTemplateParser(LLTemplateTokenizer & tokens): + mVersion(0.f), + mMessages() +{ + // the version number should be the first thing in the file + if (tokens.want("version")) + { + // version number + std::string vers_string = tokens.next(); + mVersion = (F32)atof(vers_string.c_str()); + + LL_INFOS() << "### Message template version " << mVersion << " ###" << LL_ENDL; + } + else + { + LL_ERRS() << "Version must be first in the message template, found " + << tokens.next() << LL_ENDL; + } + + while(LLMessageTemplate * templatep = parseMessage(tokens)) + { + if (templatep->getDeprecation() != MD_DEPRECATED) + { + mMessages.push_back(templatep); + } + else + { + delete templatep; + } + } + + if(!tokens.wantEOF()) + { + LL_ERRS() << "Expected end of template or a message, instead found: " + << tokens.next() << " at " << tokens.line() << LL_ENDL; + } +} + +F32 LLTemplateParser::getVersion() const +{ + return mVersion; +} + +LLTemplateParser::message_iterator LLTemplateParser::getMessagesBegin() const +{ + return mMessages.begin(); +} + +LLTemplateParser::message_iterator LLTemplateParser::getMessagesEnd() const +{ + return mMessages.end(); +} + + +// static +LLMessageTemplate * LLTemplateParser::parseMessage(LLTemplateTokenizer & tokens) +{ + LLMessageTemplate *templatep = NULL; + if(!tokens.want("{")) + { + return NULL; + } + + // name first + std::string template_name = tokens.next(); + + // is name a legit C variable name + if (!b_variable_ok(template_name.c_str())) + { + LL_ERRS() << "Not legit variable name: " << template_name << " at " << tokens.line() << LL_ENDL; + } + + // ok, now get Frequency ("High", "Medium", or "Low") + EMsgFrequency frequency = MFT_LOW; + std::string freq_string = tokens.next(); + if (freq_string == "High") + { + frequency = MFT_HIGH; + } + else if (freq_string == "Medium") + { + frequency = MFT_MEDIUM; + } + else if (freq_string == "Low" || freq_string == "Fixed") + { + frequency = MFT_LOW; + } + else + { + LL_ERRS() << "Expected frequency, got " << freq_string << " at " << tokens.line() << LL_ENDL; + } + + // TODO more explicit checking here pls + U32 message_number = strtoul(tokens.next().c_str(),NULL,0); + + switch (frequency) { + case MFT_HIGH: + break; + case MFT_MEDIUM: + message_number = (255 << 8) | message_number; + break; + case MFT_LOW: + message_number = (255 << 24) | (255 << 16) | message_number; + break; + default: + LL_ERRS() << "Unknown frequency enum: " << frequency << LL_ENDL; + } + + templatep = new LLMessageTemplate( + template_name.c_str(), + message_number, + frequency); + + // Now get trust ("Trusted", "NotTrusted") + std::string trust = tokens.next(); + if (trust == "Trusted") + { + templatep->setTrust(MT_TRUST); + } + else if (trust == "NotTrusted") + { + templatep->setTrust(MT_NOTRUST); + } + else + { + LL_ERRS() << "Bad trust " << trust << " at " << tokens.line() << LL_ENDL; + } + + // get encoding + std::string encoding = tokens.next(); + if(encoding == "Unencoded") + { + templatep->setEncoding(ME_UNENCODED); + } + else if(encoding == "Zerocoded") + { + templatep->setEncoding(ME_ZEROCODED); + } + else + { + LL_ERRS() << "Bad encoding " << encoding << " at " << tokens.line() << LL_ENDL; + } + + // get deprecation + if(tokens.want("Deprecated")) + { + templatep->setDeprecation(MD_DEPRECATED); + } + else if (tokens.want("UDPDeprecated")) + { + templatep->setDeprecation(MD_UDPDEPRECATED); + } + else if (tokens.want("UDPBlackListed")) + { + templatep->setDeprecation(MD_UDPBLACKLISTED); + } + else if (tokens.want("NotDeprecated")) + { + // this is the default value, but it can't hurt to set it twice + templatep->setDeprecation(MD_NOTDEPRECATED); + } + else { + // It's probably a brace, let's just start block processing + } + + while(LLMessageBlock * blockp = parseBlock(tokens)) + { + templatep->addBlock(blockp); + } + + if(!tokens.want("}")) + { + LL_ERRS() << "Expecting closing } for message " << template_name + << " at " << tokens.line() << LL_ENDL; + } + return templatep; +} + +// static +LLMessageBlock * LLTemplateParser::parseBlock(LLTemplateTokenizer & tokens) +{ + LLMessageBlock * blockp = NULL; + + if(!tokens.want("{")) + { + return NULL; + } + + // name first + std::string block_name = tokens.next(); + + // is name a legit C variable name + if (!b_variable_ok(block_name.c_str())) + { + LL_ERRS() << "not a legal block name: " << block_name + << " at " << tokens.line() << LL_ENDL; + } + + // now, block type ("Single", "Multiple", or "Variable") + std::string block_type = tokens.next(); + // which one is it? + if (block_type == "Single") + { + // ok, we can create a block + blockp = new LLMessageBlock(block_name.c_str(), MBT_SINGLE); + } + else if (block_type == "Multiple") + { + // need to get the number of repeats + std::string repeats = tokens.next(); + + // is it a legal integer + if (!b_positive_integer_ok(repeats.c_str())) + { + LL_ERRS() << "not a legal integer for block multiple count: " + << repeats << " at " << tokens.line() << LL_ENDL; + } + + // ok, we can create a block + blockp = new LLMessageBlock(block_name.c_str(), + MBT_MULTIPLE, + atoi(repeats.c_str())); + } + else if (block_type == "Variable") + { + // ok, we can create a block + blockp = new LLMessageBlock(block_name.c_str(), MBT_VARIABLE); + } + else + { + LL_ERRS() << "bad block type: " << block_type + << " at " << tokens.line() << LL_ENDL; + } + + + while(LLMessageVariable * varp = parseVariable(tokens)) + { + blockp->addVariable(varp->getName(), + varp->getType(), + varp->getSize()); + delete varp; + } + + if(!tokens.want("}")) + { + LL_ERRS() << "Expecting closing } for block " << block_name + << " at " << tokens.line() << LL_ENDL; + } + return blockp; + +} + +// static +LLMessageVariable * LLTemplateParser::parseVariable(LLTemplateTokenizer & tokens) +{ + LLMessageVariable * varp = NULL; + if(!tokens.want("{")) + { + return NULL; + } + + std::string var_name = tokens.next(); + + if (!b_variable_ok(var_name.c_str())) + { + LL_ERRS() << "Not a legit variable name: " << var_name + << " at " << tokens.line() << LL_ENDL; + } + + std::string var_type = tokens.next(); + + if (var_type == "U8") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_U8, 1); + } + else if (var_type == "U16") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_U16, 2); + } + else if (var_type == "U32") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_U32, 4); + } + else if (var_type == "U64") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_U64, 8); + } + else if (var_type == "S8") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_S8, 1); + } + else if (var_type == "S16") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_S16, 2); + } + else if (var_type == "S32") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_S32, 4); + } + else if (var_type == "S64") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_S64, 8); + } + else if (var_type == "F32") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_F32, 4); + } + else if (var_type == "F64") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_F64, 8); + } + else if (var_type == "LLVector3") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector3, 12); + } + else if (var_type == "LLVector3d") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector3d, 24); + } + else if (var_type == "LLVector4") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector4, 16); + } + else if (var_type == "LLQuaternion") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_LLQuaternion, 12); + } + else if (var_type == "LLUUID") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_LLUUID, 16); + } + else if (var_type == "BOOL") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_BOOL, 1); + } + else if (var_type == "IPADDR") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_IP_ADDR, 4); + } + else if (var_type == "IPPORT") + { + varp = new LLMessageVariable(var_name.c_str(), MVT_IP_PORT, 2); + } + else if (var_type == "Fixed" || var_type == "Variable") + { + std::string variable_size = tokens.next(); + + if (!b_positive_integer_ok(variable_size.c_str())) + { + LL_ERRS() << "not a legal integer variable size: " << variable_size + << " at " << tokens.line() << LL_ENDL; + } + + EMsgVariableType type_enum; + if(var_type == "Variable") + { + type_enum = MVT_VARIABLE; + } + else if(var_type == "Fixed") + { + type_enum = MVT_FIXED; + } + else + { + type_enum = MVT_FIXED; // removes a warning + LL_ERRS() << "bad variable type: " << var_type + << " at " << tokens.line() << LL_ENDL; + } + + varp = new LLMessageVariable( + var_name.c_str(), + type_enum, + atoi(variable_size.c_str())); + } + else + { + LL_ERRS() << "bad variable type:" << var_type + << " at " << tokens.line() << LL_ENDL; + } + + if(!tokens.want("}")) + { + LL_ERRS() << "Expecting closing } for variable " << var_name + << " at " << tokens.line() << LL_ENDL; + } + return varp; +} diff --git a/indra/llmessage/llmessagethrottle.cpp b/indra/llmessage/llmessagethrottle.cpp index 77af27e404..abcb1085ba 100644 --- a/indra/llmessage/llmessagethrottle.cpp +++ b/indra/llmessage/llmessagethrottle.cpp @@ -1,153 +1,153 @@ -/**
- * @file llmessagethrottle.cpp
- * @brief LLMessageThrottle class used for throttling messages.
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#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 std::string& 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(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 std::string& 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(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;
- }
-}
-
+/** + * @file llmessagethrottle.cpp + * @brief LLMessageThrottle class used for throttling messages. + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#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 std::string& 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(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 std::string& 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(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 index 9597fbccdf..295bddbd8c 100644 --- a/indra/llmessage/llmessagethrottle.h +++ b/indra/llmessage/llmessagethrottle.h @@ -1,80 +1,80 @@ -/**
- * @file llmessagethrottle.h
- * @brief LLMessageThrottle class used for throttling messages.
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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() const { return mHash; }
- U64 getEntryTime() const { return mEntryTime; }
-protected:
- size_t mHash;
- U64 mEntryTime;
-};
-
-
-class LLMessageThrottle
-{
-public:
- LLMessageThrottle();
- ~LLMessageThrottle();
-
- bool addViewerAlert (const LLUUID& to, const std::string& mesg);
- bool addAgentAlert (const LLUUID& agent, const LLUUID& task, const std::string& 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
-
-
+/** + * @file llmessagethrottle.h + * @brief LLMessageThrottle class used for throttling messages. + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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() const { return mHash; } + U64 getEntryTime() const { return mEntryTime; } +protected: + size_t mHash; + U64 mEntryTime; +}; + + +class LLMessageThrottle +{ +public: + LLMessageThrottle(); + ~LLMessageThrottle(); + + bool addViewerAlert (const LLUUID& to, const std::string& mesg); + bool addAgentAlert (const LLUUID& agent, const LLUUID& task, const std::string& 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/llnamevalue.cpp b/indra/llmessage/llnamevalue.cpp index 5254091481..853ae7df82 100644 --- a/indra/llmessage/llnamevalue.cpp +++ b/indra/llmessage/llnamevalue.cpp @@ -1,970 +1,970 @@ -/**
- * @file llnamevalue.cpp
- * @brief class for defining name value pairs.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Examples:
-// AvatarCharacter STRING RW DSV male1
-
-#include "linden_common.h"
-
-#include "llnamevalue.h"
-
-#include "u64.h"
-#include "llstring.h"
-#include "llstringtable.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
-};
-
-LLStringTable gNVNameTable(256);
-
-char NameValueTypeStrings[NVT_EOF][NAME_VALUE_TYPE_STRING_LENGTH] = /*Flawfinder: Ignore*/
-{
- "NULL",
- "STRING",
- "F32",
- "S32",
- "VEC3",
- "U32",
- "CAMERA", // Deprecated, but leaving in case removing completely would cause problems
- "ASSET",
- "U64"
-};
-
-char NameValueClassStrings[NVC_EOF][NAME_VALUE_CLASS_STRING_LENGTH] = /*Flawfinder: Ignore*/
-{
- "NULL",
- "R", // read only
- "RW" // read write
-};
-
-char NameValueSendtoStrings[NVS_EOF][NAME_VALUE_SENDTO_STRING_LENGTH] = /*Flawfinder: Ignore*/
-{
- "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*/
-
-
-//
-// 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)
-{
- 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(ll_safe_string(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
- {
- LL_WARNS() << "Unknown name value type string " << mStringType << " for " << mName << LL_ENDL;
- 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
- {
- // assume it's bad
- mClass = NVC_NULL;
- mStringClass = mNVNameTable->addString(nvclass);
- }
-
- // 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
- {
- LL_WARNS() << "LLNameValue::init() - unknown sendto field "
- << nvsendto << " for NV " << mName << LL_ENDL;
- mSendto = NVS_NULL;
- mStringSendto = mNVNameTable->addString("S");
- }
-
-}
-
-
-LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass)
-{
- baseInit();
- // if not specified, send to simulator only
- init(name, data, type, nvclass, "SIM");
-}
-
-
-LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto)
-{
- baseInit();
- init(name, data, type, nvclass, nvsendto);
-}
-
-
-
-// Initialize without any initial data.
-LLNameValue::LLNameValue(const char *name, const char *type, const char *nvclass)
-{
- 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;
- LL_INFOS() << "Unknown name-value type " << mStringType << LL_ENDL;
- }
-
- // 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
- {
- mClass = NVC_NULL;
- }
-
- // 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]; /*Flawfinder: ignore*/
- static char type[NV_BUFFER_LEN]; /*Flawfinder: ignore*/
- static char nvclass[NV_BUFFER_LEN]; /*Flawfinder: ignore*/
- static char nvsendto[NV_BUFFER_LEN]; /*Flawfinder: ignore*/
- static char nvdata[NV_BUFFER_LEN]; /*Flawfinder: ignore*/
-
- 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); /*Flawfinder: ignore*/
-
- // 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); /*Flawfinder: ignore*/
-
- // 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); /*Flawfinder: ignore*/
-
- // 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); /*Flawfinder: ignore*/
-
- // 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
- {
- LL_ERRS() << mName << " not a string!" << LL_ENDL;
- return NULL;
- }
-}
-
-const char *LLNameValue::getAsset() const
-{
- if (mType == NVT_ASSET)
- {
- return mNameValueReference.string;
- }
- else
- {
- LL_ERRS() << mName << " not an asset!" << LL_ENDL;
- return NULL;
- }
-}
-
-F32 *LLNameValue::getF32()
-{
- if (mType == NVT_F32)
- {
- return mNameValueReference.f32;
- }
- else
- {
- LL_ERRS() << mName << " not a F32!" << LL_ENDL;
- return NULL;
- }
-}
-
-S32 *LLNameValue::getS32()
-{
- if (mType == NVT_S32)
- {
- return mNameValueReference.s32;
- }
- else
- {
- LL_ERRS() << mName << " not a S32!" << LL_ENDL;
- return NULL;
- }
-}
-
-U32 *LLNameValue::getU32()
-{
- if (mType == NVT_U32)
- {
- return mNameValueReference.u32;
- }
- else
- {
- LL_ERRS() << mName << " not a U32!" << LL_ENDL;
- return NULL;
- }
-}
-
-U64 *LLNameValue::getU64()
-{
- if (mType == NVT_U64)
- {
- return mNameValueReference.u64;
- }
- else
- {
- LL_ERRS() << mName << " not a U64!" << LL_ENDL;
- return NULL;
- }
-}
-
-void LLNameValue::getVec3(LLVector3 &vec)
-{
- if (mType == NVT_VEC3)
- {
- vec = *mNameValueReference.vec3;
- }
- else
- {
- LL_ERRS() << mName << " not a Vec3!" << LL_ENDL;
- }
-}
-
-LLVector3 *LLNameValue::getVec3()
-{
- if (mType == NVT_VEC3)
- {
- return (mNameValueReference.vec3);
- }
- else
- {
- LL_ERRS() << mName << " not a Vec3!" << LL_ENDL;
- return NULL;
- }
-}
-
-
-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;
-
- 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:
- LL_ERRS() << "Unknown Name value type " << (U32)a.mType << LL_ENDL;
- break;
- }
-
- return *this;
-}
-
-void LLNameValue::setString(const char *a)
-{
- if (mClass == NVC_READ_ONLY)
- return;
-
- switch(mType)
- {
- case NVT_STRING:
- if (a)
- {
- 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 */
- }
- }
- else
- {
- if (mNameValueReference.string)
- delete [] mNameValueReference.string;
-
- mNameValueReference.string = new char [1];
- mNameValueReference.string[0] = 0;
- }
- break;
- default:
- break;
- }
-
- return;
-}
-
-
-void LLNameValue::setAsset(const char *a)
-{
- if (mClass == NVC_READ_ONLY)
- return;
-
- switch(mType)
- {
- case NVT_ASSET:
- if (a)
- {
- 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 */
- }
- }
- else
- {
- if (mNameValueReference.string)
- delete [] mNameValueReference.string;
-
- mNameValueReference.string = new char [1];
- mNameValueReference.string[0] = 0;
- }
- break;
- default:
- break;
- }
-}
-
-
-void LLNameValue::setF32(const F32 a)
-{
- if (mClass == NVC_READ_ONLY)
- return;
-
- switch(mType)
- {
- case NVT_F32:
- *mNameValueReference.f32 = a;
- break;
- default:
- break;
- }
-
- return;
-}
-
-
-void LLNameValue::setS32(const S32 a)
-{
- if (mClass == NVC_READ_ONLY)
- return;
-
- switch(mType)
- {
- case NVT_S32:
- *mNameValueReference.s32 = a;
- break;
- case NVT_U32:
- *mNameValueReference.u32 = a;
- break;
- case NVT_F32:
- *mNameValueReference.f32 = (F32)a;
- break;
- default:
- break;
- }
-
- return;
-}
-
-
-void LLNameValue::setU32(const U32 a)
-{
- if (mClass == NVC_READ_ONLY)
- return;
-
- switch(mType)
- {
- case NVT_S32:
- *mNameValueReference.s32 = a;
- break;
- case NVT_U32:
- *mNameValueReference.u32 = a;
- break;
- case NVT_F32:
- *mNameValueReference.f32 = (F32)a;
- break;
- default:
- LL_ERRS() << "NameValue: Trying to set U32 into a " << mStringType << ", unknown conversion" << LL_ENDL;
- break;
- }
- return;
-}
-
-
-void LLNameValue::setVec3(const LLVector3 &a)
-{
- if (mClass == NVC_READ_ONLY)
- return;
-
- switch(mType)
- {
- case NVT_VEC3:
- *mNameValueReference.vec3 = a;
- break;
- default:
- LL_ERRS() << "NameValue: Trying to set LLVector3 into a " << mStringType << ", unknown conversion" << LL_ENDL;
- break;
- }
- return;
-}
-
-
-std::string LLNameValue::printNameValue() const
-{
- std::string buffer;
- buffer = llformat("%s %s %s %s ", mName, mStringType, mStringClass, mStringSendto);
- buffer += printData();
-// LL_INFOS() << "Name Value Length: " << buffer.size() + 1 << LL_ENDL;
- return buffer;
-}
-
-std::string LLNameValue::printData() const
-{
- 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:
- LL_ERRS() << "Trying to print unknown NameValue type " << mStringType << LL_ENDL;
- 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;
- }
- break;
- case NVT_VEC3:
- s << *(a.mNameValueReference.vec3);
- break;
- default:
- LL_ERRS() << "Trying to print unknown NameValue type " << a.mStringType << LL_ENDL;
- break;
- }
- return s;
-}
-
+/** + * @file llnamevalue.cpp + * @brief class for defining name value pairs. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Examples: +// AvatarCharacter STRING RW DSV male1 + +#include "linden_common.h" + +#include "llnamevalue.h" + +#include "u64.h" +#include "llstring.h" +#include "llstringtable.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 +}; + +LLStringTable gNVNameTable(256); + +char NameValueTypeStrings[NVT_EOF][NAME_VALUE_TYPE_STRING_LENGTH] = /*Flawfinder: Ignore*/ +{ + "NULL", + "STRING", + "F32", + "S32", + "VEC3", + "U32", + "CAMERA", // Deprecated, but leaving in case removing completely would cause problems + "ASSET", + "U64" +}; + +char NameValueClassStrings[NVC_EOF][NAME_VALUE_CLASS_STRING_LENGTH] = /*Flawfinder: Ignore*/ +{ + "NULL", + "R", // read only + "RW" // read write +}; + +char NameValueSendtoStrings[NVS_EOF][NAME_VALUE_SENDTO_STRING_LENGTH] = /*Flawfinder: Ignore*/ +{ + "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*/ + + +// +// 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) +{ + 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(ll_safe_string(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 + { + LL_WARNS() << "Unknown name value type string " << mStringType << " for " << mName << LL_ENDL; + 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 + { + // assume it's bad + mClass = NVC_NULL; + mStringClass = mNVNameTable->addString(nvclass); + } + + // 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 + { + LL_WARNS() << "LLNameValue::init() - unknown sendto field " + << nvsendto << " for NV " << mName << LL_ENDL; + mSendto = NVS_NULL; + mStringSendto = mNVNameTable->addString("S"); + } + +} + + +LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass) +{ + baseInit(); + // if not specified, send to simulator only + init(name, data, type, nvclass, "SIM"); +} + + +LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto) +{ + baseInit(); + init(name, data, type, nvclass, nvsendto); +} + + + +// Initialize without any initial data. +LLNameValue::LLNameValue(const char *name, const char *type, const char *nvclass) +{ + 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; + LL_INFOS() << "Unknown name-value type " << mStringType << LL_ENDL; + } + + // 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 + { + mClass = NVC_NULL; + } + + // 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]; /*Flawfinder: ignore*/ + static char type[NV_BUFFER_LEN]; /*Flawfinder: ignore*/ + static char nvclass[NV_BUFFER_LEN]; /*Flawfinder: ignore*/ + static char nvsendto[NV_BUFFER_LEN]; /*Flawfinder: ignore*/ + static char nvdata[NV_BUFFER_LEN]; /*Flawfinder: ignore*/ + + 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); /*Flawfinder: ignore*/ + + // 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); /*Flawfinder: ignore*/ + + // 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); /*Flawfinder: ignore*/ + + // 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); /*Flawfinder: ignore*/ + + // 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 + { + LL_ERRS() << mName << " not a string!" << LL_ENDL; + return NULL; + } +} + +const char *LLNameValue::getAsset() const +{ + if (mType == NVT_ASSET) + { + return mNameValueReference.string; + } + else + { + LL_ERRS() << mName << " not an asset!" << LL_ENDL; + return NULL; + } +} + +F32 *LLNameValue::getF32() +{ + if (mType == NVT_F32) + { + return mNameValueReference.f32; + } + else + { + LL_ERRS() << mName << " not a F32!" << LL_ENDL; + return NULL; + } +} + +S32 *LLNameValue::getS32() +{ + if (mType == NVT_S32) + { + return mNameValueReference.s32; + } + else + { + LL_ERRS() << mName << " not a S32!" << LL_ENDL; + return NULL; + } +} + +U32 *LLNameValue::getU32() +{ + if (mType == NVT_U32) + { + return mNameValueReference.u32; + } + else + { + LL_ERRS() << mName << " not a U32!" << LL_ENDL; + return NULL; + } +} + +U64 *LLNameValue::getU64() +{ + if (mType == NVT_U64) + { + return mNameValueReference.u64; + } + else + { + LL_ERRS() << mName << " not a U64!" << LL_ENDL; + return NULL; + } +} + +void LLNameValue::getVec3(LLVector3 &vec) +{ + if (mType == NVT_VEC3) + { + vec = *mNameValueReference.vec3; + } + else + { + LL_ERRS() << mName << " not a Vec3!" << LL_ENDL; + } +} + +LLVector3 *LLNameValue::getVec3() +{ + if (mType == NVT_VEC3) + { + return (mNameValueReference.vec3); + } + else + { + LL_ERRS() << mName << " not a Vec3!" << LL_ENDL; + return NULL; + } +} + + +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; + + 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: + LL_ERRS() << "Unknown Name value type " << (U32)a.mType << LL_ENDL; + break; + } + + return *this; +} + +void LLNameValue::setString(const char *a) +{ + if (mClass == NVC_READ_ONLY) + return; + + switch(mType) + { + case NVT_STRING: + if (a) + { + 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 */ + } + } + else + { + if (mNameValueReference.string) + delete [] mNameValueReference.string; + + mNameValueReference.string = new char [1]; + mNameValueReference.string[0] = 0; + } + break; + default: + break; + } + + return; +} + + +void LLNameValue::setAsset(const char *a) +{ + if (mClass == NVC_READ_ONLY) + return; + + switch(mType) + { + case NVT_ASSET: + if (a) + { + 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 */ + } + } + else + { + if (mNameValueReference.string) + delete [] mNameValueReference.string; + + mNameValueReference.string = new char [1]; + mNameValueReference.string[0] = 0; + } + break; + default: + break; + } +} + + +void LLNameValue::setF32(const F32 a) +{ + if (mClass == NVC_READ_ONLY) + return; + + switch(mType) + { + case NVT_F32: + *mNameValueReference.f32 = a; + break; + default: + break; + } + + return; +} + + +void LLNameValue::setS32(const S32 a) +{ + if (mClass == NVC_READ_ONLY) + return; + + switch(mType) + { + case NVT_S32: + *mNameValueReference.s32 = a; + break; + case NVT_U32: + *mNameValueReference.u32 = a; + break; + case NVT_F32: + *mNameValueReference.f32 = (F32)a; + break; + default: + break; + } + + return; +} + + +void LLNameValue::setU32(const U32 a) +{ + if (mClass == NVC_READ_ONLY) + return; + + switch(mType) + { + case NVT_S32: + *mNameValueReference.s32 = a; + break; + case NVT_U32: + *mNameValueReference.u32 = a; + break; + case NVT_F32: + *mNameValueReference.f32 = (F32)a; + break; + default: + LL_ERRS() << "NameValue: Trying to set U32 into a " << mStringType << ", unknown conversion" << LL_ENDL; + break; + } + return; +} + + +void LLNameValue::setVec3(const LLVector3 &a) +{ + if (mClass == NVC_READ_ONLY) + return; + + switch(mType) + { + case NVT_VEC3: + *mNameValueReference.vec3 = a; + break; + default: + LL_ERRS() << "NameValue: Trying to set LLVector3 into a " << mStringType << ", unknown conversion" << LL_ENDL; + break; + } + return; +} + + +std::string LLNameValue::printNameValue() const +{ + std::string buffer; + buffer = llformat("%s %s %s %s ", mName, mStringType, mStringClass, mStringSendto); + buffer += printData(); +// LL_INFOS() << "Name Value Length: " << buffer.size() + 1 << LL_ENDL; + return buffer; +} + +std::string LLNameValue::printData() const +{ + 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: + LL_ERRS() << "Trying to print unknown NameValue type " << mStringType << LL_ENDL; + 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; + } + break; + case NVT_VEC3: + s << *(a.mNameValueReference.vec3); + break; + default: + LL_ERRS() << "Trying to print unknown NameValue type " << a.mStringType << LL_ENDL; + break; + } + return s; +} + diff --git a/indra/llmessage/llnamevalue.h b/indra/llmessage/llnamevalue.h index 6876c6020d..3c442df009 100644 --- a/indra/llmessage/llnamevalue.h +++ b/indra/llmessage/llnamevalue.h @@ -1,183 +1,183 @@ -/**
- * @file llnamevalue.h
- * @brief class for defining name value pairs.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLNAMEVALUE_H
-#define LL_LLNAMEVALUE_H
-
-// As of January 2008, I believe we only use the following name-value
-// pairs. This is hard to prove because they are initialized from
-// strings. JC
-//
-// FirstName STRING
-// LastName STRING
-// AttachPt U32
-// AttachmentItemId STRING
-// Title STRING
-// AttachmentOffset VEC3
-// AttachmentOrientation VEC3
-// SitObject STRING
-// SitPosition VEC3
-
-#include "llstringtable.h"
-#include "llmath.h"
-#include "v3math.h"
-#include "lldbstrings.h"
-
-class LLNameValue;
-class LLStringTable;
-
-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_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 );
-
- LLNameValue();
- LLNameValue(const char *data);
- LLNameValue(const char *name, const char *type, const char *nvclass );
- LLNameValue(const char *name, const char *data, const char *type, const char *nvclass );
- LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto );
-
- ~LLNameValue();
-
- char *getString();
- const char *getAsset() const;
- F32 *getF32();
- S32 *getS32();
- void getVec3(LLVector3 &vec);
- LLVector3 *getVec3();
- 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() const;
- std::string printData() const;
-
- 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);
-
- friend std::ostream& operator<<(std::ostream& s, 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;
- LLStringTable *mNVNameTable;
-};
-
-extern LLStringTable gNVNameTable;
-
-
-#endif
+/** + * @file llnamevalue.h + * @brief class for defining name value pairs. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLNAMEVALUE_H +#define LL_LLNAMEVALUE_H + +// As of January 2008, I believe we only use the following name-value +// pairs. This is hard to prove because they are initialized from +// strings. JC +// +// FirstName STRING +// LastName STRING +// AttachPt U32 +// AttachmentItemId STRING +// Title STRING +// AttachmentOffset VEC3 +// AttachmentOrientation VEC3 +// SitObject STRING +// SitPosition VEC3 + +#include "llstringtable.h" +#include "llmath.h" +#include "v3math.h" +#include "lldbstrings.h" + +class LLNameValue; +class LLStringTable; + +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_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 ); + + LLNameValue(); + LLNameValue(const char *data); + LLNameValue(const char *name, const char *type, const char *nvclass ); + LLNameValue(const char *name, const char *data, const char *type, const char *nvclass ); + LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto ); + + ~LLNameValue(); + + char *getString(); + const char *getAsset() const; + F32 *getF32(); + S32 *getS32(); + void getVec3(LLVector3 &vec); + LLVector3 *getVec3(); + 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() const; + std::string printData() const; + + 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); + + friend std::ostream& operator<<(std::ostream& s, 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; + LLStringTable *mNVNameTable; +}; + +extern LLStringTable gNVNameTable; + + +#endif diff --git a/indra/llmessage/llpacketack.cpp b/indra/llmessage/llpacketack.cpp index 19d21bf5d0..e66e8b0285 100644 --- a/indra/llmessage/llpacketack.cpp +++ b/indra/llmessage/llpacketack.cpp @@ -1,82 +1,82 @@ -/**
- * @file llpacketack.cpp
- * @author Phoenix
- * @date 2007-05-09
- * @brief Implementation of the LLReliablePacket.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llpacketack.h"
-
-#if !LL_WINDOWS
-#include <netinet/in.h>
-#else
-#include "winsock2.h"
-#endif
-
-#include "message.h"
-
-LLReliablePacket::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 = F32Seconds(params->mTimeout);
- mCallback = params->mCallback;
- mCallbackData = params->mCallbackData;
- mMessageName = params->mMessageName;
- }
- else
- {
- mRetries = 0;
- mPingBasedRetry = true;
- mTimeout = F32Seconds(0.f);
- mCallback = NULL;
- mCallbackData = NULL;
- mMessageName = NULL;
- }
-
- mExpirationTime = (F64Seconds)totalTime() + mTimeout;
- mPacketID = ntohl(*((U32*)(&buf_ptr[PHL_PACKET_ID])));
-
- mSocket = socket;
- if (mRetries)
- {
- mBuffer = new U8[buf_len];
- if (mBuffer != NULL)
- {
- memcpy(mBuffer,buf_ptr,buf_len); /*Flawfinder: ignore*/
- mBufferLength = buf_len;
- }
-
- }
-}
+/** + * @file llpacketack.cpp + * @author Phoenix + * @date 2007-05-09 + * @brief Implementation of the LLReliablePacket. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llpacketack.h" + +#if !LL_WINDOWS +#include <netinet/in.h> +#else +#include "winsock2.h" +#endif + +#include "message.h" + +LLReliablePacket::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 = F32Seconds(params->mTimeout); + mCallback = params->mCallback; + mCallbackData = params->mCallbackData; + mMessageName = params->mMessageName; + } + else + { + mRetries = 0; + mPingBasedRetry = true; + mTimeout = F32Seconds(0.f); + mCallback = NULL; + mCallbackData = NULL; + mMessageName = NULL; + } + + mExpirationTime = (F64Seconds)totalTime() + mTimeout; + mPacketID = ntohl(*((U32*)(&buf_ptr[PHL_PACKET_ID]))); + + mSocket = socket; + if (mRetries) + { + mBuffer = new U8[buf_len]; + if (mBuffer != NULL) + { + memcpy(mBuffer,buf_ptr,buf_len); /*Flawfinder: ignore*/ + mBufferLength = buf_len; + } + + } +} diff --git a/indra/llmessage/llpacketack.h b/indra/llmessage/llpacketack.h index c359ec2c0e..0903c01e14 100644 --- a/indra/llmessage/llpacketack.h +++ b/indra/llmessage/llpacketack.h @@ -1,116 +1,116 @@ -/**
- * @file llpacketack.h
- * @brief Reliable UDP helpers for the message system.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPACKETACK_H
-#define LL_LLPACKETACK_H
-
-#include "llhost.h"
-#include "llunits.h"
-
-class LLReliablePacketParams
-{
-public:
- LLHost mHost;
- S32 mRetries;
- bool mPingBasedRetry;
- F32Seconds mTimeout;
- void (*mCallback)(void **,S32);
- void** mCallbackData;
- char* mMessageName;
-
-public:
- LLReliablePacketParams()
- {
- clear();
- };
-
- ~LLReliablePacketParams() { };
-
- void clear()
- {
- mHost.invalidate();
- mRetries = 0;
- mPingBasedRetry = true;
- mTimeout = F32Seconds(0.f);
- mCallback = NULL;
- mCallbackData = NULL;
- mMessageName = NULL;
- };
-
- void set(
- const LLHost& host,
- S32 retries,
- bool ping_based_retry,
- F32Seconds 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);
- ~LLReliablePacket()
- {
- mCallback = NULL;
- delete [] mBuffer;
- mBuffer = NULL;
- };
-
- friend class LLCircuitData;
-protected:
- S32 mSocket;
- LLHost mHost;
- S32 mRetries;
- bool mPingBasedRetry;
- F32Seconds mTimeout;
- void (*mCallback)(void**,S32);
- void** mCallbackData;
- char* mMessageName;
-
- U8* mBuffer;
- S32 mBufferLength;
-
- TPACKETID mPacketID;
-
- F64Seconds mExpirationTime;
-};
-
-#endif
-
+/** + * @file llpacketack.h + * @brief Reliable UDP helpers for the message system. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPACKETACK_H +#define LL_LLPACKETACK_H + +#include "llhost.h" +#include "llunits.h" + +class LLReliablePacketParams +{ +public: + LLHost mHost; + S32 mRetries; + bool mPingBasedRetry; + F32Seconds mTimeout; + void (*mCallback)(void **,S32); + void** mCallbackData; + char* mMessageName; + +public: + LLReliablePacketParams() + { + clear(); + }; + + ~LLReliablePacketParams() { }; + + void clear() + { + mHost.invalidate(); + mRetries = 0; + mPingBasedRetry = true; + mTimeout = F32Seconds(0.f); + mCallback = NULL; + mCallbackData = NULL; + mMessageName = NULL; + }; + + void set( + const LLHost& host, + S32 retries, + bool ping_based_retry, + F32Seconds 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); + ~LLReliablePacket() + { + mCallback = NULL; + delete [] mBuffer; + mBuffer = NULL; + }; + + friend class LLCircuitData; +protected: + S32 mSocket; + LLHost mHost; + S32 mRetries; + bool mPingBasedRetry; + F32Seconds mTimeout; + void (*mCallback)(void**,S32); + void** mCallbackData; + char* mMessageName; + + U8* mBuffer; + S32 mBufferLength; + + TPACKETID mPacketID; + + F64Seconds mExpirationTime; +}; + +#endif + diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp index 8f96554939..be838770a8 100644 --- a/indra/llmessage/llpacketring.cpp +++ b/indra/llmessage/llpacketring.cpp @@ -1,371 +1,371 @@ -/**
- * @file llpacketring.cpp
- * @brief implementation of LLPacketRing class for a packet.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llpacketring.h"
-
-#if LL_WINDOWS
- #include <winsock2.h>
-#else
- #include <sys/socket.h>
- #include <netinet/in.h>
-#endif
-
-// linden library includes
-#include "llerror.h"
-#include "lltimer.h"
-#include "llproxy.h"
-#include "llrand.h"
-#include "message.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 ()
-{
- cleanup();
-}
-
-///////////////////////////////////////////////////////////
-void LLPacketRing::cleanup ()
-{
- 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); /*Flawfinder: ignore*/
- }
- // need to set sender IP/port!!
- mLastSender = packetp->getHost();
- mLastReceivingIF = packetp->getReceivingInterface();
- 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 && (ll_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.
- LL_WARNS() << "Throwing away packet, overflowing buffer" << LL_ENDL;
- 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
- if (LLProxy::isSOCKSProxyEnabled())
- {
- U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
- packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer)));
-
- if (packet_size > SOCKS_HEADER_SIZE)
- {
- // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6)
- memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size - SOCKS_HEADER_SIZE);
- proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer));
- mLastSender.setAddress(header->addr);
- mLastSender.setPort(ntohs(header->port));
-
- packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size
- }
- else
- {
- packet_size = 0;
- }
- }
- else
- {
- packet_size = receive_packet(socket, datap);
- mLastSender = ::get_sender();
- }
-
- mLastReceivingIF = ::get_receiving_interface();
-
- if (packet_size) // did we actually get a packet?
- {
- if (mDropPercentage && (ll_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 sendPacketImpl(h_socket, send_buffer, buf_size, host );
- }
- 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 = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost());
-
- 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 = sendPacketImpl(h_socket, send_buffer, buf_size, host );
- 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.
- LL_WARNS() << "Throwing away outbound packet, overflowing buffer" << LL_ENDL;
- }
- else
- {
- static LLTimer queue_timer;
- if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f)
- {
- // Add it to the queue
- LL_INFOS() << "Outbound packet queue " << mOutBufferLength << " bytes" << LL_ENDL;
- queue_timer.reset();
- }
- packetp = new LLPacketBuffer(host, send_buffer, buf_size);
-
- mOutBufferLength += packetp->getSize();
- mSendQueue.push(packetp);
- }
- }
-
- return status;
-}
-
-bool LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host)
-{
-
- if (!LLProxy::isSOCKSProxyEnabled())
- {
- return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort());
- }
-
- char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
-
- proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer));
- socks_header->rsv = 0;
- socks_header->addr = host.getAddress();
- socks_header->port = htons(host.getPort());
- socks_header->atype = ADDRESS_IPV4;
- socks_header->frag = 0;
-
- memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size);
-
- return send_packet( h_socket,
- headered_send_buffer,
- buf_size + SOCKS_HEADER_SIZE,
- LLProxy::getInstance()->getUDPProxy().getAddress(),
- LLProxy::getInstance()->getUDPProxy().getPort());
-}
+/** + * @file llpacketring.cpp + * @brief implementation of LLPacketRing class for a packet. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llpacketring.h" + +#if LL_WINDOWS + #include <winsock2.h> +#else + #include <sys/socket.h> + #include <netinet/in.h> +#endif + +// linden library includes +#include "llerror.h" +#include "lltimer.h" +#include "llproxy.h" +#include "llrand.h" +#include "message.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 () +{ + cleanup(); +} + +/////////////////////////////////////////////////////////// +void LLPacketRing::cleanup () +{ + 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); /*Flawfinder: ignore*/ + } + // need to set sender IP/port!! + mLastSender = packetp->getHost(); + mLastReceivingIF = packetp->getReceivingInterface(); + 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 && (ll_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. + LL_WARNS() << "Throwing away packet, overflowing buffer" << LL_ENDL; + 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 + if (LLProxy::isSOCKSProxyEnabled()) + { + U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; + packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer))); + + if (packet_size > SOCKS_HEADER_SIZE) + { + // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6) + memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size - SOCKS_HEADER_SIZE); + proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer)); + mLastSender.setAddress(header->addr); + mLastSender.setPort(ntohs(header->port)); + + packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size + } + else + { + packet_size = 0; + } + } + else + { + packet_size = receive_packet(socket, datap); + mLastSender = ::get_sender(); + } + + mLastReceivingIF = ::get_receiving_interface(); + + if (packet_size) // did we actually get a packet? + { + if (mDropPercentage && (ll_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 sendPacketImpl(h_socket, send_buffer, buf_size, host ); + } + 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 = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost()); + + 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 = sendPacketImpl(h_socket, send_buffer, buf_size, host ); + 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. + LL_WARNS() << "Throwing away outbound packet, overflowing buffer" << LL_ENDL; + } + else + { + static LLTimer queue_timer; + if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f) + { + // Add it to the queue + LL_INFOS() << "Outbound packet queue " << mOutBufferLength << " bytes" << LL_ENDL; + queue_timer.reset(); + } + packetp = new LLPacketBuffer(host, send_buffer, buf_size); + + mOutBufferLength += packetp->getSize(); + mSendQueue.push(packetp); + } + } + + return status; +} + +bool LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host) +{ + + if (!LLProxy::isSOCKSProxyEnabled()) + { + return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort()); + } + + char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; + + proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer)); + socks_header->rsv = 0; + socks_header->addr = host.getAddress(); + socks_header->port = htons(host.getPort()); + socks_header->atype = ADDRESS_IPV4; + socks_header->frag = 0; + + memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size); + + return send_packet( h_socket, + headered_send_buffer, + buf_size + SOCKS_HEADER_SIZE, + LLProxy::getInstance()->getUDPProxy().getAddress(), + LLProxy::getInstance()->getUDPProxy().getPort()); +} diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h index 64f9f937af..f0e95f8524 100644 --- a/indra/llmessage/llpacketring.h +++ b/indra/llmessage/llpacketring.h @@ -1,101 +1,101 @@ -/**
- * @file llpacketring.h
- * @brief definition of LLPacketRing class for implementing a resend,
- * drop, or delay in packet transmissions
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPACKETRING_H
-#define LL_LLPACKETRING_H
-
-#include <queue>
-
-#include "llhost.h"
-#include "llpacketbuffer.h"
-#include "llproxy.h"
-#include "llthrottle.h"
-#include "net.h"
-
-class LLPacketRing
-{
-public:
- LLPacketRing();
- ~LLPacketRing();
-
- void cleanup();
-
- 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();
- inline LLHost getLastReceivingInterface();
-
- 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;
- LLHost mLastReceivingIF;
-
-private:
- bool sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
-};
-
-
-inline LLHost LLPacketRing::getLastSender()
-{
- return mLastSender;
-}
-
-inline LLHost LLPacketRing::getLastReceivingInterface()
-{
- return mLastReceivingIF;
-}
-
-#endif
+/** + * @file llpacketring.h + * @brief definition of LLPacketRing class for implementing a resend, + * drop, or delay in packet transmissions + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPACKETRING_H +#define LL_LLPACKETRING_H + +#include <queue> + +#include "llhost.h" +#include "llpacketbuffer.h" +#include "llproxy.h" +#include "llthrottle.h" +#include "net.h" + +class LLPacketRing +{ +public: + LLPacketRing(); + ~LLPacketRing(); + + void cleanup(); + + 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(); + inline LLHost getLastReceivingInterface(); + + 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; + LLHost mLastReceivingIF; + +private: + bool sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host); +}; + + +inline LLHost LLPacketRing::getLastSender() +{ + return mLastSender; +} + +inline LLHost LLPacketRing::getLastReceivingInterface() +{ + return mLastReceivingIF; +} + +#endif diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp index afbc74b847..296f4b5464 100644 --- a/indra/llmessage/llpartdata.cpp +++ b/indra/llmessage/llpartdata.cpp @@ -1,419 +1,419 @@ -/**
- * @file llpartdata.cpp
- * @brief Particle system data packing
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llpartdata.h"
-#include "message.h"
-
-#include "lldatapacker.h"
-#include "v4coloru.h"
-
-#include "llsdutil.h"
-#include "llsdutil_math.h"
-
-
-
-const S32 PS_PART_DATA_GLOW_SIZE = 2;
-const S32 PS_PART_DATA_BLEND_SIZE = 2;
-const S32 PS_LEGACY_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; //18
-const S32 PS_SYS_DATA_BLOCK_SIZE = 68;
-const S32 PS_MAX_DATA_BLOCK_SIZE = PS_SYS_DATA_BLOCK_SIZE+
- PS_LEGACY_PART_DATA_BLOCK_SIZE +
- PS_PART_DATA_BLEND_SIZE +
- PS_PART_DATA_GLOW_SIZE+
- 8; //two S32 size fields
-
-const S32 PS_LEGACY_DATA_BLOCK_SIZE = PS_SYS_DATA_BLOCK_SIZE + PS_LEGACY_PART_DATA_BLOCK_SIZE;
-
-const F32 MAX_PART_SCALE = 4.f;
-
-bool LLPartData::hasGlow() const
-{
- return mStartGlow > 0.f || mEndGlow > 0.f;
-}
-
-bool LLPartData::hasBlendFunc() const
-{
- return mBlendFuncSource != LLPartData::LL_PART_BF_SOURCE_ALPHA || mBlendFuncDest != LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
-}
-
-S32 LLPartData::getSize() const
-{
- S32 size = PS_LEGACY_PART_DATA_BLOCK_SIZE;
- if (hasGlow()) size += PS_PART_DATA_GLOW_SIZE;
- if (hasBlendFunc()) size += PS_PART_DATA_BLEND_SIZE;
-
- return size;
-}
-
-
-bool LLPartData::unpackLegacy(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);
-
- mStartGlow = 0.f;
- mEndGlow = 0.f;
- mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA;
- mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
-
- return true;
-}
-
-bool LLPartData::unpack(LLDataPacker &dp)
-{
- S32 size = 0;
- dp.unpackS32(size, "partsize");
-
- unpackLegacy(dp);
- size -= PS_LEGACY_PART_DATA_BLOCK_SIZE;
-
- if (mFlags & LL_PART_DATA_GLOW)
- {
- if (size < PS_PART_DATA_GLOW_SIZE) return false;
-
- U8 tmp_glow = 0;
- dp.unpackU8(tmp_glow,"pdstartglow");
- mStartGlow = tmp_glow / 255.f;
- dp.unpackU8(tmp_glow,"pdendglow");
- mEndGlow = tmp_glow / 255.f;
-
- size -= PS_PART_DATA_GLOW_SIZE;
- }
- else
- {
- mStartGlow = 0.f;
- mEndGlow = 0.f;
- }
-
- if (mFlags & LL_PART_DATA_BLEND)
- {
- if (size < PS_PART_DATA_BLEND_SIZE) return false;
- dp.unpackU8(mBlendFuncSource,"pdblendsource");
- dp.unpackU8(mBlendFuncDest,"pdblenddest");
- size -= PS_PART_DATA_BLEND_SIZE;
- }
- else
- {
- mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA;
- mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
- }
-
- if (size > 0)
- { //leftover bytes, unrecognized parameters
- U8 feh = 0;
- while (size > 0)
- { //read remaining bytes in block
- dp.unpackU8(feh, "whippang");
- size--;
- }
-
- //this particle system won't display properly, better to not show anything
- return false;
- }
-
-
- 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;
-}
-
-// static
-bool LLPartData::validBlendFunc(S32 func)
-{
- if (func >= 0
- && func < LL_PART_BF_COUNT
- && func != UNSUPPORTED_DEST_ALPHA
- && func != UNSUPPORTED_ONE_MINUS_DEST_ALPHA)
- {
- return true;
- }
- return false;
-}
-
-LLPartSysData::LLPartSysData()
-{
- mCRC = 0;
- mFlags = 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;
- mPartData.mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA;
- mPartData.mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
- mPartData.mStartGlow = 0.f;
- mPartData.mEndGlow = 0.f;
-
- 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;
-
- mNumParticles = 0;
-}
-
-bool LLPartSysData::unpackSystem(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");
- return true;
-}
-
-bool LLPartSysData::unpackLegacy(LLDataPacker &dp)
-{
- unpackSystem(dp);
- mPartData.unpackLegacy(dp);
-
- return true;
-}
-
-bool LLPartSysData::unpack(LLDataPacker &dp)
-{
- // syssize is currently unused. Adding now when modifying the 'version to make extensible in the future
- S32 size = 0;
- dp.unpackS32(size, "syssize");
-
- if (size != PS_SYS_DATA_BLOCK_SIZE)
- { //unexpected size, this viewer doesn't know how to parse this particle system
-
- //skip to LLPartData block
- U8 feh = 0;
-
- for (U32 i = 0; i < size; ++i)
- {
- dp.unpackU8(feh, "whippang");
- }
-
- dp.unpackS32(size, "partsize");
- //skip LLPartData block
- for (U32 i = 0; i < size; ++i)
- {
- dp.unpackU8(feh, "whippang");
- }
- return false;
- }
-
- unpackSystem(dp);
-
- return mPartData.unpack(dp);
-}
-
-std::ostream& operator<<(std::ostream& s, const LLPartSysData &data)
-{
- s << "Flags: " << std::hex << data.mFlags;
- s << "Pattern: " << std::hex << (U32) data.mPattern << "\n";
- s << "Source Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n";
- s << "Particle Age: " << data.mPartData.mMaxAge << "\n";
- s << "Angle: [" << data.mInnerAngle << ", " << data.mOuterAngle << "]\n";
- s << "Burst Rate: " << data.mBurstRate << "\n";
- s << "Burst Radius: " << data.mBurstRadius << "\n";
- s << "Burst Speed: [" << data.mBurstSpeedMin << ", " << data.mBurstSpeedMax << "]\n";
- s << "Burst Part Count: " << std::hex << (U32) data.mBurstPartCount << "\n";
- s << "Angular Velocity: " << data.mAngularVelocity << "\n";
- s << "Accel: " << data.mPartAccel;
- return s;
-}
-
-bool LLPartSysData::isNullPS(const S32 block_num)
-{
- U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE];
- U32 crc;
-
- S32 size;
- // Check size of block
- size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
-
- if (!size)
- {
- return true;
- }
-
- if (size > PS_MAX_DATA_BLOCK_SIZE)
- {
- //size is too big, newer particle version unsupported
- return true;
- }
-
- gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE);
-
- LLDataPackerBinaryBuffer dp(ps_data_block, size);
- if (size > PS_LEGACY_DATA_BLOCK_SIZE)
- {
- // non legacy systems pack a size before the CRC
- S32 tmp = 0;
- dp.unpackS32(tmp, "syssize");
-
- if (tmp > PS_SYS_DATA_BLOCK_SIZE)
- { //unknown system data block size, don't know how to parse it, treat as NULL
- return true;
- }
- }
-
- dp.unpackU32(crc, "crc");
-
- if (crc == 0)
- {
- return true;
- }
- return false;
-}
-
-bool LLPartSysData::unpackBlock(const S32 block_num)
-{
- U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE];
-
- // Check size of block
- S32 size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
-
- if (size > PS_MAX_DATA_BLOCK_SIZE)
- {
- // Larger packets are newer and unsupported
- return false;
- }
-
- // Get from message
- gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE);
-
- LLDataPackerBinaryBuffer dp(ps_data_block, size);
-
- if (size == PS_LEGACY_DATA_BLOCK_SIZE)
- {
- return unpackLegacy(dp);
- }
- else
- {
- return unpack(dp);
- }
-}
-
-bool LLPartSysData::isLegacyCompatible() const
-{
- return !mPartData.hasGlow() && !mPartData.hasBlendFunc();
-}
-
-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);
-}
+/** + * @file llpartdata.cpp + * @brief Particle system data packing + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llpartdata.h" +#include "message.h" + +#include "lldatapacker.h" +#include "v4coloru.h" + +#include "llsdutil.h" +#include "llsdutil_math.h" + + + +const S32 PS_PART_DATA_GLOW_SIZE = 2; +const S32 PS_PART_DATA_BLEND_SIZE = 2; +const S32 PS_LEGACY_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; //18 +const S32 PS_SYS_DATA_BLOCK_SIZE = 68; +const S32 PS_MAX_DATA_BLOCK_SIZE = PS_SYS_DATA_BLOCK_SIZE+ + PS_LEGACY_PART_DATA_BLOCK_SIZE + + PS_PART_DATA_BLEND_SIZE + + PS_PART_DATA_GLOW_SIZE+ + 8; //two S32 size fields + +const S32 PS_LEGACY_DATA_BLOCK_SIZE = PS_SYS_DATA_BLOCK_SIZE + PS_LEGACY_PART_DATA_BLOCK_SIZE; + +const F32 MAX_PART_SCALE = 4.f; + +bool LLPartData::hasGlow() const +{ + return mStartGlow > 0.f || mEndGlow > 0.f; +} + +bool LLPartData::hasBlendFunc() const +{ + return mBlendFuncSource != LLPartData::LL_PART_BF_SOURCE_ALPHA || mBlendFuncDest != LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA; +} + +S32 LLPartData::getSize() const +{ + S32 size = PS_LEGACY_PART_DATA_BLOCK_SIZE; + if (hasGlow()) size += PS_PART_DATA_GLOW_SIZE; + if (hasBlendFunc()) size += PS_PART_DATA_BLEND_SIZE; + + return size; +} + + +bool LLPartData::unpackLegacy(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); + + mStartGlow = 0.f; + mEndGlow = 0.f; + mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA; + mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA; + + return true; +} + +bool LLPartData::unpack(LLDataPacker &dp) +{ + S32 size = 0; + dp.unpackS32(size, "partsize"); + + unpackLegacy(dp); + size -= PS_LEGACY_PART_DATA_BLOCK_SIZE; + + if (mFlags & LL_PART_DATA_GLOW) + { + if (size < PS_PART_DATA_GLOW_SIZE) return false; + + U8 tmp_glow = 0; + dp.unpackU8(tmp_glow,"pdstartglow"); + mStartGlow = tmp_glow / 255.f; + dp.unpackU8(tmp_glow,"pdendglow"); + mEndGlow = tmp_glow / 255.f; + + size -= PS_PART_DATA_GLOW_SIZE; + } + else + { + mStartGlow = 0.f; + mEndGlow = 0.f; + } + + if (mFlags & LL_PART_DATA_BLEND) + { + if (size < PS_PART_DATA_BLEND_SIZE) return false; + dp.unpackU8(mBlendFuncSource,"pdblendsource"); + dp.unpackU8(mBlendFuncDest,"pdblenddest"); + size -= PS_PART_DATA_BLEND_SIZE; + } + else + { + mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA; + mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA; + } + + if (size > 0) + { //leftover bytes, unrecognized parameters + U8 feh = 0; + while (size > 0) + { //read remaining bytes in block + dp.unpackU8(feh, "whippang"); + size--; + } + + //this particle system won't display properly, better to not show anything + return false; + } + + + 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; +} + +// static +bool LLPartData::validBlendFunc(S32 func) +{ + if (func >= 0 + && func < LL_PART_BF_COUNT + && func != UNSUPPORTED_DEST_ALPHA + && func != UNSUPPORTED_ONE_MINUS_DEST_ALPHA) + { + return true; + } + return false; +} + +LLPartSysData::LLPartSysData() +{ + mCRC = 0; + mFlags = 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; + mPartData.mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA; + mPartData.mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA; + mPartData.mStartGlow = 0.f; + mPartData.mEndGlow = 0.f; + + 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; + + mNumParticles = 0; +} + +bool LLPartSysData::unpackSystem(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"); + return true; +} + +bool LLPartSysData::unpackLegacy(LLDataPacker &dp) +{ + unpackSystem(dp); + mPartData.unpackLegacy(dp); + + return true; +} + +bool LLPartSysData::unpack(LLDataPacker &dp) +{ + // syssize is currently unused. Adding now when modifying the 'version to make extensible in the future + S32 size = 0; + dp.unpackS32(size, "syssize"); + + if (size != PS_SYS_DATA_BLOCK_SIZE) + { //unexpected size, this viewer doesn't know how to parse this particle system + + //skip to LLPartData block + U8 feh = 0; + + for (U32 i = 0; i < size; ++i) + { + dp.unpackU8(feh, "whippang"); + } + + dp.unpackS32(size, "partsize"); + //skip LLPartData block + for (U32 i = 0; i < size; ++i) + { + dp.unpackU8(feh, "whippang"); + } + return false; + } + + unpackSystem(dp); + + return mPartData.unpack(dp); +} + +std::ostream& operator<<(std::ostream& s, const LLPartSysData &data) +{ + s << "Flags: " << std::hex << data.mFlags; + s << "Pattern: " << std::hex << (U32) data.mPattern << "\n"; + s << "Source Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n"; + s << "Particle Age: " << data.mPartData.mMaxAge << "\n"; + s << "Angle: [" << data.mInnerAngle << ", " << data.mOuterAngle << "]\n"; + s << "Burst Rate: " << data.mBurstRate << "\n"; + s << "Burst Radius: " << data.mBurstRadius << "\n"; + s << "Burst Speed: [" << data.mBurstSpeedMin << ", " << data.mBurstSpeedMax << "]\n"; + s << "Burst Part Count: " << std::hex << (U32) data.mBurstPartCount << "\n"; + s << "Angular Velocity: " << data.mAngularVelocity << "\n"; + s << "Accel: " << data.mPartAccel; + return s; +} + +bool LLPartSysData::isNullPS(const S32 block_num) +{ + U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE]; + U32 crc; + + S32 size; + // Check size of block + size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock"); + + if (!size) + { + return true; + } + + if (size > PS_MAX_DATA_BLOCK_SIZE) + { + //size is too big, newer particle version unsupported + return true; + } + + gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE); + + LLDataPackerBinaryBuffer dp(ps_data_block, size); + if (size > PS_LEGACY_DATA_BLOCK_SIZE) + { + // non legacy systems pack a size before the CRC + S32 tmp = 0; + dp.unpackS32(tmp, "syssize"); + + if (tmp > PS_SYS_DATA_BLOCK_SIZE) + { //unknown system data block size, don't know how to parse it, treat as NULL + return true; + } + } + + dp.unpackU32(crc, "crc"); + + if (crc == 0) + { + return true; + } + return false; +} + +bool LLPartSysData::unpackBlock(const S32 block_num) +{ + U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE]; + + // Check size of block + S32 size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock"); + + if (size > PS_MAX_DATA_BLOCK_SIZE) + { + // Larger packets are newer and unsupported + return false; + } + + // Get from message + gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE); + + LLDataPackerBinaryBuffer dp(ps_data_block, size); + + if (size == PS_LEGACY_DATA_BLOCK_SIZE) + { + return unpackLegacy(dp); + } + else + { + return unpack(dp); + } +} + +bool LLPartSysData::isLegacyCompatible() const +{ + return !mPartData.hasGlow() && !mPartData.hasBlendFunc(); +} + +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 index 70c425b0a5..49b9e51a3d 100644 --- a/indra/llmessage/llpartdata.h +++ b/indra/llmessage/llpartdata.h @@ -1,279 +1,279 @@ -/**
- * @file llpartdata.h
- * @brief Particle system data packing
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPARTDATA_H
-#define LL_LLPARTDATA_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,
-
- LLPS_PART_BLEND_FUNC_SOURCE,
- LLPS_PART_BLEND_FUNC_DEST,
- LLPS_PART_START_GLOW,
- LLPS_PART_END_GLOW
-};
-
-
-class LLPartData
-{
-public:
- LLPartData() :
- mFlags(0),
- mMaxAge(0.f),
- mParameter(0.f)
- {
- }
- bool unpackLegacy(LLDataPacker &dp);
- bool unpack(LLDataPacker &dp);
-
- bool pack(LLDataPacker &dp);
-
- bool hasGlow() const;
- bool hasBlendFunc() const;
-
- // 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
- LL_PART_RIBBON_MASK = 0x400, // Particles are joined together into one continuous triangle strip
-
- // Not implemented yet!
- //LL_PART_RANDOM_ACCEL_MASK = 0x100, // Particles have random acceleration
- //LL_PART_RANDOM_VEL_MASK = 0x200, // Particles have random velocity shifts"
- //LL_PART_TRAIL_MASK = 0x400, // Particles have historical "trails"
-
- //sYSTEM SET FLAGS
- LL_PART_DATA_GLOW = 0x10000,
- LL_PART_DATA_BLEND = 0x20000,
-
- // Viewer side use only!
- LL_PART_HUD = 0x40000000,
- LL_PART_DEAD_MASK = 0x80000000,
- };
-
- enum
- {
- LL_PART_BF_ONE = 0,
- LL_PART_BF_ZERO = 1,
- LL_PART_BF_DEST_COLOR = 2,
- LL_PART_BF_SOURCE_COLOR = 3,
- LL_PART_BF_ONE_MINUS_DEST_COLOR = 4,
- LL_PART_BF_ONE_MINUS_SOURCE_COLOR = 5,
- UNSUPPORTED_DEST_ALPHA = 6,
- LL_PART_BF_SOURCE_ALPHA = 7,
- UNSUPPORTED_ONE_MINUS_DEST_ALPHA = 8,
- LL_PART_BF_ONE_MINUS_SOURCE_ALPHA = 9,
- LL_PART_BF_COUNT = 10
- };
-
- static bool validBlendFunc(S32 func);
-
- 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;
-
-private:
- S32 getSize() const;
-
- // 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
-
- F32 mStartGlow;
- F32 mEndGlow;
-
- U8 mBlendFuncSource;
- U8 mBlendFuncDest;
-};
-
-
-class LLPartSysData
-{
-public:
- LLPartSysData();
-
- bool unpack(LLDataPacker &dp);
- bool unpackLegacy(LLDataPacker &dp);
- bool unpackBlock(const S32 block_num);
-
- static bool isNullPS(const S32 block_num); // Returns false if this is a "NULL" particle system (i.e. no system)
-
- bool isLegacyCompatible() const;
-
- // 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();
-
- friend std::ostream& operator<<(std::ostream& s, const LLPartSysData &data); // Stream a
-
- S32 getdataBlockSize() const;
-
-private:
- bool unpackSystem(LLDataPacker &dp);
-
-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
+/** + * @file llpartdata.h + * @brief Particle system data packing + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPARTDATA_H +#define LL_LLPARTDATA_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, + + LLPS_PART_BLEND_FUNC_SOURCE, + LLPS_PART_BLEND_FUNC_DEST, + LLPS_PART_START_GLOW, + LLPS_PART_END_GLOW +}; + + +class LLPartData +{ +public: + LLPartData() : + mFlags(0), + mMaxAge(0.f), + mParameter(0.f) + { + } + bool unpackLegacy(LLDataPacker &dp); + bool unpack(LLDataPacker &dp); + + bool pack(LLDataPacker &dp); + + bool hasGlow() const; + bool hasBlendFunc() const; + + // 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 + LL_PART_RIBBON_MASK = 0x400, // Particles are joined together into one continuous triangle strip + + // Not implemented yet! + //LL_PART_RANDOM_ACCEL_MASK = 0x100, // Particles have random acceleration + //LL_PART_RANDOM_VEL_MASK = 0x200, // Particles have random velocity shifts" + //LL_PART_TRAIL_MASK = 0x400, // Particles have historical "trails" + + //sYSTEM SET FLAGS + LL_PART_DATA_GLOW = 0x10000, + LL_PART_DATA_BLEND = 0x20000, + + // Viewer side use only! + LL_PART_HUD = 0x40000000, + LL_PART_DEAD_MASK = 0x80000000, + }; + + enum + { + LL_PART_BF_ONE = 0, + LL_PART_BF_ZERO = 1, + LL_PART_BF_DEST_COLOR = 2, + LL_PART_BF_SOURCE_COLOR = 3, + LL_PART_BF_ONE_MINUS_DEST_COLOR = 4, + LL_PART_BF_ONE_MINUS_SOURCE_COLOR = 5, + UNSUPPORTED_DEST_ALPHA = 6, + LL_PART_BF_SOURCE_ALPHA = 7, + UNSUPPORTED_ONE_MINUS_DEST_ALPHA = 8, + LL_PART_BF_ONE_MINUS_SOURCE_ALPHA = 9, + LL_PART_BF_COUNT = 10 + }; + + static bool validBlendFunc(S32 func); + + 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; + +private: + S32 getSize() const; + + // 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 + + F32 mStartGlow; + F32 mEndGlow; + + U8 mBlendFuncSource; + U8 mBlendFuncDest; +}; + + +class LLPartSysData +{ +public: + LLPartSysData(); + + bool unpack(LLDataPacker &dp); + bool unpackLegacy(LLDataPacker &dp); + bool unpackBlock(const S32 block_num); + + static bool isNullPS(const S32 block_num); // Returns false if this is a "NULL" particle system (i.e. no system) + + bool isLegacyCompatible() const; + + // 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(); + + friend std::ostream& operator<<(std::ostream& s, const LLPartSysData &data); // Stream a + + S32 getdataBlockSize() const; + +private: + bool unpackSystem(LLDataPacker &dp); + +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 index 646f8aa2ca..d3b75cf86b 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -1,1150 +1,1150 @@ -/**
- * @file llpumpio.cpp
- * @author Phoenix
- * @date 2004-11-21
- * @brief Implementation of the i/o pump and related functions.
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llpumpio.h"
-
-#include <map>
-#include <set>
-#include "apr_poll.h"
-
-#include "llapr.h"
-#include "llfasttimer.h"
-#include "llstl.h"
-
-// These should not be enabled in production, but they can be
-// intensely useful during development for finding certain kinds of
-// bugs.
-#if LL_LINUX
-//#define LL_DEBUG_PIPE_TYPE_IN_PUMP 1
-//#define LL_DEBUG_POLL_FILE_DESCRIPTORS 1
-#if LL_DEBUG_POLL_FILE_DESCRIPTORS
-#include "apr_portable.h"
-#endif
-#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.
-static const S32 DEFAULT_POLL_TIMEOUT = 0;
-
-// 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
-
-//
-// local functions
-//
-void ll_debug_poll_fd(const char* msg, const apr_pollfd_t* poll)
-{
-#if LL_DEBUG_POLL_FILE_DESCRIPTORS
- if(!poll)
- {
- LL_DEBUGS() << "Poll -- " << (msg?msg:"") << ": no pollfd." << LL_ENDL;
- return;
- }
- if(poll->desc.s)
- {
- apr_os_sock_t os_sock;
- if(APR_SUCCESS == apr_os_sock_get(&os_sock, poll->desc.s))
- {
- LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " on fd " << os_sock
- << " at " << poll->desc.s << LL_ENDL;
- }
- else
- {
- LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " no fd "
- << " at " << poll->desc.s << LL_ENDL;
- }
- }
- else if(poll->desc.f)
- {
- apr_os_file_t os_file;
- if(APR_SUCCESS == apr_os_file_get(&os_file, poll->desc.f))
- {
- LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " on fd " << os_file
- << " at " << poll->desc.f << LL_ENDL;
- }
- else
- {
- LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " no fd "
- << " at " << poll->desc.f << LL_ENDL;
- }
- }
- else
- {
- LL_DEBUGS() << "Poll -- " << (msg?msg:"") << ": no descriptor." << LL_ENDL;
- }
-#endif
-}
-
-/**
- * @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)
- {
- 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),
- mCurrentChain(mRunningChains.end())
-{
- mCurrentChain = mRunningChains.end();
-
- initialize(pool);
-}
-
-LLPumpIO::~LLPumpIO()
-{
- cleanup();
-}
-
-bool LLPumpIO::prime(apr_pool_t* pool)
-{
- cleanup();
- initialize(pool);
- return pool != nullptr;
-}
-
-bool LLPumpIO::addChain(const chain_t& chain, F32 timeout, bool has_curl_request)
-{
- if (chain.empty())
- return false;
-
- LLChainInfo info;
- info.mHasCurlRequest = has_curl_request;
- info.setTimeoutSeconds(timeout);
- info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray);
- info.mData->setThreaded(has_curl_request);
- LLLinkInfo link;
-#if LL_DEBUG_PIPE_TYPE_IN_PUMP
- LL_DEBUGS() << "LLPumpIO::addChain() " << chain[0] << " '"
- << typeid(*(chain[0])).name() << "'" << LL_ENDL;
-#else
- LL_DEBUGS() << "LLPumpIO::addChain() " << chain[0] <<LL_ENDL;
-#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)
-{
- // 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_DEBUG_PIPE_TYPE_IN_PUMP
- LL_DEBUGS() << "LLPumpIO::addChain() " << links[0].mPipe << " '"
- << typeid(*(links[0].mPipe)).name() << "'" << LL_ENDL;
-#else
- LL_DEBUGS() << "LLPumpIO::addChain() " << links[0].mPipe << LL_ENDL;
-#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 (mRunningChains.end() == mCurrentChain)
- {
- return false;
- }
-
- (*mCurrentChain).setTimeoutSeconds(timeout);
- return true;
-}
-
-void LLPumpIO::adjustTimeoutSeconds(F32 delta)
-{
- // Ensure a chain is running
- if (mRunningChains.end() != mCurrentChain)
- {
- (*mCurrentChain).adjustTimeoutSeconds(delta);
- }
-}
-
-static std::string events_2_string(apr_int16_t events)
-{
- std::ostringstream ostr;
- if (events & APR_POLLIN)
- {
- ostr << "read,";
- }
- if (events & APR_POLLPRI)
- {
- ostr << "priority,";
- }
- if (events & APR_POLLOUT)
- {
- ostr << "write,";
- }
- if (events & APR_POLLERR)
- {
- ostr << "error,";
- }
- if (events & APR_POLLHUP)
- {
- ostr << "hangup,";
- }
- if (events & APR_POLLNVAL)
- {
- ostr << "invalid,";
- }
- return chop_tail_copy(ostr.str(), 1);
-}
-
-bool LLPumpIO::setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll)
-{
- if (!pipe)
- return false;
- ll_debug_poll_fd("Set conditional", poll);
-
- LL_DEBUGS() << "Setting conditionals (" << (poll ? events_2_string(poll->reqevents) :"null")
- << ") "
-#if LL_DEBUG_PIPE_TYPE_IN_PUMP
- << "on pipe " << typeid(*pipe).name()
-#endif
- << " at " << pipe << LL_ENDL;
-
- // remove any matching poll file descriptors for this pipe.
- LLIOPipe::ptr_t pipe_ptr(pipe);
- LLChainInfo::conditionals_t::iterator it;
- it = (*mCurrentChain).mDescriptors.begin();
- while(it != (*mCurrentChain).mDescriptors.end())
- {
- LLChainInfo::pipe_conditional_t& value = (*it);
- if(pipe_ptr == value.first)
- {
- ll_delete_apr_pollset_fd_client_data()(value);
- it = (*mCurrentChain).mDescriptors.erase(it);
- mRebuildPollset = true;
- }
- else
- {
- ++it;
- }
- }
-
- if(!poll)
- {
- mRebuildPollset = true;
- return true;
- }
- LLChainInfo::pipe_conditional_t value;
- value.first = pipe_ptr;
- value.second = *poll;
- value.second.rtnevents = 0;
- 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;
-}
-
-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(mRunningChains.end() == 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.
- 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
-{
- if(mRunningChains.end() == mCurrentChain)
- {
- return false;
- }
- std::copy(
- (*mCurrentChain).mChainLinks.begin(),
- (*mCurrentChain).mChainLinks.end(),
- std::back_insert_iterator<links_t>(links));
- return true;
-}
-
-void LLPumpIO::pump()
-{
- pump(DEFAULT_POLL_TIMEOUT);
-}
-
-LLPumpIO::current_chain_t LLPumpIO::removeRunningChain(LLPumpIO::current_chain_t& run_chain)
-{
- std::for_each(
- (*run_chain).mDescriptors.begin(),
- (*run_chain).mDescriptors.end(),
- ll_delete_apr_pollset_fd_client_data());
- return mRunningChains.erase(run_chain);
-}
-
-//timeout is in microseconds
-void LLPumpIO::pump(const S32& poll_timeout)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
- //LL_INFOS() << "LLPumpIO::pump()" << LL_ENDL;
-
- // Run any pending runners.
- mRunner.run();
-
- // We need to move all of the pending heads over to the running
- // chains.
- PUMP_DEBUG;
- if(true)
- {
- // 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;
- //LL_DEBUGS() << "Pushing " << mPendingChains.size() << "." << LL_ENDL;
- 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
- // *TODO: may want to pass in a poll timeout so it works correctly
- // in single and multi threaded processes.
- PUMP_DEBUG;
- typedef std::map<S32, S32> signal_client_t;
- signal_client_t signalled_client;
- const apr_pollfd_t* poll_fd = NULL;
- if(mPollset)
- {
- PUMP_DEBUG;
- //LL_INFOS() << "polling" << LL_ENDL;
- S32 count = 0;
- S32 client_id = 0;
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
- apr_pollset_poll(mPollset, poll_timeout, &count, &poll_fd);
- }
- PUMP_DEBUG;
- for(S32 ii = 0; ii < count; ++ii)
- {
- ll_debug_poll_fd("Signalled pipe", &poll_fd[ii]);
- client_id = *((S32*)poll_fd[ii].client_data);
- signalled_client[client_id] = ii;
- }
- 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
- //LL_DEBUGS() << "Running chain count: " << mRunningChains.size() << LL_ENDL;
- running_chains_t::iterator run_chain = mRunningChains.begin();
- bool process_this_chain = false;
- while( 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;
- LL_INFOS() << "Error handler forgot to reset timeout. "
- << "Resetting to " << DEFAULT_CHAIN_EXPIRY_SECS
- << " seconds." << LL_ENDL;
- (*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
- LL_DEBUGS() << "Removing chain "
- << (*run_chain).mChainLinks[0].mPipe
- << " '"
- << typeid(*((*run_chain).mChainLinks[0].mPipe)).name()
- << "' because it timed out." << LL_ENDL;
-#else
-// LL_DEBUGS() << "Removing chain "
-// << (*run_chain).mChainLinks[0].mPipe
-// << " because we reached the end." << LL_ENDL;
-#endif
- run_chain = removeRunningChain(run_chain);
- continue;
- }
- }
- else if(isChainExpired(*run_chain))
- {
- run_chain = removeRunningChain(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;
- //LL_DEBUGS() << "no conditionals - processing" << LL_ENDL;
- }
- else
- {
- PUMP_DEBUG;
- //LL_DEBUGS() << "checking conditionals" << LL_ENDL;
- // 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;
- signal_client_t::iterator signal;
- for(; it != end; ++it)
- {
- PUMP_DEBUG;
- client_id = *((S32*)((*it).second.client_data));
- signal = signalled_client.find(client_id);
- if (signal == not_signalled) continue;
- static const apr_int16_t POLL_CHAIN_ERROR =
- APR_POLLHUP | APR_POLLNVAL | APR_POLLERR;
- const apr_pollfd_t* poll = &(poll_fd[(*signal).second]);
- if(poll->rtnevents & POLL_CHAIN_ERROR)
- {
- // Potential eror condition has been
- // returned. If HUP was one of them, we pass
- // that as the error even though there may be
- // more. If there are in fact more errors,
- // we'll just wait for that detection until
- // the next pump() cycle to catch it so that
- // the logic here gets no more strained than
- // it already is.
- LLIOPipe::EStatus error_status;
- if(poll->rtnevents & APR_POLLHUP)
- error_status = LLIOPipe::STATUS_LOST_CONNECTION;
- else
- error_status = LLIOPipe::STATUS_ERROR;
- if(handleChainError(*run_chain, error_status)) break;
- ll_debug_poll_fd("Removing pipe", poll);
- LL_WARNS() << "Removing pipe "
- << (*run_chain).mChainLinks[0].mPipe
- << " '"
-#if LL_DEBUG_PIPE_TYPE_IN_PUMP
- << typeid(
- *((*run_chain).mChainLinks[0].mPipe)).name()
-#endif
- << "' because: "
- << events_2_string(poll->rtnevents)
- << LL_ENDL;
- (*run_chain).mHead = (*run_chain).mChainLinks.end();
- break;
- }
-
- // at least 1 fd got signalled, and there were no
- // errors. That means we process this chain.
- process_this_chain = true;
- break;
- }
- }
- }
- 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
- LL_DEBUGS() << "Removing chain " << (*run_chain).mChainLinks[0].mPipe
- << " '"
- << typeid(*((*run_chain).mChainLinks[0].mPipe)).name()
- << "' because we reached the end." << LL_ENDL;
-#else
-// LL_DEBUGS() << "Removing chain " << (*run_chain).mChainLinks[0].mPipe
-// << " because we reached the end." << LL_ENDL;
-#endif
-
- PUMP_DEBUG;
- // This chain is done. Clean up any allocated memory and
- // erase the chain info.
- run_chain = removeRunningChain(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 = mRunningChains.end();
- END_PUMP_DEBUG;
-}
-
-bool LLPumpIO::respond(LLIOPipe* pipe)
-{
- if(NULL == pipe) return false;
-
- 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)
-{
- // 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;
-
- // Add the callback response
- LLChainInfo info;
- info.mChainLinks = links;
- info.mData = data;
- info.mContext = context;
- mPendingCallbacks.push_back(info);
- return true;
-}
-
-void LLPumpIO::callback()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
- //LL_INFOS() << "LLPumpIO::callback()" << LL_ENDL;
- if(true)
- {
- 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)
-{
- switch(op)
- {
- case PAUSE:
- mState = PAUSING;
- break;
- case RESUME:
- mState = NORMAL;
- break;
- default:
- // no-op
- break;
- }
-}
-
-void LLPumpIO::initialize(apr_pool_t* pool)
-{
- if(!pool) return;
- mPool = pool;
-}
-
-void LLPumpIO::cleanup()
-{
- if(mPollset)
- {
-// LL_DEBUGS() << "cleaning up pollset" << LL_ENDL;
- apr_pollset_destroy(mPollset);
- mPollset = NULL;
- }
- if(mCurrentPool)
- {
- apr_pool_destroy(mCurrentPool);
- mCurrentPool = NULL;
- }
- mPool = NULL;
-}
-
-void LLPumpIO::rebuildPollset()
-{
-// LL_DEBUGS() << "LLPumpIO::rebuildPollset()" << LL_ENDL;
- if(mPollset)
- {
- //LL_DEBUGS() << "destroying pollset" << LL_ENDL;
- 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();
- }
- //LL_DEBUGS() << "found " << size << " descriptors." << LL_ENDL;
- 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;
- 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
- LL_INFOS() << "Processing " << typeid(*((*it).mPipe)).name() << "."
- << LL_ENDL;
-#else
- LL_INFOS() << "Processing link " << (*it).mPipe << "." << LL_ENDL;
-#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';
- LL_INFOS() << "CHANNEL IN(" << (*it).mChannels.in() << "): "
- << buf << LL_ENDL;
- delete[] buf;
- buf = NULL;
- }
- else
- {
- LL_INFOS() << "CHANNEL IN(" << (*it).mChannels.in()<< "): (null)"
- << LL_ENDL;
- }
- }
-#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';
- LL_INFOS() << "CHANNEL OUT(" << (*it).mChannels.out()<< "): "
- << buf << LL_ENDL;
- delete[] buf;
- buf = NULL;
- }
- else
- {
- LL_INFOS() << "CHANNEL OUT(" << (*it).mChannels.out()<< "): (null)"
- << LL_ENDL;
- }
- }
-#endif
-
-#if LL_DEBUG_PROCESS_RETURN_VALUE
- // Only bother with the success codes - error codes are logged
- // below.
- if(LLIOPipe::isSuccess(status))
- {
- LL_INFOS() << "Pipe returned: '"
-#if LL_DEBUG_PIPE_TYPE_IN_PUMP
- << typeid(*((*it).mPipe)).name() << "':'"
-#endif
- << LLIOPipe::lookupStatusString(status) << "'" << LL_ENDL;
- }
-#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))
- {
- LL_INFOS() << "Pump generated pipe err: '"
-#if LL_DEBUG_PIPE_TYPE_IN_PUMP
- << typeid(*((*it).mPipe)).name() << "':'"
-#endif
- << LLIOPipe::lookupStatusString(status)
- << "'" << LL_ENDL;
-#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';
- LL_INFOS() << "Input After Error: " << buf << LL_ENDL;
- delete[] buf;
- buf = NULL;
- }
- else
- {
- LL_INFOS() << "Input After Error: (null)" << LL_ENDL;
- }
- }
- else
- {
- LL_INFOS() << "Input After Error: (null)" << LL_ENDL;
- }
-#endif
- keep_going = false;
- chain.mHead = it;
- if(!handleChainError(chain, status))
- {
- chain.mHead = end;
- }
- }
- else
- {
- LL_INFOS() << "Unhandled status code: " << status << ":"
- << LLIOPipe::lookupStatusString(status) << LL_ENDL;
- }
- break;
- }
- PUMP_DEBUG;
- } while(keep_going && (++it != end));
- PUMP_DEBUG;
-}
-
-bool LLPumpIO::isChainExpired(LLChainInfo& chain)
-{
- if(!chain.mHasCurlRequest)
- {
- return false ;
- }
-
- for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter)
- {
- if(!(*iter).mPipe->isValid())
- {
- return true ;
- }
- }
-
- return false ;
-}
-
-bool LLPumpIO::handleChainError(
- LLChainInfo& chain,
- LLIOPipe::EStatus error)
-{
- 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
- LL_DEBUGS() << "Passing error to " << typeid(*((*rit).mPipe)).name()
- << "." << LL_ENDL;
-#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
- LL_DEBUGS() << "Pipe " << typeid(*((*rit).mPipe)).name()
- << " returned code to stop error handler." << LL_ENDL;
-#endif
- keep_going = false;
- break;
- case LLIOPipe::STATUS_EXPIRED:
- keep_going = false;
- break ;
- default:
- if(LLIOPipe::isSuccess(error))
- {
- LL_INFOS() << "Unhandled status code: " << error << ":"
- << LLIOPipe::lookupStatusString(error) << LL_ENDL;
- 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),
- mHasCurlRequest(false)
-{
- mTimer.setTimerExpirySec(DEFAULT_CHAIN_EXPIRY_SECS);
-}
-
-void LLPumpIO::LLChainInfo::setTimeoutSeconds(F32 timeout)
-{
- if(timeout > 0.0f)
- {
- mTimer.start();
- mTimer.reset();
- mTimer.setTimerExpirySec(timeout);
- }
- else
- {
- mTimer.stop();
- }
-}
-
-void LLPumpIO::LLChainInfo::adjustTimeoutSeconds(F32 delta)
-{
- if(mTimer.getStarted())
- {
- F64 expiry = mTimer.expiresAt();
- expiry += delta;
- mTimer.setExpiryAt(expiry);
- }
-}
+/** + * @file llpumpio.cpp + * @author Phoenix + * @date 2004-11-21 + * @brief Implementation of the i/o pump and related functions. + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llpumpio.h" + +#include <map> +#include <set> +#include "apr_poll.h" + +#include "llapr.h" +#include "llfasttimer.h" +#include "llstl.h" + +// These should not be enabled in production, but they can be +// intensely useful during development for finding certain kinds of +// bugs. +#if LL_LINUX +//#define LL_DEBUG_PIPE_TYPE_IN_PUMP 1 +//#define LL_DEBUG_POLL_FILE_DESCRIPTORS 1 +#if LL_DEBUG_POLL_FILE_DESCRIPTORS +#include "apr_portable.h" +#endif +#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. +static const S32 DEFAULT_POLL_TIMEOUT = 0; + +// 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 + +// +// local functions +// +void ll_debug_poll_fd(const char* msg, const apr_pollfd_t* poll) +{ +#if LL_DEBUG_POLL_FILE_DESCRIPTORS + if(!poll) + { + LL_DEBUGS() << "Poll -- " << (msg?msg:"") << ": no pollfd." << LL_ENDL; + return; + } + if(poll->desc.s) + { + apr_os_sock_t os_sock; + if(APR_SUCCESS == apr_os_sock_get(&os_sock, poll->desc.s)) + { + LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " on fd " << os_sock + << " at " << poll->desc.s << LL_ENDL; + } + else + { + LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " no fd " + << " at " << poll->desc.s << LL_ENDL; + } + } + else if(poll->desc.f) + { + apr_os_file_t os_file; + if(APR_SUCCESS == apr_os_file_get(&os_file, poll->desc.f)) + { + LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " on fd " << os_file + << " at " << poll->desc.f << LL_ENDL; + } + else + { + LL_DEBUGS() << "Poll -- " << (msg?msg:"") << " no fd " + << " at " << poll->desc.f << LL_ENDL; + } + } + else + { + LL_DEBUGS() << "Poll -- " << (msg?msg:"") << ": no descriptor." << LL_ENDL; + } +#endif +} + +/** + * @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) + { + 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), + mCurrentChain(mRunningChains.end()) +{ + mCurrentChain = mRunningChains.end(); + + initialize(pool); +} + +LLPumpIO::~LLPumpIO() +{ + cleanup(); +} + +bool LLPumpIO::prime(apr_pool_t* pool) +{ + cleanup(); + initialize(pool); + return pool != nullptr; +} + +bool LLPumpIO::addChain(const chain_t& chain, F32 timeout, bool has_curl_request) +{ + if (chain.empty()) + return false; + + LLChainInfo info; + info.mHasCurlRequest = has_curl_request; + info.setTimeoutSeconds(timeout); + info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray); + info.mData->setThreaded(has_curl_request); + LLLinkInfo link; +#if LL_DEBUG_PIPE_TYPE_IN_PUMP + LL_DEBUGS() << "LLPumpIO::addChain() " << chain[0] << " '" + << typeid(*(chain[0])).name() << "'" << LL_ENDL; +#else + LL_DEBUGS() << "LLPumpIO::addChain() " << chain[0] <<LL_ENDL; +#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) +{ + // 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_DEBUG_PIPE_TYPE_IN_PUMP + LL_DEBUGS() << "LLPumpIO::addChain() " << links[0].mPipe << " '" + << typeid(*(links[0].mPipe)).name() << "'" << LL_ENDL; +#else + LL_DEBUGS() << "LLPumpIO::addChain() " << links[0].mPipe << LL_ENDL; +#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 (mRunningChains.end() == mCurrentChain) + { + return false; + } + + (*mCurrentChain).setTimeoutSeconds(timeout); + return true; +} + +void LLPumpIO::adjustTimeoutSeconds(F32 delta) +{ + // Ensure a chain is running + if (mRunningChains.end() != mCurrentChain) + { + (*mCurrentChain).adjustTimeoutSeconds(delta); + } +} + +static std::string events_2_string(apr_int16_t events) +{ + std::ostringstream ostr; + if (events & APR_POLLIN) + { + ostr << "read,"; + } + if (events & APR_POLLPRI) + { + ostr << "priority,"; + } + if (events & APR_POLLOUT) + { + ostr << "write,"; + } + if (events & APR_POLLERR) + { + ostr << "error,"; + } + if (events & APR_POLLHUP) + { + ostr << "hangup,"; + } + if (events & APR_POLLNVAL) + { + ostr << "invalid,"; + } + return chop_tail_copy(ostr.str(), 1); +} + +bool LLPumpIO::setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll) +{ + if (!pipe) + return false; + ll_debug_poll_fd("Set conditional", poll); + + LL_DEBUGS() << "Setting conditionals (" << (poll ? events_2_string(poll->reqevents) :"null") + << ") " +#if LL_DEBUG_PIPE_TYPE_IN_PUMP + << "on pipe " << typeid(*pipe).name() +#endif + << " at " << pipe << LL_ENDL; + + // remove any matching poll file descriptors for this pipe. + LLIOPipe::ptr_t pipe_ptr(pipe); + LLChainInfo::conditionals_t::iterator it; + it = (*mCurrentChain).mDescriptors.begin(); + while(it != (*mCurrentChain).mDescriptors.end()) + { + LLChainInfo::pipe_conditional_t& value = (*it); + if(pipe_ptr == value.first) + { + ll_delete_apr_pollset_fd_client_data()(value); + it = (*mCurrentChain).mDescriptors.erase(it); + mRebuildPollset = true; + } + else + { + ++it; + } + } + + if(!poll) + { + mRebuildPollset = true; + return true; + } + LLChainInfo::pipe_conditional_t value; + value.first = pipe_ptr; + value.second = *poll; + value.second.rtnevents = 0; + 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; +} + +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(mRunningChains.end() == 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. + 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 +{ + if(mRunningChains.end() == mCurrentChain) + { + return false; + } + std::copy( + (*mCurrentChain).mChainLinks.begin(), + (*mCurrentChain).mChainLinks.end(), + std::back_insert_iterator<links_t>(links)); + return true; +} + +void LLPumpIO::pump() +{ + pump(DEFAULT_POLL_TIMEOUT); +} + +LLPumpIO::current_chain_t LLPumpIO::removeRunningChain(LLPumpIO::current_chain_t& run_chain) +{ + std::for_each( + (*run_chain).mDescriptors.begin(), + (*run_chain).mDescriptors.end(), + ll_delete_apr_pollset_fd_client_data()); + return mRunningChains.erase(run_chain); +} + +//timeout is in microseconds +void LLPumpIO::pump(const S32& poll_timeout) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; + //LL_INFOS() << "LLPumpIO::pump()" << LL_ENDL; + + // Run any pending runners. + mRunner.run(); + + // We need to move all of the pending heads over to the running + // chains. + PUMP_DEBUG; + if(true) + { + // 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; + //LL_DEBUGS() << "Pushing " << mPendingChains.size() << "." << LL_ENDL; + 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 + // *TODO: may want to pass in a poll timeout so it works correctly + // in single and multi threaded processes. + PUMP_DEBUG; + typedef std::map<S32, S32> signal_client_t; + signal_client_t signalled_client; + const apr_pollfd_t* poll_fd = NULL; + if(mPollset) + { + PUMP_DEBUG; + //LL_INFOS() << "polling" << LL_ENDL; + S32 count = 0; + S32 client_id = 0; + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; + apr_pollset_poll(mPollset, poll_timeout, &count, &poll_fd); + } + PUMP_DEBUG; + for(S32 ii = 0; ii < count; ++ii) + { + ll_debug_poll_fd("Signalled pipe", &poll_fd[ii]); + client_id = *((S32*)poll_fd[ii].client_data); + signalled_client[client_id] = ii; + } + 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 + //LL_DEBUGS() << "Running chain count: " << mRunningChains.size() << LL_ENDL; + running_chains_t::iterator run_chain = mRunningChains.begin(); + bool process_this_chain = false; + while( 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; + LL_INFOS() << "Error handler forgot to reset timeout. " + << "Resetting to " << DEFAULT_CHAIN_EXPIRY_SECS + << " seconds." << LL_ENDL; + (*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 + LL_DEBUGS() << "Removing chain " + << (*run_chain).mChainLinks[0].mPipe + << " '" + << typeid(*((*run_chain).mChainLinks[0].mPipe)).name() + << "' because it timed out." << LL_ENDL; +#else +// LL_DEBUGS() << "Removing chain " +// << (*run_chain).mChainLinks[0].mPipe +// << " because we reached the end." << LL_ENDL; +#endif + run_chain = removeRunningChain(run_chain); + continue; + } + } + else if(isChainExpired(*run_chain)) + { + run_chain = removeRunningChain(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; + //LL_DEBUGS() << "no conditionals - processing" << LL_ENDL; + } + else + { + PUMP_DEBUG; + //LL_DEBUGS() << "checking conditionals" << LL_ENDL; + // 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; + signal_client_t::iterator signal; + for(; it != end; ++it) + { + PUMP_DEBUG; + client_id = *((S32*)((*it).second.client_data)); + signal = signalled_client.find(client_id); + if (signal == not_signalled) continue; + static const apr_int16_t POLL_CHAIN_ERROR = + APR_POLLHUP | APR_POLLNVAL | APR_POLLERR; + const apr_pollfd_t* poll = &(poll_fd[(*signal).second]); + if(poll->rtnevents & POLL_CHAIN_ERROR) + { + // Potential eror condition has been + // returned. If HUP was one of them, we pass + // that as the error even though there may be + // more. If there are in fact more errors, + // we'll just wait for that detection until + // the next pump() cycle to catch it so that + // the logic here gets no more strained than + // it already is. + LLIOPipe::EStatus error_status; + if(poll->rtnevents & APR_POLLHUP) + error_status = LLIOPipe::STATUS_LOST_CONNECTION; + else + error_status = LLIOPipe::STATUS_ERROR; + if(handleChainError(*run_chain, error_status)) break; + ll_debug_poll_fd("Removing pipe", poll); + LL_WARNS() << "Removing pipe " + << (*run_chain).mChainLinks[0].mPipe + << " '" +#if LL_DEBUG_PIPE_TYPE_IN_PUMP + << typeid( + *((*run_chain).mChainLinks[0].mPipe)).name() +#endif + << "' because: " + << events_2_string(poll->rtnevents) + << LL_ENDL; + (*run_chain).mHead = (*run_chain).mChainLinks.end(); + break; + } + + // at least 1 fd got signalled, and there were no + // errors. That means we process this chain. + process_this_chain = true; + break; + } + } + } + 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 + LL_DEBUGS() << "Removing chain " << (*run_chain).mChainLinks[0].mPipe + << " '" + << typeid(*((*run_chain).mChainLinks[0].mPipe)).name() + << "' because we reached the end." << LL_ENDL; +#else +// LL_DEBUGS() << "Removing chain " << (*run_chain).mChainLinks[0].mPipe +// << " because we reached the end." << LL_ENDL; +#endif + + PUMP_DEBUG; + // This chain is done. Clean up any allocated memory and + // erase the chain info. + run_chain = removeRunningChain(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 = mRunningChains.end(); + END_PUMP_DEBUG; +} + +bool LLPumpIO::respond(LLIOPipe* pipe) +{ + if(NULL == pipe) return false; + + 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) +{ + // 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; + + // Add the callback response + LLChainInfo info; + info.mChainLinks = links; + info.mData = data; + info.mContext = context; + mPendingCallbacks.push_back(info); + return true; +} + +void LLPumpIO::callback() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; + //LL_INFOS() << "LLPumpIO::callback()" << LL_ENDL; + if(true) + { + 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) +{ + switch(op) + { + case PAUSE: + mState = PAUSING; + break; + case RESUME: + mState = NORMAL; + break; + default: + // no-op + break; + } +} + +void LLPumpIO::initialize(apr_pool_t* pool) +{ + if(!pool) return; + mPool = pool; +} + +void LLPumpIO::cleanup() +{ + if(mPollset) + { +// LL_DEBUGS() << "cleaning up pollset" << LL_ENDL; + apr_pollset_destroy(mPollset); + mPollset = NULL; + } + if(mCurrentPool) + { + apr_pool_destroy(mCurrentPool); + mCurrentPool = NULL; + } + mPool = NULL; +} + +void LLPumpIO::rebuildPollset() +{ +// LL_DEBUGS() << "LLPumpIO::rebuildPollset()" << LL_ENDL; + if(mPollset) + { + //LL_DEBUGS() << "destroying pollset" << LL_ENDL; + 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(); + } + //LL_DEBUGS() << "found " << size << " descriptors." << LL_ENDL; + 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; + 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 + LL_INFOS() << "Processing " << typeid(*((*it).mPipe)).name() << "." + << LL_ENDL; +#else + LL_INFOS() << "Processing link " << (*it).mPipe << "." << LL_ENDL; +#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'; + LL_INFOS() << "CHANNEL IN(" << (*it).mChannels.in() << "): " + << buf << LL_ENDL; + delete[] buf; + buf = NULL; + } + else + { + LL_INFOS() << "CHANNEL IN(" << (*it).mChannels.in()<< "): (null)" + << LL_ENDL; + } + } +#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'; + LL_INFOS() << "CHANNEL OUT(" << (*it).mChannels.out()<< "): " + << buf << LL_ENDL; + delete[] buf; + buf = NULL; + } + else + { + LL_INFOS() << "CHANNEL OUT(" << (*it).mChannels.out()<< "): (null)" + << LL_ENDL; + } + } +#endif + +#if LL_DEBUG_PROCESS_RETURN_VALUE + // Only bother with the success codes - error codes are logged + // below. + if(LLIOPipe::isSuccess(status)) + { + LL_INFOS() << "Pipe returned: '" +#if LL_DEBUG_PIPE_TYPE_IN_PUMP + << typeid(*((*it).mPipe)).name() << "':'" +#endif + << LLIOPipe::lookupStatusString(status) << "'" << LL_ENDL; + } +#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)) + { + LL_INFOS() << "Pump generated pipe err: '" +#if LL_DEBUG_PIPE_TYPE_IN_PUMP + << typeid(*((*it).mPipe)).name() << "':'" +#endif + << LLIOPipe::lookupStatusString(status) + << "'" << LL_ENDL; +#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'; + LL_INFOS() << "Input After Error: " << buf << LL_ENDL; + delete[] buf; + buf = NULL; + } + else + { + LL_INFOS() << "Input After Error: (null)" << LL_ENDL; + } + } + else + { + LL_INFOS() << "Input After Error: (null)" << LL_ENDL; + } +#endif + keep_going = false; + chain.mHead = it; + if(!handleChainError(chain, status)) + { + chain.mHead = end; + } + } + else + { + LL_INFOS() << "Unhandled status code: " << status << ":" + << LLIOPipe::lookupStatusString(status) << LL_ENDL; + } + break; + } + PUMP_DEBUG; + } while(keep_going && (++it != end)); + PUMP_DEBUG; +} + +bool LLPumpIO::isChainExpired(LLChainInfo& chain) +{ + if(!chain.mHasCurlRequest) + { + return false ; + } + + for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter) + { + if(!(*iter).mPipe->isValid()) + { + return true ; + } + } + + return false ; +} + +bool LLPumpIO::handleChainError( + LLChainInfo& chain, + LLIOPipe::EStatus error) +{ + 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 + LL_DEBUGS() << "Passing error to " << typeid(*((*rit).mPipe)).name() + << "." << LL_ENDL; +#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 + LL_DEBUGS() << "Pipe " << typeid(*((*rit).mPipe)).name() + << " returned code to stop error handler." << LL_ENDL; +#endif + keep_going = false; + break; + case LLIOPipe::STATUS_EXPIRED: + keep_going = false; + break ; + default: + if(LLIOPipe::isSuccess(error)) + { + LL_INFOS() << "Unhandled status code: " << error << ":" + << LLIOPipe::lookupStatusString(error) << LL_ENDL; + 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), + mHasCurlRequest(false) +{ + mTimer.setTimerExpirySec(DEFAULT_CHAIN_EXPIRY_SECS); +} + +void LLPumpIO::LLChainInfo::setTimeoutSeconds(F32 timeout) +{ + if(timeout > 0.0f) + { + mTimer.start(); + mTimer.reset(); + mTimer.setTimerExpirySec(timeout); + } + else + { + mTimer.stop(); + } +} + +void LLPumpIO::LLChainInfo::adjustTimeoutSeconds(F32 delta) +{ + if(mTimer.getStarted()) + { + F64 expiry = mTimer.expiresAt(); + expiry += delta; + mTimer.setExpiryAt(expiry); + } +} diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h index 835bccfb14..4f23c4d160 100644 --- a/indra/llmessage/llregionflags.h +++ b/indra/llmessage/llregionflags.h @@ -1,211 +1,211 @@ -/**
- * @file llregionflags.h
- * @brief Flags that are sent in the statistics message region_flags field.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLREGIONFLAGS_H
-#define LL_LLREGIONFLAGS_H
-
-// Can you be hurt here? Should health be on?
-const U64 REGION_FLAGS_ALLOW_DAMAGE = (1 << 0);
-
-// Can you make landmarks here?
-const U64 REGION_FLAGS_ALLOW_LANDMARK = (1 << 1);
-
-// Do we reset the home position when someone teleports away from here?
-const U64 REGION_FLAGS_ALLOW_SET_HOME = (1 << 2);
-
-// Do we reset the home position when someone teleports away from here?
-const U64 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3);
-
-// Does the sun move?
-const U64 REGION_FLAGS_SUN_FIXED = (1 << 4);
-
-// Does the estate owner allow private parcels?
-const U64 REGION_FLAGS_ALLOW_ACCESS_OVERRIDE = (1 << 5);
-
-// Can't change the terrain heightfield, even on owned parcels,
-// but can plant trees and grass.
-const U64 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6);
-
-// Can't release, sell, or buy land.
-const U64 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7);
-
-// All content wiped once per night
-const U64 REGION_FLAGS_SANDBOX = (1 << 8);
-
-const U64 REGION_FLAGS_ALLOW_ENVIRONMENT_OVERRIDE = (1 << 9);
-
-const U64 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies
-const U64 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
-const U64 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
-const U64 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
-const U64 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_OBJECT = (1 << 16);
-const U64 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_ESTATE_OBJECT = (1 << 17);
-const U64 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
-
-// Is flight allowed?
-const U64 REGION_FLAGS_BLOCK_FLY = (1 << 19);
-
-// Is direct teleport (p2p) allowed?
-const U64 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 U64 REGION_FLAGS_ESTATE_SKIP_SCRIPTS = (1 << 21);
-
-const U64 REGION_FLAGS_RESTRICT_PUSHOBJECT = (1 << 22);
-
-const U64 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23);
-
-const U64 REGION_FLAGS_ALLOW_PARCEL_CHANGES = (1 << 26);
-
-const U64 REGION_FLAGS_BLOCK_FLYOVER = (1 << 27);
-
-const U64 REGION_FLAGS_ALLOW_VOICE = (1 << 28);
-
-const U64 REGION_FLAGS_BLOCK_PARCEL_SEARCH = (1 << 29);
-const U64 REGION_FLAGS_DENY_AGEUNVERIFIED = (1 << 30);
-
-const U64 REGION_FLAGS_DENY_BOTS = (1 << 31);
-
-const U64 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK |
- REGION_FLAGS_ALLOW_SET_HOME |
- REGION_FLAGS_ALLOW_PARCEL_CHANGES |
- REGION_FLAGS_ALLOW_VOICE;
-
-
-const U64 REGION_FLAGS_PRELUDE_SET = REGION_FLAGS_RESET_HOME_ON_TELEPORT;
-const U64 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK
- | REGION_FLAGS_ALLOW_SET_HOME;
-
-const U64 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
- | REGION_FLAGS_SUN_FIXED
- | REGION_FLAGS_DENY_ANONYMOUS
- | REGION_FLAGS_DENY_AGEUNVERIFIED;
-
-inline bool is_flag_set(U64 flags, U64 flag)
-{
- return (flags & flag) != 0;
-}
-
-inline bool is_prelude( U64 flags )
-{
- // definition of prelude does not depend on fixed-sun
- return !is_flag_set(flags, REGION_FLAGS_PRELUDE_UNSET) &&
- is_flag_set(flags, REGION_FLAGS_PRELUDE_SET);
-}
-
-inline U64 set_prelude_flags(U64 flags)
-{
- // also set the sun-fixed flag
- return ((flags & ~REGION_FLAGS_PRELUDE_UNSET)
- | (REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED));
-}
-
-inline U64 unset_prelude_flags(U64 flags)
-{
- // also unset the fixed-sun flag
- return ((flags | REGION_FLAGS_PRELUDE_UNSET)
- & ~(REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED));
-}
-
-// Region protocols
-const U64 REGION_PROTOCOLS_AGENT_APPEARANCE_SERVICE = (1 << 0);
-
-// 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_TEEN = 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, estateexperiencedelta messages
-const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1U << 0;
-const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1U << 1;
-
-const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1U << 2;
-const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1U << 3;
-const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1U << 4;
-const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1U << 5;
-const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1U << 6;
-const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1U << 7;
-const U32 ESTATE_ACCESS_MANAGER_ADD = 1U << 8;
-const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1U << 9;
-const U32 ESTATE_ACCESS_NO_REPLY = 1U << 10;
-const U32 ESTATE_ACCESS_FAILED_BAN_ESTATE_MANAGER = 1U << 11;
-
-const S32 ESTATE_MAX_MANAGERS = 20;
-const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access
-const S32 ESTATE_MAX_BANNED_IDS = 750; // max for 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);
-
-// Controls experience key validity in the estate
-const U32 EXPERIENCE_KEY_TYPE_NONE = 0;
-const U32 EXPERIENCE_KEY_TYPE_BLOCKED = 1;
-const U32 EXPERIENCE_KEY_TYPE_ALLOWED = 2;
-const U32 EXPERIENCE_KEY_TYPE_TRUSTED = 3;
-
-const U32 EXPERIENCE_KEY_TYPE_FIRST = EXPERIENCE_KEY_TYPE_BLOCKED;
-const U32 EXPERIENCE_KEY_TYPE_LAST = EXPERIENCE_KEY_TYPE_TRUSTED;
-
-//
-const U32 ESTATE_EXPERIENCE_TRUSTED_ADD = 1U << 2;
-const U32 ESTATE_EXPERIENCE_TRUSTED_REMOVE = 1U << 3;
-const U32 ESTATE_EXPERIENCE_ALLOWED_ADD = 1U << 4;
-const U32 ESTATE_EXPERIENCE_ALLOWED_REMOVE = 1U << 5;
-const U32 ESTATE_EXPERIENCE_BLOCKED_ADD = 1U << 6;
-const U32 ESTATE_EXPERIENCE_BLOCKED_REMOVE = 1U << 7;
-
-const S32 ESTATE_MAX_EXPERIENCE_IDS = 8;
-
-
-#endif
-
-
+/** + * @file llregionflags.h + * @brief Flags that are sent in the statistics message region_flags field. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLREGIONFLAGS_H +#define LL_LLREGIONFLAGS_H + +// Can you be hurt here? Should health be on? +const U64 REGION_FLAGS_ALLOW_DAMAGE = (1 << 0); + +// Can you make landmarks here? +const U64 REGION_FLAGS_ALLOW_LANDMARK = (1 << 1); + +// Do we reset the home position when someone teleports away from here? +const U64 REGION_FLAGS_ALLOW_SET_HOME = (1 << 2); + +// Do we reset the home position when someone teleports away from here? +const U64 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3); + +// Does the sun move? +const U64 REGION_FLAGS_SUN_FIXED = (1 << 4); + +// Does the estate owner allow private parcels? +const U64 REGION_FLAGS_ALLOW_ACCESS_OVERRIDE = (1 << 5); + +// Can't change the terrain heightfield, even on owned parcels, +// but can plant trees and grass. +const U64 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6); + +// Can't release, sell, or buy land. +const U64 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7); + +// All content wiped once per night +const U64 REGION_FLAGS_SANDBOX = (1 << 8); + +const U64 REGION_FLAGS_ALLOW_ENVIRONMENT_OVERRIDE = (1 << 9); + +const U64 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies +const U64 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13); +const U64 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics +const U64 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15); +const U64 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_OBJECT = (1 << 16); +const U64 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_ESTATE_OBJECT = (1 << 17); +const U64 REGION_FLAGS_BLOCK_DWELL = (1 << 18); + +// Is flight allowed? +const U64 REGION_FLAGS_BLOCK_FLY = (1 << 19); + +// Is direct teleport (p2p) allowed? +const U64 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 U64 REGION_FLAGS_ESTATE_SKIP_SCRIPTS = (1 << 21); + +const U64 REGION_FLAGS_RESTRICT_PUSHOBJECT = (1 << 22); + +const U64 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23); + +const U64 REGION_FLAGS_ALLOW_PARCEL_CHANGES = (1 << 26); + +const U64 REGION_FLAGS_BLOCK_FLYOVER = (1 << 27); + +const U64 REGION_FLAGS_ALLOW_VOICE = (1 << 28); + +const U64 REGION_FLAGS_BLOCK_PARCEL_SEARCH = (1 << 29); +const U64 REGION_FLAGS_DENY_AGEUNVERIFIED = (1 << 30); + +const U64 REGION_FLAGS_DENY_BOTS = (1 << 31); + +const U64 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK | + REGION_FLAGS_ALLOW_SET_HOME | + REGION_FLAGS_ALLOW_PARCEL_CHANGES | + REGION_FLAGS_ALLOW_VOICE; + + +const U64 REGION_FLAGS_PRELUDE_SET = REGION_FLAGS_RESET_HOME_ON_TELEPORT; +const U64 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK + | REGION_FLAGS_ALLOW_SET_HOME; + +const U64 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE + | REGION_FLAGS_SUN_FIXED + | REGION_FLAGS_DENY_ANONYMOUS + | REGION_FLAGS_DENY_AGEUNVERIFIED; + +inline bool is_flag_set(U64 flags, U64 flag) +{ + return (flags & flag) != 0; +} + +inline bool is_prelude( U64 flags ) +{ + // definition of prelude does not depend on fixed-sun + return !is_flag_set(flags, REGION_FLAGS_PRELUDE_UNSET) && + is_flag_set(flags, REGION_FLAGS_PRELUDE_SET); +} + +inline U64 set_prelude_flags(U64 flags) +{ + // also set the sun-fixed flag + return ((flags & ~REGION_FLAGS_PRELUDE_UNSET) + | (REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED)); +} + +inline U64 unset_prelude_flags(U64 flags) +{ + // also unset the fixed-sun flag + return ((flags | REGION_FLAGS_PRELUDE_UNSET) + & ~(REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED)); +} + +// Region protocols +const U64 REGION_PROTOCOLS_AGENT_APPEARANCE_SERVICE = (1 << 0); + +// 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_TEEN = 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, estateexperiencedelta messages +const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1U << 0; +const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1U << 1; + +const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1U << 2; +const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1U << 3; +const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1U << 4; +const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1U << 5; +const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1U << 6; +const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1U << 7; +const U32 ESTATE_ACCESS_MANAGER_ADD = 1U << 8; +const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1U << 9; +const U32 ESTATE_ACCESS_NO_REPLY = 1U << 10; +const U32 ESTATE_ACCESS_FAILED_BAN_ESTATE_MANAGER = 1U << 11; + +const S32 ESTATE_MAX_MANAGERS = 20; +const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access +const S32 ESTATE_MAX_BANNED_IDS = 750; // max for 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); + +// Controls experience key validity in the estate +const U32 EXPERIENCE_KEY_TYPE_NONE = 0; +const U32 EXPERIENCE_KEY_TYPE_BLOCKED = 1; +const U32 EXPERIENCE_KEY_TYPE_ALLOWED = 2; +const U32 EXPERIENCE_KEY_TYPE_TRUSTED = 3; + +const U32 EXPERIENCE_KEY_TYPE_FIRST = EXPERIENCE_KEY_TYPE_BLOCKED; +const U32 EXPERIENCE_KEY_TYPE_LAST = EXPERIENCE_KEY_TYPE_TRUSTED; + +// +const U32 ESTATE_EXPERIENCE_TRUSTED_ADD = 1U << 2; +const U32 ESTATE_EXPERIENCE_TRUSTED_REMOVE = 1U << 3; +const U32 ESTATE_EXPERIENCE_ALLOWED_ADD = 1U << 4; +const U32 ESTATE_EXPERIENCE_ALLOWED_REMOVE = 1U << 5; +const U32 ESTATE_EXPERIENCE_BLOCKED_ADD = 1U << 6; +const U32 ESTATE_EXPERIENCE_BLOCKED_REMOVE = 1U << 7; + +const S32 ESTATE_MAX_EXPERIENCE_IDS = 8; + + +#endif + + diff --git a/indra/llmessage/llregionhandle.h b/indra/llmessage/llregionhandle.h index f732cdcce5..d68cd4d202 100644 --- a/indra/llmessage/llregionhandle.h +++ b/indra/llmessage/llregionhandle.h @@ -1,126 +1,126 @@ -/**
- * @file llregionhandle.h
- * @brief Routines for converting positions to/from region handles.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLREGIONHANDLE_H
-#define LL_LLREGIONHANDLE_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)
- {
-// LL_WARNS() << "to_region_handle:Clamping negative x position " << x_pos << " to zero!" << LL_ENDL;
- return false;
- }
- else
- {
- x_int = (U32)ll_round(x_pos);
- }
- if (y_pos < 0.f)
- {
-// LL_WARNS() << "to_region_handle:Clamping negative y position " << y_pos << " to zero!" << LL_ENDL;
- return false;
- }
- else
- {
- y_int = (U32)ll_round(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(const U32 grid_x, const 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
+/** + * @file llregionhandle.h + * @brief Routines for converting positions to/from region handles. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLREGIONHANDLE_H +#define LL_LLREGIONHANDLE_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) + { +// LL_WARNS() << "to_region_handle:Clamping negative x position " << x_pos << " to zero!" << LL_ENDL; + return false; + } + else + { + x_int = (U32)ll_round(x_pos); + } + if (y_pos < 0.f) + { +// LL_WARNS() << "to_region_handle:Clamping negative y position " << y_pos << " to zero!" << LL_ENDL; + return false; + } + else + { + y_int = (U32)ll_round(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(const U32 grid_x, const 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/llsdmessagebuilder.cpp b/indra/llmessage/llsdmessagebuilder.cpp index 3f4553fcee..8d7d51e13f 100644 --- a/indra/llmessage/llsdmessagebuilder.cpp +++ b/indra/llmessage/llsdmessagebuilder.cpp @@ -1,424 +1,424 @@ -/**
- * @file llsdmessagebuilder.cpp
- * @brief LLSDMessageBuilder class implementation.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llsdmessagebuilder.h"
-
-#include "llmessagetemplate.h"
-#include "llmath.h"
-#include "llquaternion.h"
-#include "llsdutil.h"
-#include "llsdutil_math.h"
-#include "llsdserialize.h"
-#include "u64.h"
-#include "v3dmath.h"
-#include "v3math.h"
-#include "v4math.h"
-
-LLSDMessageBuilder::LLSDMessageBuilder() :
- mCurrentMessage(LLSD::emptyMap()),
- mCurrentBlock(NULL),
- mCurrentMessageName(""),
- mCurrentBlockName(""),
- mbSBuilt(false),
- mbSClear(true)
-{
-}
-
-//virtual
-LLSDMessageBuilder::~LLSDMessageBuilder()
-{
-}
-
-
-// virtual
-void LLSDMessageBuilder::newMessage(const char* name)
-{
- mbSBuilt = false;
- mbSClear = false;
-
- mCurrentMessage = LLSD::emptyMap();
- mCurrentMessageName = (char*)name;
-}
-
-// virtual
-void LLSDMessageBuilder::clearMessage()
-{
- mbSBuilt = false;
- mbSClear = true;
-
- mCurrentMessage = LLSD::emptyMap();
- mCurrentMessageName = "";
-}
-
-// virtual
-void LLSDMessageBuilder::nextBlock(const char* blockname)
-{
- LLSD& block = mCurrentMessage[blockname];
- if(block.isUndefined())
- {
- block[0] = LLSD::emptyMap();
- mCurrentBlock = &(block[0]);
- }
- else if(block.isArray())
- {
- block[block.size()] = LLSD::emptyMap();
- mCurrentBlock = &(block[block.size() - 1]);
- }
- else
- {
- LL_ERRS() << "existing block not array" << LL_ENDL;
- }
-}
-
-// TODO: Remove this horror...
-bool LLSDMessageBuilder::removeLastBlock()
-{
- /* TODO: finish implementing this */
- return false;
-}
-
-void LLSDMessageBuilder::addBinaryData(
- const char* varname,
- const void* data,
- S32 size)
-{
- std::vector<U8> v;
- v.resize(size);
- memcpy(&(v[0]), reinterpret_cast<const U8*>(data), size);
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addS8(const char* varname, S8 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addU8(const char* varname, U8 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addS16(const char* varname, S16 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addU16(const char* varname, U16 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addF32(const char* varname, F32 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addS32(const char* varname, S32 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addU32(const char* varname, U32 v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_U32(v);
-}
-
-void LLSDMessageBuilder::addU64(const char* varname, U64 v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_U64(v);
-}
-
-void LLSDMessageBuilder::addF64(const char* varname, F64 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addIPAddr(const char* varname, U32 v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_ipaddr(v);
-}
-
-void LLSDMessageBuilder::addIPPort(const char* varname, U16 v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addBOOL(const char* varname, bool v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::addString(const char* varname, const char* v)
-{
- if (v)
- (*mCurrentBlock)[varname] = v; /* Flawfinder: ignore */
- else
- (*mCurrentBlock)[varname] = "";
-}
-
-void LLSDMessageBuilder::addString(const char* varname, const std::string& v)
-{
- if (v.size())
- (*mCurrentBlock)[varname] = v;
- else
- (*mCurrentBlock)[varname] = "";
-}
-
-void LLSDMessageBuilder::addVector3(const char* varname, const LLVector3& v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_vector3(v);
-}
-
-void LLSDMessageBuilder::addVector4(const char* varname, const LLVector4& v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_vector4(v);
-}
-
-void LLSDMessageBuilder::addVector3d(const char* varname, const LLVector3d& v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_vector3d(v);
-}
-
-void LLSDMessageBuilder::addQuat(const char* varname, const LLQuaternion& v)
-{
- (*mCurrentBlock)[varname] = ll_sd_from_quaternion(v);
-}
-
-void LLSDMessageBuilder::addUUID(const char* varname, const LLUUID& v)
-{
- (*mCurrentBlock)[varname] = v;
-}
-
-void LLSDMessageBuilder::compressMessage(U8*& buf_ptr, U32& buffer_length)
-{
-}
-
-bool LLSDMessageBuilder::isMessageFull(const char* blockname) const
-{
- return false;
-}
-
-U32 LLSDMessageBuilder::buildMessage(U8*, U32, U8)
-{
- return 0;
-}
-
-void LLSDMessageBuilder::copyFromMessageData(const LLMsgData& data)
-{
- // 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::const_iterator iter =
- data.mMemberBlocks.begin();
- LLMsgData::msg_blk_data_map_t::const_iterator end =
- data.mMemberBlocks.end();
- for(; iter != end; ++iter)
- {
- const 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--;
-
- nextBlock(block_name);
-
- // now loop through the variables
- LLMsgBlkData::msg_var_data_map_t::const_iterator dit = mbci->mMemberVarData.begin();
- LLMsgBlkData::msg_var_data_map_t::const_iterator dend = mbci->mMemberVarData.end();
-
- for(; dit != dend; ++dit)
- {
- const LLMsgVarData& mvci = *dit;
- const char* varname = mvci.getName();
-
- switch(mvci.getType())
- {
- case MVT_FIXED:
- addBinaryData(varname, mvci.getData(), mvci.getSize());
- break;
-
- case MVT_VARIABLE:
- {
- const char end = ((const char*)mvci.getData())[mvci.getSize()-1]; // Ensure null terminated
- if (mvci.getDataSize() == 1 && end == 0)
- {
- addString(varname, (const char*)mvci.getData());
- }
- else
- {
- addBinaryData(varname, mvci.getData(), mvci.getSize());
- }
- break;
- }
-
- case MVT_U8:
- addU8(varname, *(U8*)mvci.getData());
- break;
-
- case MVT_U16:
- addU16(varname, *(U16*)mvci.getData());
- break;
-
- case MVT_U32:
- addU32(varname, *(U32*)mvci.getData());
- break;
-
- case MVT_U64:
- addU64(varname, *(U64*)mvci.getData());
- break;
-
- case MVT_S8:
- addS8(varname, *(S8*)mvci.getData());
- break;
-
- case MVT_S16:
- addS16(varname, *(S16*)mvci.getData());
- break;
-
- case MVT_S32:
- addS32(varname, *(S32*)mvci.getData());
- break;
-
- // S64 not supported in LLSD so we just truncate it
- case MVT_S64:
- addS32(varname, (S32)*(S64*)mvci.getData());
- break;
-
- case MVT_F32:
- addF32(varname, *(F32*)mvci.getData());
- break;
-
- case MVT_F64:
- addF64(varname, *(F64*)mvci.getData());
- break;
-
- case MVT_LLVector3:
- addVector3(varname, *(LLVector3*)mvci.getData());
- break;
-
- case MVT_LLVector3d:
- addVector3d(varname, *(LLVector3d*)mvci.getData());
- break;
-
- case MVT_LLVector4:
- addVector4(varname, *(LLVector4*)mvci.getData());
- break;
-
- case MVT_LLQuaternion:
- {
- LLVector3 v = *(LLVector3*)mvci.getData();
- LLQuaternion q;
- q.unpackFromVector3(v);
- addQuat(varname, q);
- break;
- }
-
- case MVT_LLUUID:
- addUUID(varname, *(LLUUID*)mvci.getData());
- break;
-
- case MVT_BOOL:
- addBOOL(varname, *(bool*)mvci.getData());
- break;
-
- case MVT_IP_ADDR:
- addIPAddr(varname, *(U32*)mvci.getData());
- break;
-
- case MVT_IP_PORT:
- addIPPort(varname, *(U16*)mvci.getData());
- break;
-
- case MVT_U16Vec3:
- //treated as an array of 6 bytes
- addBinaryData(varname, mvci.getData(), 6);
- break;
-
- case MVT_U16Quat:
- //treated as an array of 8 bytes
- addBinaryData(varname, mvci.getData(), 8);
- break;
-
- case MVT_S16Array:
- addBinaryData(varname, mvci.getData(), mvci.getSize());
- break;
-
- default:
- LL_WARNS() << "Unknown type in conversion of message to LLSD" << LL_ENDL;
- break;
- }
- }
- }
-}
-
-//virtual
-void LLSDMessageBuilder::copyFromLLSD(const LLSD& msg)
-{
- mCurrentMessage = msg;
- LL_DEBUGS() << LLSDNotationStreamer(mCurrentMessage) << LL_ENDL;
-}
-
-const LLSD& LLSDMessageBuilder::getMessage() const
-{
- return mCurrentMessage;
-}
-
-//virtual
-void LLSDMessageBuilder::setBuilt(bool b) { mbSBuilt = b; }
-
-//virtual
-bool LLSDMessageBuilder::isBuilt() const {return mbSBuilt;}
-
-//virtual
-bool LLSDMessageBuilder::isClear() const {return mbSClear;}
-
-//virtual
-S32 LLSDMessageBuilder::getMessageSize()
-{
- // babbage: size is unknown as message stored as LLSD.
- // return non-zero if pending data, as send can be skipped for 0 size.
- // return 1 to encourage senders checking size against splitting message.
- return mCurrentMessage.size()? 1 : 0;
-}
-
-//virtual
-const char* LLSDMessageBuilder::getMessageName() const
-{
- return mCurrentMessageName.c_str();
-}
+/** + * @file llsdmessagebuilder.cpp + * @brief LLSDMessageBuilder class implementation. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llsdmessagebuilder.h" + +#include "llmessagetemplate.h" +#include "llmath.h" +#include "llquaternion.h" +#include "llsdutil.h" +#include "llsdutil_math.h" +#include "llsdserialize.h" +#include "u64.h" +#include "v3dmath.h" +#include "v3math.h" +#include "v4math.h" + +LLSDMessageBuilder::LLSDMessageBuilder() : + mCurrentMessage(LLSD::emptyMap()), + mCurrentBlock(NULL), + mCurrentMessageName(""), + mCurrentBlockName(""), + mbSBuilt(false), + mbSClear(true) +{ +} + +//virtual +LLSDMessageBuilder::~LLSDMessageBuilder() +{ +} + + +// virtual +void LLSDMessageBuilder::newMessage(const char* name) +{ + mbSBuilt = false; + mbSClear = false; + + mCurrentMessage = LLSD::emptyMap(); + mCurrentMessageName = (char*)name; +} + +// virtual +void LLSDMessageBuilder::clearMessage() +{ + mbSBuilt = false; + mbSClear = true; + + mCurrentMessage = LLSD::emptyMap(); + mCurrentMessageName = ""; +} + +// virtual +void LLSDMessageBuilder::nextBlock(const char* blockname) +{ + LLSD& block = mCurrentMessage[blockname]; + if(block.isUndefined()) + { + block[0] = LLSD::emptyMap(); + mCurrentBlock = &(block[0]); + } + else if(block.isArray()) + { + block[block.size()] = LLSD::emptyMap(); + mCurrentBlock = &(block[block.size() - 1]); + } + else + { + LL_ERRS() << "existing block not array" << LL_ENDL; + } +} + +// TODO: Remove this horror... +bool LLSDMessageBuilder::removeLastBlock() +{ + /* TODO: finish implementing this */ + return false; +} + +void LLSDMessageBuilder::addBinaryData( + const char* varname, + const void* data, + S32 size) +{ + std::vector<U8> v; + v.resize(size); + memcpy(&(v[0]), reinterpret_cast<const U8*>(data), size); + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addS8(const char* varname, S8 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addU8(const char* varname, U8 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addS16(const char* varname, S16 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addU16(const char* varname, U16 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addF32(const char* varname, F32 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addS32(const char* varname, S32 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addU32(const char* varname, U32 v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_U32(v); +} + +void LLSDMessageBuilder::addU64(const char* varname, U64 v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_U64(v); +} + +void LLSDMessageBuilder::addF64(const char* varname, F64 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addIPAddr(const char* varname, U32 v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_ipaddr(v); +} + +void LLSDMessageBuilder::addIPPort(const char* varname, U16 v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addBOOL(const char* varname, bool v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::addString(const char* varname, const char* v) +{ + if (v) + (*mCurrentBlock)[varname] = v; /* Flawfinder: ignore */ + else + (*mCurrentBlock)[varname] = ""; +} + +void LLSDMessageBuilder::addString(const char* varname, const std::string& v) +{ + if (v.size()) + (*mCurrentBlock)[varname] = v; + else + (*mCurrentBlock)[varname] = ""; +} + +void LLSDMessageBuilder::addVector3(const char* varname, const LLVector3& v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_vector3(v); +} + +void LLSDMessageBuilder::addVector4(const char* varname, const LLVector4& v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_vector4(v); +} + +void LLSDMessageBuilder::addVector3d(const char* varname, const LLVector3d& v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_vector3d(v); +} + +void LLSDMessageBuilder::addQuat(const char* varname, const LLQuaternion& v) +{ + (*mCurrentBlock)[varname] = ll_sd_from_quaternion(v); +} + +void LLSDMessageBuilder::addUUID(const char* varname, const LLUUID& v) +{ + (*mCurrentBlock)[varname] = v; +} + +void LLSDMessageBuilder::compressMessage(U8*& buf_ptr, U32& buffer_length) +{ +} + +bool LLSDMessageBuilder::isMessageFull(const char* blockname) const +{ + return false; +} + +U32 LLSDMessageBuilder::buildMessage(U8*, U32, U8) +{ + return 0; +} + +void LLSDMessageBuilder::copyFromMessageData(const LLMsgData& data) +{ + // 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::const_iterator iter = + data.mMemberBlocks.begin(); + LLMsgData::msg_blk_data_map_t::const_iterator end = + data.mMemberBlocks.end(); + for(; iter != end; ++iter) + { + const 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--; + + nextBlock(block_name); + + // now loop through the variables + LLMsgBlkData::msg_var_data_map_t::const_iterator dit = mbci->mMemberVarData.begin(); + LLMsgBlkData::msg_var_data_map_t::const_iterator dend = mbci->mMemberVarData.end(); + + for(; dit != dend; ++dit) + { + const LLMsgVarData& mvci = *dit; + const char* varname = mvci.getName(); + + switch(mvci.getType()) + { + case MVT_FIXED: + addBinaryData(varname, mvci.getData(), mvci.getSize()); + break; + + case MVT_VARIABLE: + { + const char end = ((const char*)mvci.getData())[mvci.getSize()-1]; // Ensure null terminated + if (mvci.getDataSize() == 1 && end == 0) + { + addString(varname, (const char*)mvci.getData()); + } + else + { + addBinaryData(varname, mvci.getData(), mvci.getSize()); + } + break; + } + + case MVT_U8: + addU8(varname, *(U8*)mvci.getData()); + break; + + case MVT_U16: + addU16(varname, *(U16*)mvci.getData()); + break; + + case MVT_U32: + addU32(varname, *(U32*)mvci.getData()); + break; + + case MVT_U64: + addU64(varname, *(U64*)mvci.getData()); + break; + + case MVT_S8: + addS8(varname, *(S8*)mvci.getData()); + break; + + case MVT_S16: + addS16(varname, *(S16*)mvci.getData()); + break; + + case MVT_S32: + addS32(varname, *(S32*)mvci.getData()); + break; + + // S64 not supported in LLSD so we just truncate it + case MVT_S64: + addS32(varname, (S32)*(S64*)mvci.getData()); + break; + + case MVT_F32: + addF32(varname, *(F32*)mvci.getData()); + break; + + case MVT_F64: + addF64(varname, *(F64*)mvci.getData()); + break; + + case MVT_LLVector3: + addVector3(varname, *(LLVector3*)mvci.getData()); + break; + + case MVT_LLVector3d: + addVector3d(varname, *(LLVector3d*)mvci.getData()); + break; + + case MVT_LLVector4: + addVector4(varname, *(LLVector4*)mvci.getData()); + break; + + case MVT_LLQuaternion: + { + LLVector3 v = *(LLVector3*)mvci.getData(); + LLQuaternion q; + q.unpackFromVector3(v); + addQuat(varname, q); + break; + } + + case MVT_LLUUID: + addUUID(varname, *(LLUUID*)mvci.getData()); + break; + + case MVT_BOOL: + addBOOL(varname, *(bool*)mvci.getData()); + break; + + case MVT_IP_ADDR: + addIPAddr(varname, *(U32*)mvci.getData()); + break; + + case MVT_IP_PORT: + addIPPort(varname, *(U16*)mvci.getData()); + break; + + case MVT_U16Vec3: + //treated as an array of 6 bytes + addBinaryData(varname, mvci.getData(), 6); + break; + + case MVT_U16Quat: + //treated as an array of 8 bytes + addBinaryData(varname, mvci.getData(), 8); + break; + + case MVT_S16Array: + addBinaryData(varname, mvci.getData(), mvci.getSize()); + break; + + default: + LL_WARNS() << "Unknown type in conversion of message to LLSD" << LL_ENDL; + break; + } + } + } +} + +//virtual +void LLSDMessageBuilder::copyFromLLSD(const LLSD& msg) +{ + mCurrentMessage = msg; + LL_DEBUGS() << LLSDNotationStreamer(mCurrentMessage) << LL_ENDL; +} + +const LLSD& LLSDMessageBuilder::getMessage() const +{ + return mCurrentMessage; +} + +//virtual +void LLSDMessageBuilder::setBuilt(bool b) { mbSBuilt = b; } + +//virtual +bool LLSDMessageBuilder::isBuilt() const {return mbSBuilt;} + +//virtual +bool LLSDMessageBuilder::isClear() const {return mbSClear;} + +//virtual +S32 LLSDMessageBuilder::getMessageSize() +{ + // babbage: size is unknown as message stored as LLSD. + // return non-zero if pending data, as send can be skipped for 0 size. + // return 1 to encourage senders checking size against splitting message. + return mCurrentMessage.size()? 1 : 0; +} + +//virtual +const char* LLSDMessageBuilder::getMessageName() const +{ + return mCurrentMessageName.c_str(); +} diff --git a/indra/llmessage/llsdmessagebuilder.h b/indra/llmessage/llsdmessagebuilder.h index 8eac962ba0..72ea6d0dd6 100644 --- a/indra/llmessage/llsdmessagebuilder.h +++ b/indra/llmessage/llsdmessagebuilder.h @@ -1,126 +1,126 @@ -/**
- * @file llsdmessagebuilder.h
- * @brief Declaration of LLSDMessageBuilder class.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSDMESSAGEBUILDER_H
-#define LL_LLSDMESSAGEBUILDER_H
-
-#include <map>
-
-#include "llmessagebuilder.h"
-#include "llmsgvariabletype.h"
-#include "llsd.h"
-
-class LLMessageTemplate;
-class LLMsgData;
-
-class LLSDMessageBuilder : public LLMessageBuilder
-{
-public:
-
- //CLASS_LOG_TYPE(LLSDMessageBuilder);
-
- LLSDMessageBuilder();
- virtual ~LLSDMessageBuilder();
-
- virtual void newMessage(const char* name);
-
- virtual void nextBlock(const char* blockname);
- virtual bool removeLastBlock(); // TODO: babbage: remove this horror...
-
- /** All add* methods expect pointers to canonical varname strings. */
- virtual void addBinaryData(
- const char* varname,
- const void* data,
- S32 size);
- virtual void addBOOL(const char* varname, bool b);
- virtual void addS8(const char* varname, S8 s);
- virtual void addU8(const char* varname, U8 u);
- virtual void addS16(const char* varname, S16 i);
- virtual void addU16(const char* varname, U16 i);
- virtual void addF32(const char* varname, F32 f);
- virtual void addS32(const char* varname, S32 s);
- virtual void addU32(const char* varname, U32 u);
- virtual void addU64(const char* varname, U64 lu);
- virtual void addF64(const char* varname, F64 d);
- virtual void addVector3(const char* varname, const LLVector3& vec);
- virtual void addVector4(const char* varname, const LLVector4& vec);
- virtual void addVector3d(const char* varname, const LLVector3d& vec);
- virtual void addQuat(const char* varname, const LLQuaternion& quat);
- virtual void addUUID(const char* varname, const LLUUID& uuid);
- virtual void addIPAddr(const char* varname, const U32 ip);
- virtual void addIPPort(const char* varname, const U16 port);
- virtual void addString(const char* varname, const char* s);
- virtual void addString(const char* varname, const std::string& s);
-
- virtual bool isMessageFull(const char* blockname) const;
- virtual void compressMessage(U8*& buf_ptr, U32& buffer_length);
-
- virtual bool isBuilt() const;
- virtual bool isClear() const;
- virtual U32 buildMessage(U8* buffer, U32 buffer_size, U8 offset_to_data);
- /**< Null implementation which returns 0. */
-
- virtual void clearMessage();
-
- // TODO: babbage: remove this horror.
- virtual void setBuilt(bool b);
-
- virtual S32 getMessageSize();
- virtual const char* getMessageName() const;
-
- virtual void copyFromMessageData(const LLMsgData& data);
-
- virtual void copyFromLLSD(const LLSD& msg);
-
- const LLSD& getMessage() const;
-private:
-
- /* mCurrentMessage is of the following format:
- mCurrentMessage = { 'block_name1' : [ { 'block1_field1' : 'b1f1_data',
- 'block1_field2' : 'b1f2_data',
- ...
- 'block1_fieldn' : 'b1fn_data'},
- { 'block2_field1' : 'b2f1_data',
- 'block2_field2' : 'b2f2_data',
- ...
- 'block2_fieldn' : 'b2fn_data'},
- ...
- { 'blockm_field1' : 'bmf1_data',
- 'blockm_field2' : 'bmf2_data',
- ...
- 'blockm_fieldn' : 'bmfn_data'} ],
- 'block_name2' : ...,
- ...
- 'block_namem' } */
- LLSD mCurrentMessage;
- LLSD* mCurrentBlock;
- std::string mCurrentMessageName;
- std::string mCurrentBlockName;
- bool mbSBuilt;
- bool mbSClear;
-};
-
-#endif // LL_LLSDMESSAGEBUILDER_H
+/** + * @file llsdmessagebuilder.h + * @brief Declaration of LLSDMessageBuilder class. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSDMESSAGEBUILDER_H +#define LL_LLSDMESSAGEBUILDER_H + +#include <map> + +#include "llmessagebuilder.h" +#include "llmsgvariabletype.h" +#include "llsd.h" + +class LLMessageTemplate; +class LLMsgData; + +class LLSDMessageBuilder : public LLMessageBuilder +{ +public: + + //CLASS_LOG_TYPE(LLSDMessageBuilder); + + LLSDMessageBuilder(); + virtual ~LLSDMessageBuilder(); + + virtual void newMessage(const char* name); + + virtual void nextBlock(const char* blockname); + virtual bool removeLastBlock(); // TODO: babbage: remove this horror... + + /** All add* methods expect pointers to canonical varname strings. */ + virtual void addBinaryData( + const char* varname, + const void* data, + S32 size); + virtual void addBOOL(const char* varname, bool b); + virtual void addS8(const char* varname, S8 s); + virtual void addU8(const char* varname, U8 u); + virtual void addS16(const char* varname, S16 i); + virtual void addU16(const char* varname, U16 i); + virtual void addF32(const char* varname, F32 f); + virtual void addS32(const char* varname, S32 s); + virtual void addU32(const char* varname, U32 u); + virtual void addU64(const char* varname, U64 lu); + virtual void addF64(const char* varname, F64 d); + virtual void addVector3(const char* varname, const LLVector3& vec); + virtual void addVector4(const char* varname, const LLVector4& vec); + virtual void addVector3d(const char* varname, const LLVector3d& vec); + virtual void addQuat(const char* varname, const LLQuaternion& quat); + virtual void addUUID(const char* varname, const LLUUID& uuid); + virtual void addIPAddr(const char* varname, const U32 ip); + virtual void addIPPort(const char* varname, const U16 port); + virtual void addString(const char* varname, const char* s); + virtual void addString(const char* varname, const std::string& s); + + virtual bool isMessageFull(const char* blockname) const; + virtual void compressMessage(U8*& buf_ptr, U32& buffer_length); + + virtual bool isBuilt() const; + virtual bool isClear() const; + virtual U32 buildMessage(U8* buffer, U32 buffer_size, U8 offset_to_data); + /**< Null implementation which returns 0. */ + + virtual void clearMessage(); + + // TODO: babbage: remove this horror. + virtual void setBuilt(bool b); + + virtual S32 getMessageSize(); + virtual const char* getMessageName() const; + + virtual void copyFromMessageData(const LLMsgData& data); + + virtual void copyFromLLSD(const LLSD& msg); + + const LLSD& getMessage() const; +private: + + /* mCurrentMessage is of the following format: + mCurrentMessage = { 'block_name1' : [ { 'block1_field1' : 'b1f1_data', + 'block1_field2' : 'b1f2_data', + ... + 'block1_fieldn' : 'b1fn_data'}, + { 'block2_field1' : 'b2f1_data', + 'block2_field2' : 'b2f2_data', + ... + 'block2_fieldn' : 'b2fn_data'}, + ... + { 'blockm_field1' : 'bmf1_data', + 'blockm_field2' : 'bmf2_data', + ... + 'blockm_fieldn' : 'bmfn_data'} ], + 'block_name2' : ..., + ... + 'block_namem' } */ + LLSD mCurrentMessage; + LLSD* mCurrentBlock; + std::string mCurrentMessageName; + std::string mCurrentBlockName; + bool mbSBuilt; + bool mbSClear; +}; + +#endif // LL_LLSDMESSAGEBUILDER_H diff --git a/indra/llmessage/llsdmessagereader.cpp b/indra/llmessage/llsdmessagereader.cpp index c0e8f634b2..8be6158d82 100644 --- a/indra/llmessage/llsdmessagereader.cpp +++ b/indra/llmessage/llsdmessagereader.cpp @@ -1,344 +1,344 @@ -/**
- * @file llsdmessagereader.cpp
- * @brief LLSDMessageReader class implementation.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llsdmessagereader.h"
-
-#include "llmessagebuilder.h"
-#include "llsdmessagebuilder.h"
-#include "llsdutil.h"
-
-#include "llsdutil_math.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "v3dmath.h"
-#include "v2math.h"
-#include "llquaternion.h"
-#include "v4color.h"
-
-LLSDMessageReader::LLSDMessageReader() :
- mMessageName(NULL)
-{
-}
-
-//virtual
-LLSDMessageReader::~LLSDMessageReader()
-{
-}
-
-
-LLSD getLLSD(const LLSD& input, const char* block, const char* var, S32 blocknum)
-{
- // babbage: log error to LL_ERRS() if variable not found to mimic
- // LLTemplateMessageReader::getData behaviour
- if(NULL == block)
- {
- LL_ERRS() << "NULL block name" << LL_ENDL;
- return LLSD();
- }
- if(NULL == var)
- {
- LL_ERRS() << "NULL var name" << LL_ENDL;
- return LLSD();
- }
- if(! input[block].isArray())
- {
- // NOTE: babbage: need to return default for missing blocks to allow
- // backwards/forwards compatibility - handlers must cope with default
- // values.
- LL_WARNS() << "block " << block << " not found" << LL_ENDL;
- return LLSD();
- }
-
- LLSD result = input[block][blocknum][var];
- if(result.isUndefined())
- {
- // NOTE: babbage: need to return default for missing vars to allow
- // backwards/forwards compatibility - handlers must cope with default
- // values.
- LL_WARNS() << "var " << var << " not found" << LL_ENDL;
- }
- return result;
-}
-
-//virtual
-void LLSDMessageReader::getBinaryData(const char *block, const char *var,
- void *datap, S32 size, S32 blocknum,
- S32 max_size)
-{
- std::vector<U8> data = getLLSD(mMessage, block, var, blocknum);
- S32 data_size = (S32)data.size();
-
- if (size && data_size != size)
- {
- return;
- }
-
- if (max_size < data_size)
- {
- data_size = max_size;
- }
-
- // Calls to memcpy will fail if data_size is not positive.
- // Phoenix 2009-02-27
- if(data_size <= 0)
- {
- return;
- }
- memcpy(datap, &(data[0]), data_size);
-}
-
-//virtual
-void LLSDMessageReader::getBOOL(const char *block, const char *var,
- bool &data,
- S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum);
-}
-
-//virtual
-void LLSDMessageReader::getS8(const char *block, const char *var, S8 &data,
- S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum).asInteger();
-}
-
-//virtual
-void LLSDMessageReader::getU8(const char *block, const char *var, U8 &data,
- S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum).asInteger();
-}
-
-//virtual
-void LLSDMessageReader::getS16(const char *block, const char *var, S16 &data,
- S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum).asInteger();
-}
-
-//virtual
-void LLSDMessageReader::getU16(const char *block, const char *var, U16 &data,
- S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum).asInteger();
-}
-
-//virtual
-void LLSDMessageReader::getS32(const char *block, const char *var, S32 &data,
- S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum);
-}
-
-//virtual
-void LLSDMessageReader::getF32(const char *block, const char *var, F32 &data,
- S32 blocknum)
-{
- data = (F32)getLLSD(mMessage, block, var, blocknum).asReal();
-}
-
-//virtual
-void LLSDMessageReader::getU32(const char *block, const char *var, U32 &data,
- S32 blocknum)
-{
- data = ll_U32_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getU64(const char *block, const char *var,
- U64 &data, S32 blocknum)
-{
- data = ll_U64_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getF64(const char *block, const char *var,
- F64 &data, S32 blocknum)
-{
- data = getLLSD(mMessage, block, var, blocknum);
-}
-
-//virtual
-void LLSDMessageReader::getVector3(const char *block, const char *var,
- LLVector3 &vec, S32 blocknum)
-{
- vec = ll_vector3_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getVector4(const char *block, const char *var,
- LLVector4 &vec, S32 blocknum)
-{
- vec = ll_vector4_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getVector3d(const char *block, const char *var,
- LLVector3d &vec, S32 blocknum)
-{
- vec = ll_vector3d_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getQuat(const char *block, const char *var,
- LLQuaternion &q, S32 blocknum)
-{
- q = ll_quaternion_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getUUID(const char *block, const char *var,
- LLUUID &uuid, S32 blocknum)
-{
- uuid = getLLSD(mMessage, block, var, blocknum);
-}
-
-//virtual
-void LLSDMessageReader::getIPAddr(const char *block, const char *var,
- U32 &ip, S32 blocknum)
-{
- ip = ll_ipaddr_from_sd(getLLSD(mMessage, block, var, blocknum));
-}
-
-//virtual
-void LLSDMessageReader::getIPPort(const char *block, const char *var,
- U16 &port, S32 blocknum)
-{
- port = getLLSD(mMessage, block, var, blocknum).asInteger();
-}
-
-//virtual
-void LLSDMessageReader::getString(const char *block, const char *var,
- S32 buffer_size, char *buffer, S32 blocknum)
-{
- if(buffer_size <= 0)
- {
- LL_WARNS() << "buffer_size <= 0" << LL_ENDL;
- return;
- }
- std::string data = getLLSD(mMessage, block, var, blocknum);
- S32 data_size = data.size();
- if (data_size >= buffer_size)
- {
- data_size = buffer_size - 1;
- }
- memcpy(buffer, data.data(), data_size);
- buffer[data_size] = '\0';
-}
-
-//virtual
-void LLSDMessageReader::getString(const char *block, const char *var,
- std::string& outstr, S32 blocknum)
-{
- outstr = getLLSD(mMessage, block, var, blocknum).asString();
-}
-
-//virtual
-S32 LLSDMessageReader::getNumberOfBlocks(const char *blockname)
-{
- return mMessage[blockname].size();
-}
-
-S32 getElementSize(const LLSD& llsd)
-{
- LLSD::Type type = llsd.type();
- switch(type)
- {
- case LLSD::TypeBoolean:
- return sizeof(bool);
- case LLSD::TypeInteger:
- return sizeof(S32);
- case LLSD::TypeReal:
- return sizeof(F64);
- case LLSD::TypeString:
- return llsd.size();
- case LLSD::TypeUUID:
- return sizeof(LLUUID);
- case LLSD::TypeDate:
- return sizeof(LLDate);
- case LLSD::TypeURI:
- return sizeof(LLURI);
- case LLSD::TypeBinary:
- {
- std::vector<U8> data = llsd;
- return data.size() * sizeof(U8);
- }
- case LLSD::TypeMap:
- case LLSD::TypeArray:
- case LLSD::TypeUndefined:
- default: // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc.
- return 0;
- }
- //return 0;
-}
-
-//virtual
-//Mainly used to find size of binary block of data
-S32 LLSDMessageReader::getSize(const char *blockname, const char *varname)
-{
- return getElementSize(mMessage[blockname][0][varname]);
-}
-
-
-//virtual
-S32 LLSDMessageReader::getSize(const char *blockname, S32 blocknum,
- const char *varname)
-{
- return getElementSize(mMessage[blockname][blocknum][varname]);
-}
-
-//virtual
-void LLSDMessageReader::clearMessage()
-{
- mMessage = LLSD();
-}
-
-//virtual
-const char* LLSDMessageReader::getMessageName() const
-{
- return mMessageName;
-}
-
-// virtual
-S32 LLSDMessageReader::getMessageSize() const
-{
- return 0;
-}
-
-//virtual
-void LLSDMessageReader::copyToBuilder(LLMessageBuilder& builder) const
-{
- builder.copyFromLLSD(mMessage);
-}
-
-void LLSDMessageReader::setMessage(const char* name, const LLSD& message)
-{
- mMessageName = name;
- // TODO: Validate
- mMessage = message;
-}
+/** + * @file llsdmessagereader.cpp + * @brief LLSDMessageReader class implementation. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llsdmessagereader.h" + +#include "llmessagebuilder.h" +#include "llsdmessagebuilder.h" +#include "llsdutil.h" + +#include "llsdutil_math.h" +#include "v3math.h" +#include "v4math.h" +#include "v3dmath.h" +#include "v2math.h" +#include "llquaternion.h" +#include "v4color.h" + +LLSDMessageReader::LLSDMessageReader() : + mMessageName(NULL) +{ +} + +//virtual +LLSDMessageReader::~LLSDMessageReader() +{ +} + + +LLSD getLLSD(const LLSD& input, const char* block, const char* var, S32 blocknum) +{ + // babbage: log error to LL_ERRS() if variable not found to mimic + // LLTemplateMessageReader::getData behaviour + if(NULL == block) + { + LL_ERRS() << "NULL block name" << LL_ENDL; + return LLSD(); + } + if(NULL == var) + { + LL_ERRS() << "NULL var name" << LL_ENDL; + return LLSD(); + } + if(! input[block].isArray()) + { + // NOTE: babbage: need to return default for missing blocks to allow + // backwards/forwards compatibility - handlers must cope with default + // values. + LL_WARNS() << "block " << block << " not found" << LL_ENDL; + return LLSD(); + } + + LLSD result = input[block][blocknum][var]; + if(result.isUndefined()) + { + // NOTE: babbage: need to return default for missing vars to allow + // backwards/forwards compatibility - handlers must cope with default + // values. + LL_WARNS() << "var " << var << " not found" << LL_ENDL; + } + return result; +} + +//virtual +void LLSDMessageReader::getBinaryData(const char *block, const char *var, + void *datap, S32 size, S32 blocknum, + S32 max_size) +{ + std::vector<U8> data = getLLSD(mMessage, block, var, blocknum); + S32 data_size = (S32)data.size(); + + if (size && data_size != size) + { + return; + } + + if (max_size < data_size) + { + data_size = max_size; + } + + // Calls to memcpy will fail if data_size is not positive. + // Phoenix 2009-02-27 + if(data_size <= 0) + { + return; + } + memcpy(datap, &(data[0]), data_size); +} + +//virtual +void LLSDMessageReader::getBOOL(const char *block, const char *var, + bool &data, + S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum); +} + +//virtual +void LLSDMessageReader::getS8(const char *block, const char *var, S8 &data, + S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum).asInteger(); +} + +//virtual +void LLSDMessageReader::getU8(const char *block, const char *var, U8 &data, + S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum).asInteger(); +} + +//virtual +void LLSDMessageReader::getS16(const char *block, const char *var, S16 &data, + S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum).asInteger(); +} + +//virtual +void LLSDMessageReader::getU16(const char *block, const char *var, U16 &data, + S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum).asInteger(); +} + +//virtual +void LLSDMessageReader::getS32(const char *block, const char *var, S32 &data, + S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum); +} + +//virtual +void LLSDMessageReader::getF32(const char *block, const char *var, F32 &data, + S32 blocknum) +{ + data = (F32)getLLSD(mMessage, block, var, blocknum).asReal(); +} + +//virtual +void LLSDMessageReader::getU32(const char *block, const char *var, U32 &data, + S32 blocknum) +{ + data = ll_U32_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getU64(const char *block, const char *var, + U64 &data, S32 blocknum) +{ + data = ll_U64_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getF64(const char *block, const char *var, + F64 &data, S32 blocknum) +{ + data = getLLSD(mMessage, block, var, blocknum); +} + +//virtual +void LLSDMessageReader::getVector3(const char *block, const char *var, + LLVector3 &vec, S32 blocknum) +{ + vec = ll_vector3_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getVector4(const char *block, const char *var, + LLVector4 &vec, S32 blocknum) +{ + vec = ll_vector4_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getVector3d(const char *block, const char *var, + LLVector3d &vec, S32 blocknum) +{ + vec = ll_vector3d_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getQuat(const char *block, const char *var, + LLQuaternion &q, S32 blocknum) +{ + q = ll_quaternion_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getUUID(const char *block, const char *var, + LLUUID &uuid, S32 blocknum) +{ + uuid = getLLSD(mMessage, block, var, blocknum); +} + +//virtual +void LLSDMessageReader::getIPAddr(const char *block, const char *var, + U32 &ip, S32 blocknum) +{ + ip = ll_ipaddr_from_sd(getLLSD(mMessage, block, var, blocknum)); +} + +//virtual +void LLSDMessageReader::getIPPort(const char *block, const char *var, + U16 &port, S32 blocknum) +{ + port = getLLSD(mMessage, block, var, blocknum).asInteger(); +} + +//virtual +void LLSDMessageReader::getString(const char *block, const char *var, + S32 buffer_size, char *buffer, S32 blocknum) +{ + if(buffer_size <= 0) + { + LL_WARNS() << "buffer_size <= 0" << LL_ENDL; + return; + } + std::string data = getLLSD(mMessage, block, var, blocknum); + S32 data_size = data.size(); + if (data_size >= buffer_size) + { + data_size = buffer_size - 1; + } + memcpy(buffer, data.data(), data_size); + buffer[data_size] = '\0'; +} + +//virtual +void LLSDMessageReader::getString(const char *block, const char *var, + std::string& outstr, S32 blocknum) +{ + outstr = getLLSD(mMessage, block, var, blocknum).asString(); +} + +//virtual +S32 LLSDMessageReader::getNumberOfBlocks(const char *blockname) +{ + return mMessage[blockname].size(); +} + +S32 getElementSize(const LLSD& llsd) +{ + LLSD::Type type = llsd.type(); + switch(type) + { + case LLSD::TypeBoolean: + return sizeof(bool); + case LLSD::TypeInteger: + return sizeof(S32); + case LLSD::TypeReal: + return sizeof(F64); + case LLSD::TypeString: + return llsd.size(); + case LLSD::TypeUUID: + return sizeof(LLUUID); + case LLSD::TypeDate: + return sizeof(LLDate); + case LLSD::TypeURI: + return sizeof(LLURI); + case LLSD::TypeBinary: + { + std::vector<U8> data = llsd; + return data.size() * sizeof(U8); + } + case LLSD::TypeMap: + case LLSD::TypeArray: + case LLSD::TypeUndefined: + default: // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc. + return 0; + } + //return 0; +} + +//virtual +//Mainly used to find size of binary block of data +S32 LLSDMessageReader::getSize(const char *blockname, const char *varname) +{ + return getElementSize(mMessage[blockname][0][varname]); +} + + +//virtual +S32 LLSDMessageReader::getSize(const char *blockname, S32 blocknum, + const char *varname) +{ + return getElementSize(mMessage[blockname][blocknum][varname]); +} + +//virtual +void LLSDMessageReader::clearMessage() +{ + mMessage = LLSD(); +} + +//virtual +const char* LLSDMessageReader::getMessageName() const +{ + return mMessageName; +} + +// virtual +S32 LLSDMessageReader::getMessageSize() const +{ + return 0; +} + +//virtual +void LLSDMessageReader::copyToBuilder(LLMessageBuilder& builder) const +{ + builder.copyFromLLSD(mMessage); +} + +void LLSDMessageReader::setMessage(const char* name, const LLSD& message) +{ + mMessageName = name; + // TODO: Validate + mMessage = message; +} diff --git a/indra/llmessage/llsdmessagereader.h b/indra/llmessage/llsdmessagereader.h index 510730c115..6f4256380b 100644 --- a/indra/llmessage/llsdmessagereader.h +++ b/indra/llmessage/llsdmessagereader.h @@ -1,108 +1,108 @@ -/**
- * @file llsdmessagereader.h
- * @brief LLSDMessageReader class Declaration
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSDMESSAGEREADER_H
-#define LL_LLSDMESSAGEREADER_H
-
-#include "llmessagereader.h"
-#include "llsd.h"
-
-#include <map>
-
-class LLMessageTemplate;
-class LLMsgData;
-
-class LLSDMessageReader : public LLMessageReader
-{
-public:
-
- LLSDMessageReader();
- virtual ~LLSDMessageReader();
-
- /** All get* methods expect pointers to canonical strings. */
- virtual void getBinaryData(const char *block, const char *var,
- void *datap, S32 size, S32 blocknum = 0,
- S32 max_size = S32_MAX);
- virtual void getBOOL(const char *block, const char *var, bool &data,
- S32 blocknum = 0);
- virtual void getS8(const char *block, const char *var, S8 &data,
- S32 blocknum = 0);
- virtual void getU8(const char *block, const char *var, U8 &data,
- S32 blocknum = 0);
- virtual void getS16(const char *block, const char *var, S16 &data,
- S32 blocknum = 0);
- virtual void getU16(const char *block, const char *var, U16 &data,
- S32 blocknum = 0);
- virtual void getS32(const char *block, const char *var, S32 &data,
- S32 blocknum = 0);
- virtual void getF32(const char *block, const char *var, F32 &data,
- S32 blocknum = 0);
- virtual void getU32(const char *block, const char *var, U32 &data,
- S32 blocknum = 0);
- virtual void getU64(const char *block, const char *var, U64 &data,
- S32 blocknum = 0);
- virtual void getF64(const char *block, const char *var, F64 &data,
- S32 blocknum = 0);
- virtual void getVector3(const char *block, const char *var,
- LLVector3 &vec, S32 blocknum = 0);
- virtual void getVector4(const char *block, const char *var,
- LLVector4 &vec, S32 blocknum = 0);
- virtual void getVector3d(const char *block, const char *var,
- LLVector3d &vec, S32 blocknum = 0);
- virtual void getQuat(const char *block, const char *var, LLQuaternion &q,
- S32 blocknum = 0);
- virtual void getUUID(const char *block, const char *var, LLUUID &uuid,
- S32 blocknum = 0);
- virtual void getIPAddr(const char *block, const char *var, U32 &ip,
- S32 blocknum = 0);
- virtual void getIPPort(const char *block, const char *var, U16 &port,
- S32 blocknum = 0);
- virtual void getString(const char *block, const char *var,
- S32 buffer_size, char *buffer, S32 blocknum = 0);
- virtual void getString(const char *block, const char *var, std::string& outstr,
- S32 blocknum = 0);
-
- virtual S32 getNumberOfBlocks(const char *blockname);
- virtual S32 getSize(const char *blockname, const char *varname);
- virtual S32 getSize(const char *blockname, S32 blocknum,
- const char *varname);
-
- virtual void clearMessage();
-
- virtual const char* getMessageName() const;
- virtual S32 getMessageSize() const;
-
- virtual void copyToBuilder(LLMessageBuilder&) const;
-
- /** Expects a pointer to a canonical name string */
- void setMessage(const char* name, const LLSD& msg);
-
-private:
- const char* mMessageName; // Canonical (prehashed) string.
- LLSD mMessage;
-};
-
-#endif // LL_LLSDMESSAGEREADER_H
+/** + * @file llsdmessagereader.h + * @brief LLSDMessageReader class Declaration + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSDMESSAGEREADER_H +#define LL_LLSDMESSAGEREADER_H + +#include "llmessagereader.h" +#include "llsd.h" + +#include <map> + +class LLMessageTemplate; +class LLMsgData; + +class LLSDMessageReader : public LLMessageReader +{ +public: + + LLSDMessageReader(); + virtual ~LLSDMessageReader(); + + /** All get* methods expect pointers to canonical strings. */ + virtual void getBinaryData(const char *block, const char *var, + void *datap, S32 size, S32 blocknum = 0, + S32 max_size = S32_MAX); + virtual void getBOOL(const char *block, const char *var, bool &data, + S32 blocknum = 0); + virtual void getS8(const char *block, const char *var, S8 &data, + S32 blocknum = 0); + virtual void getU8(const char *block, const char *var, U8 &data, + S32 blocknum = 0); + virtual void getS16(const char *block, const char *var, S16 &data, + S32 blocknum = 0); + virtual void getU16(const char *block, const char *var, U16 &data, + S32 blocknum = 0); + virtual void getS32(const char *block, const char *var, S32 &data, + S32 blocknum = 0); + virtual void getF32(const char *block, const char *var, F32 &data, + S32 blocknum = 0); + virtual void getU32(const char *block, const char *var, U32 &data, + S32 blocknum = 0); + virtual void getU64(const char *block, const char *var, U64 &data, + S32 blocknum = 0); + virtual void getF64(const char *block, const char *var, F64 &data, + S32 blocknum = 0); + virtual void getVector3(const char *block, const char *var, + LLVector3 &vec, S32 blocknum = 0); + virtual void getVector4(const char *block, const char *var, + LLVector4 &vec, S32 blocknum = 0); + virtual void getVector3d(const char *block, const char *var, + LLVector3d &vec, S32 blocknum = 0); + virtual void getQuat(const char *block, const char *var, LLQuaternion &q, + S32 blocknum = 0); + virtual void getUUID(const char *block, const char *var, LLUUID &uuid, + S32 blocknum = 0); + virtual void getIPAddr(const char *block, const char *var, U32 &ip, + S32 blocknum = 0); + virtual void getIPPort(const char *block, const char *var, U16 &port, + S32 blocknum = 0); + virtual void getString(const char *block, const char *var, + S32 buffer_size, char *buffer, S32 blocknum = 0); + virtual void getString(const char *block, const char *var, std::string& outstr, + S32 blocknum = 0); + + virtual S32 getNumberOfBlocks(const char *blockname); + virtual S32 getSize(const char *blockname, const char *varname); + virtual S32 getSize(const char *blockname, S32 blocknum, + const char *varname); + + virtual void clearMessage(); + + virtual const char* getMessageName() const; + virtual S32 getMessageSize() const; + + virtual void copyToBuilder(LLMessageBuilder&) const; + + /** Expects a pointer to a canonical name string */ + void setMessage(const char* name, const LLSD& msg); + +private: + const char* mMessageName; // Canonical (prehashed) string. + LLSD mMessage; +}; + +#endif // LL_LLSDMESSAGEREADER_H diff --git a/indra/llmessage/lltemplatemessagebuilder.cpp b/indra/llmessage/lltemplatemessagebuilder.cpp index 57ffba9705..758d6d343f 100644 --- a/indra/llmessage/lltemplatemessagebuilder.cpp +++ b/indra/llmessage/lltemplatemessagebuilder.cpp @@ -1,893 +1,893 @@ -/**
- * @file lltemplatemessagebuilder.cpp
- * @brief LLTemplateMessageBuilder class implementation.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lltemplatemessagebuilder.h"
-
-#include "llmessagetemplate.h"
-#include "llmath.h"
-#include "llquaternion.h"
-#include "u64.h"
-#include "v3dmath.h"
-#include "v3math.h"
-#include "v4math.h"
-
-LLTemplateMessageBuilder::LLTemplateMessageBuilder(const message_template_name_map_t& name_template_map) :
- mCurrentSMessageData(NULL),
- mCurrentSMessageTemplate(NULL),
- mCurrentSDataBlock(NULL),
- mCurrentSMessageName(NULL),
- mCurrentSBlockName(NULL),
- mbSBuilt(false),
- mbSClear(true),
- mCurrentSendTotal(0),
- mMessageTemplates(name_template_map)
-{
-}
-
-//virtual
-LLTemplateMessageBuilder::~LLTemplateMessageBuilder()
-{
- delete mCurrentSMessageData;
- mCurrentSMessageData = NULL;
-}
-
-// virtual
-void LLTemplateMessageBuilder::newMessage(const char *name)
-{
- mbSBuilt = false;
- mbSClear = false;
-
- mCurrentSendTotal = 0;
-
- delete mCurrentSMessageData;
- mCurrentSMessageData = NULL;
-
- char* namep = (char*)name;
- if (mMessageTemplates.count(namep) > 0)
- {
- mCurrentSMessageTemplate = mMessageTemplates.find(name)->second;
- mCurrentSMessageData = new LLMsgData(namep);
- mCurrentSMessageName = namep;
- mCurrentSDataBlock = NULL;
- mCurrentSBlockName = NULL;
-
- // add at one of each block
- const LLMessageTemplate* msg_template = mMessageTemplates.find(name)->second;
-
- if (msg_template->getDeprecation() != MD_NOTDEPRECATED)
- {
- LL_WARNS() << "Sending deprecated message " << namep << LL_ENDL;
- }
-
- LLMessageTemplate::message_block_map_t::const_iterator iter;
- for(iter = msg_template->mMemberBlocks.begin();
- iter != msg_template->mMemberBlocks.end();
- ++iter)
- {
- LLMessageBlock* ci = *iter;
- LLMsgBlkData* tblockp = new LLMsgBlkData(ci->mName, 0);
- mCurrentSMessageData->addBlock(tblockp);
- }
- }
- else
- {
- LL_ERRS() << "newMessage - Message " << name << " not registered" << LL_ENDL;
- }
-}
-
-// virtual
-void LLTemplateMessageBuilder::clearMessage()
-{
- mbSBuilt = false;
- mbSClear = true;
-
- mCurrentSendTotal = 0;
-
- mCurrentSMessageTemplate = NULL;
-
- delete mCurrentSMessageData;
- mCurrentSMessageData = NULL;
-
- mCurrentSMessageName = NULL;
- mCurrentSDataBlock = NULL;
- mCurrentSBlockName = NULL;
-}
-
-// virtual
-void LLTemplateMessageBuilder::nextBlock(const char* blockname)
-{
- char *bnamep = (char *)blockname;
-
- if (!mCurrentSMessageTemplate)
- {
- LL_ERRS() << "newMessage not called prior to setBlock" << LL_ENDL;
- return;
- }
-
- // now, does this block exist?
- const LLMessageBlock* template_data = mCurrentSMessageTemplate->getBlock(bnamep);
- if (!template_data)
- {
- LL_ERRS() << "LLTemplateMessageBuilder::nextBlock " << bnamep
- << " not a block in " << mCurrentSMessageTemplate->mName << LL_ENDL;
- return;
- }
-
- // 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::const_iterator iter = template_data->mMemberVariables.begin();
- iter != template_data->mMemberVariables.end(); iter++)
- {
- LLMessageVariable& ci = **iter;
- 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)
- {
- LL_ERRS() << "LLTemplateMessageBuilder::nextBlock called multiple times"
- << " for " << bnamep << " but is type MBT_SINGLE" << LL_ENDL;
- 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))
- {
- LL_ERRS() << "LLTemplateMessageBuilder::nextBlock called "
- << mCurrentSDataBlock->mBlockNumber << " times for " << bnamep
- << " exceeding " << template_data->mNumber
- << " specified in type MBT_MULTIPLE." << LL_ENDL;
- 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)
- {
- LL_ERRS() << "Trying to pack too many blocks into MBT_VARIABLE type "
- << "(limited to " << MAX_BLOCKS << ")" << LL_ENDL;
- }
-
- // 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::const_iterator
- iter = template_data->mMemberVariables.begin(),
- end = template_data->mMemberVariables.end();
- iter != end; iter++)
- {
- LLMessageVariable& ci = **iter;
- mCurrentSDataBlock->addVariable(ci.getName(), ci.getType());
- }
- return;
- }
-}
-
-// TODO: Remove this horror...
-bool LLTemplateMessageBuilder::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.
-
- const LLMessageBlock* template_data = mCurrentSMessageTemplate->getBlock(mCurrentSBlockName);
-
- for (LLMessageBlock::message_variable_map_t::const_iterator iter = template_data->mMemberVariables.begin();
- iter != template_data->mMemberVariables.end(); iter++)
- {
- LLMessageVariable& ci = **iter;
- 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
- LL_WARNS() << "not blowing away the only block of message "
- << mCurrentSMessageName
- << ". Block: " << block_name
- << ". Number: " << num_blocks
- << LL_ENDL;
- return false;
- }
- else
- {
- // Decrement the counter.
- block_data->mBlockNumber--;
- return true;
- }
- }
- }
- }
- return false;
-}
-
-// add data to variable in current block
-void LLTemplateMessageBuilder::addData(const char *varname, const void *data, EMsgVariableType type, S32 size)
-{
- char *vnamep = (char *)varname;
-
- // do we have a current message?
- if (!mCurrentSMessageTemplate)
- {
- LL_ERRS() << "newMessage not called prior to addData" << LL_ENDL;
- return;
- }
-
- // do we have a current block?
- if (!mCurrentSDataBlock)
- {
- LL_ERRS() << "setBlock not called prior to addData" << LL_ENDL;
- return;
- }
-
- // kewl, add the data if it exists
- const LLMessageVariable* var_data = mCurrentSMessageTemplate->getBlock(mCurrentSBlockName)->getVariable(vnamep);
- if (!var_data || !var_data->getName())
- {
- LL_ERRS() << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << LL_ENDL;
- 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))
- {
- LL_WARNS() << "Field " << varname << " is a Variable 1 but program "
- << "attempted to stuff more than 255 bytes in "
- << "(" << size << "). Clamping size and truncating data." << LL_ENDL;
- size = 255;
- char *truncate = (char *)data;
- truncate[254] = 0; // array size is 255 but the last element index is 254
- }
-
- // 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())
- {
- LL_ERRS() << varname << " is type MVT_FIXED but request size " << size << " doesn't match template size "
- << var_data->getSize() << LL_ENDL;
- 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 LLTemplateMessageBuilder::addData(const char *varname, const void *data, EMsgVariableType type)
-{
- char *vnamep = (char *)varname;
-
- // do we have a current message?
- if (!mCurrentSMessageTemplate)
- {
- LL_ERRS() << "newMessage not called prior to addData" << LL_ENDL;
- return;
- }
-
- // do we have a current block?
- if (!mCurrentSDataBlock)
- {
- LL_ERRS() << "setBlock not called prior to addData" << LL_ENDL;
- return;
- }
-
- // kewl, add the data if it exists
- const LLMessageVariable* var_data = mCurrentSMessageTemplate->getBlock(mCurrentSBlockName)->getVariable(vnamep);
- if (!var_data->getName())
- {
- LL_ERRS() << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << LL_ENDL;
- return;
- }
-
- // ok, it seems ok. . . are we MVT_VARIABLE?
- if (var_data->getType() == MVT_VARIABLE)
- {
- // nope
- LL_ERRS() << vnamep << " is type MVT_VARIABLE. Call using addData(name, data, size)" << LL_ENDL;
- return;
- }
- else
- {
- mCurrentSDataBlock->addData(vnamep, data, var_data->getSize(), type);
- mCurrentSendTotal += var_data->getSize();
- }
-}
-
-void LLTemplateMessageBuilder::addBinaryData(const char *varname,
- const void *data, S32 size)
-{
- addData(varname, data, MVT_FIXED, size);
-}
-
-void LLTemplateMessageBuilder::addS8(const char *varname, S8 s)
-{
- addData(varname, &s, MVT_S8, sizeof(s));
-}
-
-void LLTemplateMessageBuilder::addU8(const char *varname, U8 u)
-{
- addData(varname, &u, MVT_U8, sizeof(u));
-}
-
-void LLTemplateMessageBuilder::addS16(const char *varname, S16 i)
-{
- addData(varname, &i, MVT_S16, sizeof(i));
-}
-
-void LLTemplateMessageBuilder::addU16(const char *varname, U16 i)
-{
- addData(varname, &i, MVT_U16, sizeof(i));
-}
-
-void LLTemplateMessageBuilder::addF32(const char *varname, F32 f)
-{
- addData(varname, &f, MVT_F32, sizeof(f));
-}
-
-void LLTemplateMessageBuilder::addS32(const char *varname, S32 s)
-{
- addData(varname, &s, MVT_S32, sizeof(s));
-}
-
-void LLTemplateMessageBuilder::addU32(const char *varname, U32 u)
-{
- addData(varname, &u, MVT_U32, sizeof(u));
-}
-
-void LLTemplateMessageBuilder::addU64(const char *varname, U64 lu)
-{
- addData(varname, &lu, MVT_U64, sizeof(lu));
-}
-
-void LLTemplateMessageBuilder::addF64(const char *varname, F64 d)
-{
- addData(varname, &d, MVT_F64, sizeof(d));
-}
-
-void LLTemplateMessageBuilder::addIPAddr(const char *varname, U32 u)
-{
- addData(varname, &u, MVT_IP_ADDR, sizeof(u));
-}
-
-void LLTemplateMessageBuilder::addIPPort(const char *varname, U16 u)
-{
- u = htons(u);
- addData(varname, &u, MVT_IP_PORT, sizeof(u));
-}
-
-void LLTemplateMessageBuilder::addBOOL(const char* varname, bool b)
-{
- U8 temp = (b != 0);
- addData(varname, &temp, MVT_BOOL, sizeof(temp));
-}
-
-void LLTemplateMessageBuilder::addString(const char* varname, const char* s)
-{
- if (s)
- addData( varname, (void *)s, MVT_VARIABLE, (S32)strlen(s) + 1); /* Flawfinder: ignore */
- else
- addData( varname, NULL, MVT_VARIABLE, 0);
-}
-
-void LLTemplateMessageBuilder::addString(const char* varname, const std::string& s)
-{
- if (s.size())
- addData( varname, (void *)s.c_str(), MVT_VARIABLE, (S32)(s.size()) + 1);
- else
- addData( varname, NULL, MVT_VARIABLE, 0);
-}
-
-void LLTemplateMessageBuilder::addVector3(const char *varname, const LLVector3& vec)
-{
- addData(varname, vec.mV, MVT_LLVector3, sizeof(vec.mV));
-}
-
-void LLTemplateMessageBuilder::addVector4(const char *varname, const LLVector4& vec)
-{
- addData(varname, vec.mV, MVT_LLVector4, sizeof(vec.mV));
-}
-
-void LLTemplateMessageBuilder::addVector3d(const char *varname, const LLVector3d& vec)
-{
- addData(varname, vec.mdV, MVT_LLVector3d, sizeof(vec.mdV));
-}
-
-void LLTemplateMessageBuilder::addQuat(const char *varname, const LLQuaternion& quat)
-{
- addData(varname, quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3));
-}
-
-void LLTemplateMessageBuilder::addUUID(const char *varname, const LLUUID& uuid)
-{
- addData(varname, uuid.mData, MVT_LLUUID, sizeof(uuid.mData));
-}
-
-static S32 zero_code(U8 **data, U32 *data_size)
-{
- // Encoded send buffer needs to be slightly larger since the zero
- // coding can potentially increase the size of the send data.
- static U8 encodedSendBuffer[2 * MAX_BUFFER_SIZE];
-
- S32 count = *data_size;
-
- S32 net_gain = 0;
- U8 num_zeroes = 0;
-
- U8 *inptr = (U8 *)*data;
- U8 *outptr = (U8 *)encodedSendBuffer;
-
-// skip the packet id field
-
- for (U32 ii = 0; ii < LL_PACKET_ID_SIZE ; ++ii)
- {
- 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)
- {
- // TODO: babbage: reinstate stat collecting...
- //mCompressedPacketsOut++;
- //mUncompressedBytesOut += *data_size;
-
- *data = encodedSendBuffer;
- *data_size += net_gain;
- encodedSendBuffer[0] |= LL_ZERO_CODE_FLAG; // set the head bit to indicate zero coding
-
- //mCompressedBytesOut += *data_size;
-
- }
- //mTotalBytesOut += *data_size;
-
- return(net_gain);
-}
-
-void LLTemplateMessageBuilder::compressMessage(U8*& buf_ptr, U32& buffer_length)
-{
- if(ME_ZEROCODED == mCurrentSMessageTemplate->getEncoding())
- {
- zero_code(&buf_ptr, &buffer_length);
- }
-}
-
-bool LLTemplateMessageBuilder::isMessageFull(const char* blockname) const
-{
- if(mCurrentSendTotal > MTUBYTES)
- {
- return true;
- }
- if(!blockname)
- {
- return false;
- }
- char* bnamep = (char*)blockname;
- S32 max;
-
- const LLMessageBlock* template_data = mCurrentSMessageTemplate->getBlock(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;
-}
-
-static S32 buildBlock(U8* buffer, S32 buffer_size, const LLMessageBlock* template_data, LLMsgData* message_data)
-{
- S32 result = 0;
- LLMsgData::msg_blk_data_map_t::const_iterator block_iter = message_data->mMemberBlocks.find(template_data->mName);
- const LLMsgBlkData* mbci = block_iter->second;
-
- // 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
- S32 block_count = mbci->mBlockNumber;
- if (template_data->mType == MBT_VARIABLE)
- {
- // remember that mBlockNumber is a S32
- U8 temp_block_number = (U8)mbci->mBlockNumber;
- if ((S32)(result + sizeof(U8)) < MAX_BUFFER_SIZE)
- {
- memcpy(&buffer[result], &temp_block_number, sizeof(U8));
- result += sizeof(U8);
- }
- else
- {
- // Just reporting error is likely not enough. Need
- // to check how to abort or error out gracefully
- // from this function. XXXTBD
- LL_ERRS() << "buildBlock failed. Message excedding "
- << "sendBuffersize." << LL_ENDL;
- }
- }
- else if (template_data->mType == MBT_MULTIPLE)
- {
- if (block_count != template_data->mNumber)
- {
- // nope! need to fill it in all the way!
- LL_ERRS() << "Block " << mbci->mName
- << " is type MBT_MULTIPLE but only has data for "
- << block_count << " out of its "
- << template_data->mNumber << " blocks" << LL_ENDL;
- }
- }
-
- while(block_count > 0)
- {
- // now loop through the variables
- for (LLMsgBlkData::msg_var_data_map_t::const_iterator iter = mbci->mMemberVarData.begin();
- iter != mbci->mMemberVarData.end(); iter++)
- {
- const LLMsgVarData& mvci = *iter;
- if (mvci.getSize() == -1)
- {
- // oops, this variable wasn't ever set!
- LL_ERRS() << "The variable " << mvci.getName() << " in block "
- << mbci->mName << " of message "
- << template_data->mName
- << " wasn't set prior to buildMessage call" << LL_ENDL;
- }
- 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;
- htolememcpy(&buffer[result], &sizeb, MVT_U8, 1);
- break;
- case 2:
- sizeh = size;
- htolememcpy(&buffer[result], &sizeh, MVT_U16, 2);
- break;
- case 4:
- htolememcpy(&buffer[result], &size, MVT_S32, 4);
- break;
- default:
- LL_ERRS() << "Attempting to build variable field with unknown size of " << size << LL_ENDL;
- break;
- }
- result += mvci.getDataSize();
- }
-
- // if there is any data to pack, pack it
- if((mvci.getData() != NULL) && mvci.getSize())
- {
- if(result + mvci.getSize() < buffer_size)
- {
- memcpy(
- &buffer[result],
- mvci.getData(),
- mvci.getSize());
- result += mvci.getSize();
- }
- else
- {
- // Just reporting error is likely not
- // enough. Need to check how to abort or error
- // out gracefully from this function. XXXTBD
- LL_ERRS() << "buildBlock failed. "
- << "Attempted to pack "
- << (result + mvci.getSize())
- << " bytes into a buffer with size "
- << buffer_size << "." << LL_ENDL;
- }
- }
- }
- }
-
- --block_count;
-
- if (block_iter != message_data->mMemberBlocks.end())
- {
- ++block_iter;
- if (block_iter != message_data->mMemberBlocks.end())
- {
- mbci = block_iter->second;
- }
- }
- }
-
- return result;
-}
-
-
-// make sure that all the desired data is in place and then copy the data into MAX_BUFFER_SIZEd buffer
-U32 LLTemplateMessageBuilder::buildMessage(
- U8* buffer,
- U32 buffer_size,
- U8 offset_to_data)
-{
- // 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)
- {
- LL_ERRS() << "newMessage not called prior to buildMessage" << LL_ENDL;
- return 0;
- }
-
- // leave room for flags, packet sequence #, and data offset
- // information.
- buffer[PHL_OFFSET] = offset_to_data;
- U32 result = LL_PACKET_ID_SIZE;
-
- // encode message number and adjust total_offset
- if (mCurrentSMessageTemplate->mFrequency == MFT_HIGH)
- {
-// old, endian-dependant way
-// memcpy(&buffer[result], &mCurrentMessageTemplate->mMessageNumber, sizeof(U8));
-
-// new, independant way
- buffer[result] = (U8)mCurrentSMessageTemplate->mMessageNumber;
- result += sizeof(U8);
- }
- else if (mCurrentSMessageTemplate->mFrequency == MFT_MEDIUM)
- {
- U8 temp = 255;
- memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/
- result += sizeof(U8);
-
- // mask off unsightly bits
- temp = mCurrentSMessageTemplate->mMessageNumber & 255;
- memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/
- result += sizeof(U8);
- }
- else if (mCurrentSMessageTemplate->mFrequency == MFT_LOW)
- {
- U8 temp = 255;
- U16 message_num;
- memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/
- result += sizeof(U8);
- memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/
- result += sizeof(U8);
-
- // mask off unsightly bits
- message_num = mCurrentSMessageTemplate->mMessageNumber & 0xFFFF;
-
- // convert to network byte order
- message_num = htons(message_num);
- memcpy(&buffer[result], &message_num, sizeof(U16)); /*Flawfinder: ignore*/
- result += sizeof(U16);
- }
- else
- {
- LL_ERRS() << "unexpected message frequency in buildMessage" << LL_ENDL;
- return 0;
- }
-
- // fast forward through the offset and build the message
- result += offset_to_data;
- for(LLMessageTemplate::message_block_map_t::const_iterator
- iter = mCurrentSMessageTemplate->mMemberBlocks.begin(),
- end = mCurrentSMessageTemplate->mMemberBlocks.end();
- iter != end;
- ++iter)
- {
- result += buildBlock(buffer + result, buffer_size - result, *iter, mCurrentSMessageData);
- }
- mbSBuilt = true;
-
- return result;
-}
-
-void LLTemplateMessageBuilder::copyFromMessageData(const LLMsgData& data)
-{
- // 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::const_iterator iter =
- data.mMemberBlocks.begin();
- LLMsgData::msg_blk_data_map_t::const_iterator end =
- data.mMemberBlocks.end();
- for(; iter != end; ++iter)
- {
- const 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--;
-
- nextBlock(block_name);
-
- // now loop through the variables
- LLMsgBlkData::msg_var_data_map_t::const_iterator dit = mbci->mMemberVarData.begin();
- LLMsgBlkData::msg_var_data_map_t::const_iterator dend = mbci->mMemberVarData.end();
-
- for(; dit != dend; ++dit)
- {
- const LLMsgVarData& mvci = *dit;
- addData(mvci.getName(), mvci.getData(), mvci.getType(), mvci.getSize());
- }
- }
-}
-
-//virtual
-void LLTemplateMessageBuilder::copyFromLLSD(const LLSD&)
-{
- // TODO
-}
-
-//virtual
-void LLTemplateMessageBuilder::setBuilt(bool b) { mbSBuilt = b; }
-
-//virtual
-bool LLTemplateMessageBuilder::isBuilt() const {return mbSBuilt;}
-
-//virtual
-bool LLTemplateMessageBuilder::isClear() const {return mbSClear;}
-
-//virtual
-S32 LLTemplateMessageBuilder::getMessageSize() {return mCurrentSendTotal;}
-
-//virtual
-const char* LLTemplateMessageBuilder::getMessageName() const
-{
- return mCurrentSMessageName;
-}
+/** + * @file lltemplatemessagebuilder.cpp + * @brief LLTemplateMessageBuilder class implementation. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lltemplatemessagebuilder.h" + +#include "llmessagetemplate.h" +#include "llmath.h" +#include "llquaternion.h" +#include "u64.h" +#include "v3dmath.h" +#include "v3math.h" +#include "v4math.h" + +LLTemplateMessageBuilder::LLTemplateMessageBuilder(const message_template_name_map_t& name_template_map) : + mCurrentSMessageData(NULL), + mCurrentSMessageTemplate(NULL), + mCurrentSDataBlock(NULL), + mCurrentSMessageName(NULL), + mCurrentSBlockName(NULL), + mbSBuilt(false), + mbSClear(true), + mCurrentSendTotal(0), + mMessageTemplates(name_template_map) +{ +} + +//virtual +LLTemplateMessageBuilder::~LLTemplateMessageBuilder() +{ + delete mCurrentSMessageData; + mCurrentSMessageData = NULL; +} + +// virtual +void LLTemplateMessageBuilder::newMessage(const char *name) +{ + mbSBuilt = false; + mbSClear = false; + + mCurrentSendTotal = 0; + + delete mCurrentSMessageData; + mCurrentSMessageData = NULL; + + char* namep = (char*)name; + if (mMessageTemplates.count(namep) > 0) + { + mCurrentSMessageTemplate = mMessageTemplates.find(name)->second; + mCurrentSMessageData = new LLMsgData(namep); + mCurrentSMessageName = namep; + mCurrentSDataBlock = NULL; + mCurrentSBlockName = NULL; + + // add at one of each block + const LLMessageTemplate* msg_template = mMessageTemplates.find(name)->second; + + if (msg_template->getDeprecation() != MD_NOTDEPRECATED) + { + LL_WARNS() << "Sending deprecated message " << namep << LL_ENDL; + } + + LLMessageTemplate::message_block_map_t::const_iterator iter; + for(iter = msg_template->mMemberBlocks.begin(); + iter != msg_template->mMemberBlocks.end(); + ++iter) + { + LLMessageBlock* ci = *iter; + LLMsgBlkData* tblockp = new LLMsgBlkData(ci->mName, 0); + mCurrentSMessageData->addBlock(tblockp); + } + } + else + { + LL_ERRS() << "newMessage - Message " << name << " not registered" << LL_ENDL; + } +} + +// virtual +void LLTemplateMessageBuilder::clearMessage() +{ + mbSBuilt = false; + mbSClear = true; + + mCurrentSendTotal = 0; + + mCurrentSMessageTemplate = NULL; + + delete mCurrentSMessageData; + mCurrentSMessageData = NULL; + + mCurrentSMessageName = NULL; + mCurrentSDataBlock = NULL; + mCurrentSBlockName = NULL; +} + +// virtual +void LLTemplateMessageBuilder::nextBlock(const char* blockname) +{ + char *bnamep = (char *)blockname; + + if (!mCurrentSMessageTemplate) + { + LL_ERRS() << "newMessage not called prior to setBlock" << LL_ENDL; + return; + } + + // now, does this block exist? + const LLMessageBlock* template_data = mCurrentSMessageTemplate->getBlock(bnamep); + if (!template_data) + { + LL_ERRS() << "LLTemplateMessageBuilder::nextBlock " << bnamep + << " not a block in " << mCurrentSMessageTemplate->mName << LL_ENDL; + return; + } + + // 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::const_iterator iter = template_data->mMemberVariables.begin(); + iter != template_data->mMemberVariables.end(); iter++) + { + LLMessageVariable& ci = **iter; + 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) + { + LL_ERRS() << "LLTemplateMessageBuilder::nextBlock called multiple times" + << " for " << bnamep << " but is type MBT_SINGLE" << LL_ENDL; + 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)) + { + LL_ERRS() << "LLTemplateMessageBuilder::nextBlock called " + << mCurrentSDataBlock->mBlockNumber << " times for " << bnamep + << " exceeding " << template_data->mNumber + << " specified in type MBT_MULTIPLE." << LL_ENDL; + 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) + { + LL_ERRS() << "Trying to pack too many blocks into MBT_VARIABLE type " + << "(limited to " << MAX_BLOCKS << ")" << LL_ENDL; + } + + // 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::const_iterator + iter = template_data->mMemberVariables.begin(), + end = template_data->mMemberVariables.end(); + iter != end; iter++) + { + LLMessageVariable& ci = **iter; + mCurrentSDataBlock->addVariable(ci.getName(), ci.getType()); + } + return; + } +} + +// TODO: Remove this horror... +bool LLTemplateMessageBuilder::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. + + const LLMessageBlock* template_data = mCurrentSMessageTemplate->getBlock(mCurrentSBlockName); + + for (LLMessageBlock::message_variable_map_t::const_iterator iter = template_data->mMemberVariables.begin(); + iter != template_data->mMemberVariables.end(); iter++) + { + LLMessageVariable& ci = **iter; + 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 + LL_WARNS() << "not blowing away the only block of message " + << mCurrentSMessageName + << ". Block: " << block_name + << ". Number: " << num_blocks + << LL_ENDL; + return false; + } + else + { + // Decrement the counter. + block_data->mBlockNumber--; + return true; + } + } + } + } + return false; +} + +// add data to variable in current block +void LLTemplateMessageBuilder::addData(const char *varname, const void *data, EMsgVariableType type, S32 size) +{ + char *vnamep = (char *)varname; + + // do we have a current message? + if (!mCurrentSMessageTemplate) + { + LL_ERRS() << "newMessage not called prior to addData" << LL_ENDL; + return; + } + + // do we have a current block? + if (!mCurrentSDataBlock) + { + LL_ERRS() << "setBlock not called prior to addData" << LL_ENDL; + return; + } + + // kewl, add the data if it exists + const LLMessageVariable* var_data = mCurrentSMessageTemplate->getBlock(mCurrentSBlockName)->getVariable(vnamep); + if (!var_data || !var_data->getName()) + { + LL_ERRS() << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << LL_ENDL; + 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)) + { + LL_WARNS() << "Field " << varname << " is a Variable 1 but program " + << "attempted to stuff more than 255 bytes in " + << "(" << size << "). Clamping size and truncating data." << LL_ENDL; + size = 255; + char *truncate = (char *)data; + truncate[254] = 0; // array size is 255 but the last element index is 254 + } + + // 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()) + { + LL_ERRS() << varname << " is type MVT_FIXED but request size " << size << " doesn't match template size " + << var_data->getSize() << LL_ENDL; + 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 LLTemplateMessageBuilder::addData(const char *varname, const void *data, EMsgVariableType type) +{ + char *vnamep = (char *)varname; + + // do we have a current message? + if (!mCurrentSMessageTemplate) + { + LL_ERRS() << "newMessage not called prior to addData" << LL_ENDL; + return; + } + + // do we have a current block? + if (!mCurrentSDataBlock) + { + LL_ERRS() << "setBlock not called prior to addData" << LL_ENDL; + return; + } + + // kewl, add the data if it exists + const LLMessageVariable* var_data = mCurrentSMessageTemplate->getBlock(mCurrentSBlockName)->getVariable(vnamep); + if (!var_data->getName()) + { + LL_ERRS() << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << LL_ENDL; + return; + } + + // ok, it seems ok. . . are we MVT_VARIABLE? + if (var_data->getType() == MVT_VARIABLE) + { + // nope + LL_ERRS() << vnamep << " is type MVT_VARIABLE. Call using addData(name, data, size)" << LL_ENDL; + return; + } + else + { + mCurrentSDataBlock->addData(vnamep, data, var_data->getSize(), type); + mCurrentSendTotal += var_data->getSize(); + } +} + +void LLTemplateMessageBuilder::addBinaryData(const char *varname, + const void *data, S32 size) +{ + addData(varname, data, MVT_FIXED, size); +} + +void LLTemplateMessageBuilder::addS8(const char *varname, S8 s) +{ + addData(varname, &s, MVT_S8, sizeof(s)); +} + +void LLTemplateMessageBuilder::addU8(const char *varname, U8 u) +{ + addData(varname, &u, MVT_U8, sizeof(u)); +} + +void LLTemplateMessageBuilder::addS16(const char *varname, S16 i) +{ + addData(varname, &i, MVT_S16, sizeof(i)); +} + +void LLTemplateMessageBuilder::addU16(const char *varname, U16 i) +{ + addData(varname, &i, MVT_U16, sizeof(i)); +} + +void LLTemplateMessageBuilder::addF32(const char *varname, F32 f) +{ + addData(varname, &f, MVT_F32, sizeof(f)); +} + +void LLTemplateMessageBuilder::addS32(const char *varname, S32 s) +{ + addData(varname, &s, MVT_S32, sizeof(s)); +} + +void LLTemplateMessageBuilder::addU32(const char *varname, U32 u) +{ + addData(varname, &u, MVT_U32, sizeof(u)); +} + +void LLTemplateMessageBuilder::addU64(const char *varname, U64 lu) +{ + addData(varname, &lu, MVT_U64, sizeof(lu)); +} + +void LLTemplateMessageBuilder::addF64(const char *varname, F64 d) +{ + addData(varname, &d, MVT_F64, sizeof(d)); +} + +void LLTemplateMessageBuilder::addIPAddr(const char *varname, U32 u) +{ + addData(varname, &u, MVT_IP_ADDR, sizeof(u)); +} + +void LLTemplateMessageBuilder::addIPPort(const char *varname, U16 u) +{ + u = htons(u); + addData(varname, &u, MVT_IP_PORT, sizeof(u)); +} + +void LLTemplateMessageBuilder::addBOOL(const char* varname, bool b) +{ + U8 temp = (b != 0); + addData(varname, &temp, MVT_BOOL, sizeof(temp)); +} + +void LLTemplateMessageBuilder::addString(const char* varname, const char* s) +{ + if (s) + addData( varname, (void *)s, MVT_VARIABLE, (S32)strlen(s) + 1); /* Flawfinder: ignore */ + else + addData( varname, NULL, MVT_VARIABLE, 0); +} + +void LLTemplateMessageBuilder::addString(const char* varname, const std::string& s) +{ + if (s.size()) + addData( varname, (void *)s.c_str(), MVT_VARIABLE, (S32)(s.size()) + 1); + else + addData( varname, NULL, MVT_VARIABLE, 0); +} + +void LLTemplateMessageBuilder::addVector3(const char *varname, const LLVector3& vec) +{ + addData(varname, vec.mV, MVT_LLVector3, sizeof(vec.mV)); +} + +void LLTemplateMessageBuilder::addVector4(const char *varname, const LLVector4& vec) +{ + addData(varname, vec.mV, MVT_LLVector4, sizeof(vec.mV)); +} + +void LLTemplateMessageBuilder::addVector3d(const char *varname, const LLVector3d& vec) +{ + addData(varname, vec.mdV, MVT_LLVector3d, sizeof(vec.mdV)); +} + +void LLTemplateMessageBuilder::addQuat(const char *varname, const LLQuaternion& quat) +{ + addData(varname, quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3)); +} + +void LLTemplateMessageBuilder::addUUID(const char *varname, const LLUUID& uuid) +{ + addData(varname, uuid.mData, MVT_LLUUID, sizeof(uuid.mData)); +} + +static S32 zero_code(U8 **data, U32 *data_size) +{ + // Encoded send buffer needs to be slightly larger since the zero + // coding can potentially increase the size of the send data. + static U8 encodedSendBuffer[2 * MAX_BUFFER_SIZE]; + + S32 count = *data_size; + + S32 net_gain = 0; + U8 num_zeroes = 0; + + U8 *inptr = (U8 *)*data; + U8 *outptr = (U8 *)encodedSendBuffer; + +// skip the packet id field + + for (U32 ii = 0; ii < LL_PACKET_ID_SIZE ; ++ii) + { + 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) + { + // TODO: babbage: reinstate stat collecting... + //mCompressedPacketsOut++; + //mUncompressedBytesOut += *data_size; + + *data = encodedSendBuffer; + *data_size += net_gain; + encodedSendBuffer[0] |= LL_ZERO_CODE_FLAG; // set the head bit to indicate zero coding + + //mCompressedBytesOut += *data_size; + + } + //mTotalBytesOut += *data_size; + + return(net_gain); +} + +void LLTemplateMessageBuilder::compressMessage(U8*& buf_ptr, U32& buffer_length) +{ + if(ME_ZEROCODED == mCurrentSMessageTemplate->getEncoding()) + { + zero_code(&buf_ptr, &buffer_length); + } +} + +bool LLTemplateMessageBuilder::isMessageFull(const char* blockname) const +{ + if(mCurrentSendTotal > MTUBYTES) + { + return true; + } + if(!blockname) + { + return false; + } + char* bnamep = (char*)blockname; + S32 max; + + const LLMessageBlock* template_data = mCurrentSMessageTemplate->getBlock(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; +} + +static S32 buildBlock(U8* buffer, S32 buffer_size, const LLMessageBlock* template_data, LLMsgData* message_data) +{ + S32 result = 0; + LLMsgData::msg_blk_data_map_t::const_iterator block_iter = message_data->mMemberBlocks.find(template_data->mName); + const LLMsgBlkData* mbci = block_iter->second; + + // 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 + S32 block_count = mbci->mBlockNumber; + if (template_data->mType == MBT_VARIABLE) + { + // remember that mBlockNumber is a S32 + U8 temp_block_number = (U8)mbci->mBlockNumber; + if ((S32)(result + sizeof(U8)) < MAX_BUFFER_SIZE) + { + memcpy(&buffer[result], &temp_block_number, sizeof(U8)); + result += sizeof(U8); + } + else + { + // Just reporting error is likely not enough. Need + // to check how to abort or error out gracefully + // from this function. XXXTBD + LL_ERRS() << "buildBlock failed. Message excedding " + << "sendBuffersize." << LL_ENDL; + } + } + else if (template_data->mType == MBT_MULTIPLE) + { + if (block_count != template_data->mNumber) + { + // nope! need to fill it in all the way! + LL_ERRS() << "Block " << mbci->mName + << " is type MBT_MULTIPLE but only has data for " + << block_count << " out of its " + << template_data->mNumber << " blocks" << LL_ENDL; + } + } + + while(block_count > 0) + { + // now loop through the variables + for (LLMsgBlkData::msg_var_data_map_t::const_iterator iter = mbci->mMemberVarData.begin(); + iter != mbci->mMemberVarData.end(); iter++) + { + const LLMsgVarData& mvci = *iter; + if (mvci.getSize() == -1) + { + // oops, this variable wasn't ever set! + LL_ERRS() << "The variable " << mvci.getName() << " in block " + << mbci->mName << " of message " + << template_data->mName + << " wasn't set prior to buildMessage call" << LL_ENDL; + } + 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; + htolememcpy(&buffer[result], &sizeb, MVT_U8, 1); + break; + case 2: + sizeh = size; + htolememcpy(&buffer[result], &sizeh, MVT_U16, 2); + break; + case 4: + htolememcpy(&buffer[result], &size, MVT_S32, 4); + break; + default: + LL_ERRS() << "Attempting to build variable field with unknown size of " << size << LL_ENDL; + break; + } + result += mvci.getDataSize(); + } + + // if there is any data to pack, pack it + if((mvci.getData() != NULL) && mvci.getSize()) + { + if(result + mvci.getSize() < buffer_size) + { + memcpy( + &buffer[result], + mvci.getData(), + mvci.getSize()); + result += mvci.getSize(); + } + else + { + // Just reporting error is likely not + // enough. Need to check how to abort or error + // out gracefully from this function. XXXTBD + LL_ERRS() << "buildBlock failed. " + << "Attempted to pack " + << (result + mvci.getSize()) + << " bytes into a buffer with size " + << buffer_size << "." << LL_ENDL; + } + } + } + } + + --block_count; + + if (block_iter != message_data->mMemberBlocks.end()) + { + ++block_iter; + if (block_iter != message_data->mMemberBlocks.end()) + { + mbci = block_iter->second; + } + } + } + + return result; +} + + +// make sure that all the desired data is in place and then copy the data into MAX_BUFFER_SIZEd buffer +U32 LLTemplateMessageBuilder::buildMessage( + U8* buffer, + U32 buffer_size, + U8 offset_to_data) +{ + // 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) + { + LL_ERRS() << "newMessage not called prior to buildMessage" << LL_ENDL; + return 0; + } + + // leave room for flags, packet sequence #, and data offset + // information. + buffer[PHL_OFFSET] = offset_to_data; + U32 result = LL_PACKET_ID_SIZE; + + // encode message number and adjust total_offset + if (mCurrentSMessageTemplate->mFrequency == MFT_HIGH) + { +// old, endian-dependant way +// memcpy(&buffer[result], &mCurrentMessageTemplate->mMessageNumber, sizeof(U8)); + +// new, independant way + buffer[result] = (U8)mCurrentSMessageTemplate->mMessageNumber; + result += sizeof(U8); + } + else if (mCurrentSMessageTemplate->mFrequency == MFT_MEDIUM) + { + U8 temp = 255; + memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/ + result += sizeof(U8); + + // mask off unsightly bits + temp = mCurrentSMessageTemplate->mMessageNumber & 255; + memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/ + result += sizeof(U8); + } + else if (mCurrentSMessageTemplate->mFrequency == MFT_LOW) + { + U8 temp = 255; + U16 message_num; + memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/ + result += sizeof(U8); + memcpy(&buffer[result], &temp, sizeof(U8)); /*Flawfinder: ignore*/ + result += sizeof(U8); + + // mask off unsightly bits + message_num = mCurrentSMessageTemplate->mMessageNumber & 0xFFFF; + + // convert to network byte order + message_num = htons(message_num); + memcpy(&buffer[result], &message_num, sizeof(U16)); /*Flawfinder: ignore*/ + result += sizeof(U16); + } + else + { + LL_ERRS() << "unexpected message frequency in buildMessage" << LL_ENDL; + return 0; + } + + // fast forward through the offset and build the message + result += offset_to_data; + for(LLMessageTemplate::message_block_map_t::const_iterator + iter = mCurrentSMessageTemplate->mMemberBlocks.begin(), + end = mCurrentSMessageTemplate->mMemberBlocks.end(); + iter != end; + ++iter) + { + result += buildBlock(buffer + result, buffer_size - result, *iter, mCurrentSMessageData); + } + mbSBuilt = true; + + return result; +} + +void LLTemplateMessageBuilder::copyFromMessageData(const LLMsgData& data) +{ + // 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::const_iterator iter = + data.mMemberBlocks.begin(); + LLMsgData::msg_blk_data_map_t::const_iterator end = + data.mMemberBlocks.end(); + for(; iter != end; ++iter) + { + const 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--; + + nextBlock(block_name); + + // now loop through the variables + LLMsgBlkData::msg_var_data_map_t::const_iterator dit = mbci->mMemberVarData.begin(); + LLMsgBlkData::msg_var_data_map_t::const_iterator dend = mbci->mMemberVarData.end(); + + for(; dit != dend; ++dit) + { + const LLMsgVarData& mvci = *dit; + addData(mvci.getName(), mvci.getData(), mvci.getType(), mvci.getSize()); + } + } +} + +//virtual +void LLTemplateMessageBuilder::copyFromLLSD(const LLSD&) +{ + // TODO +} + +//virtual +void LLTemplateMessageBuilder::setBuilt(bool b) { mbSBuilt = b; } + +//virtual +bool LLTemplateMessageBuilder::isBuilt() const {return mbSBuilt;} + +//virtual +bool LLTemplateMessageBuilder::isClear() const {return mbSClear;} + +//virtual +S32 LLTemplateMessageBuilder::getMessageSize() {return mCurrentSendTotal;} + +//virtual +const char* LLTemplateMessageBuilder::getMessageName() const +{ + return mCurrentSMessageName; +} diff --git a/indra/llmessage/lltemplatemessagebuilder.h b/indra/llmessage/lltemplatemessagebuilder.h index bb50b0ab8a..b86ec4d87d 100644 --- a/indra/llmessage/lltemplatemessagebuilder.h +++ b/indra/llmessage/lltemplatemessagebuilder.h @@ -1,115 +1,115 @@ -/**
- * @file lltemplatemessagebuilder.h
- * @brief Declaration of LLTemplateMessageBuilder class.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTEMPLATEMESSAGEBUILDER_H
-#define LL_LLTEMPLATEMESSAGEBUILDER_H
-
-#include <map>
-
-#include "llmessagebuilder.h"
-#include "llmsgvariabletype.h"
-
-class LLMsgData;
-class LLMessageTemplate;
-class LLMsgBlkData;
-class LLMessageTemplate;
-
-class LLTemplateMessageBuilder : public LLMessageBuilder
-{
-public:
-
- typedef std::map<const char* , LLMessageTemplate*> message_template_name_map_t;
-
- LLTemplateMessageBuilder(const message_template_name_map_t&);
- virtual ~LLTemplateMessageBuilder();
-
- virtual void newMessage(const char* name);
-
- virtual void nextBlock(const char* blockname);
- virtual bool removeLastBlock(); // TODO: babbage: remove this horror...
-
- /** All add* methods expect pointers to canonical varname strings. */
- virtual void addBinaryData(const char *varname, const void *data,
- S32 size);
- virtual void addBOOL(const char* varname, bool b);
- virtual void addS8(const char* varname, S8 s);
- virtual void addU8(const char* varname, U8 u);
- virtual void addS16(const char* varname, S16 i);
- virtual void addU16(const char* varname, U16 i);
- virtual void addF32(const char* varname, F32 f);
- virtual void addS32(const char* varname, S32 s);
- virtual void addU32(const char* varname, U32 u);
- virtual void addU64(const char* varname, U64 lu);
- virtual void addF64(const char* varname, F64 d);
- virtual void addVector3(const char* varname, const LLVector3& vec);
- virtual void addVector4(const char* varname, const LLVector4& vec);
- virtual void addVector3d(const char* varname, const LLVector3d& vec);
- virtual void addQuat(const char* varname, const LLQuaternion& quat);
- virtual void addUUID(const char* varname, const LLUUID& uuid);
- virtual void addIPAddr(const char* varname, const U32 ip);
- virtual void addIPPort(const char* varname, const U16 port);
- virtual void addString(const char* varname, const char* s);
- virtual void addString(const char* varname, const std::string& s);
-
- virtual bool isMessageFull(const char* blockname) const;
- virtual void compressMessage(U8*& buf_ptr, U32& buffer_length);
-
- virtual bool isBuilt() const;
- virtual bool isClear() const;
- virtual U32 buildMessage(U8* buffer, U32 buffer_size, U8 offset_to_data);
- /**< Return built message size */
-
- virtual void clearMessage();
-
- // TODO: babbage: remove this horror.
- virtual void setBuilt(bool b);
-
- virtual S32 getMessageSize();
- virtual const char* getMessageName() const;
-
- virtual void copyFromMessageData(const LLMsgData& data);
- virtual void copyFromLLSD(const LLSD&);
-
- LLMsgData* getCurrentMessage() const { return mCurrentSMessageData; }
-private:
- void addData(const char* varname, const void* data,
- EMsgVariableType type, S32 size);
-
- void addData(const char* varname, const void* data,
- EMsgVariableType type);
-
- LLMsgData* mCurrentSMessageData;
- const LLMessageTemplate* mCurrentSMessageTemplate;
- LLMsgBlkData* mCurrentSDataBlock;
- char* mCurrentSMessageName;
- char* mCurrentSBlockName;
- bool mbSBuilt;
- bool mbSClear;
- S32 mCurrentSendTotal;
- const message_template_name_map_t& mMessageTemplates;
-};
-
-#endif // LL_LLTEMPLATEMESSAGEBUILDER_H
+/** + * @file lltemplatemessagebuilder.h + * @brief Declaration of LLTemplateMessageBuilder class. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEMPLATEMESSAGEBUILDER_H +#define LL_LLTEMPLATEMESSAGEBUILDER_H + +#include <map> + +#include "llmessagebuilder.h" +#include "llmsgvariabletype.h" + +class LLMsgData; +class LLMessageTemplate; +class LLMsgBlkData; +class LLMessageTemplate; + +class LLTemplateMessageBuilder : public LLMessageBuilder +{ +public: + + typedef std::map<const char* , LLMessageTemplate*> message_template_name_map_t; + + LLTemplateMessageBuilder(const message_template_name_map_t&); + virtual ~LLTemplateMessageBuilder(); + + virtual void newMessage(const char* name); + + virtual void nextBlock(const char* blockname); + virtual bool removeLastBlock(); // TODO: babbage: remove this horror... + + /** All add* methods expect pointers to canonical varname strings. */ + virtual void addBinaryData(const char *varname, const void *data, + S32 size); + virtual void addBOOL(const char* varname, bool b); + virtual void addS8(const char* varname, S8 s); + virtual void addU8(const char* varname, U8 u); + virtual void addS16(const char* varname, S16 i); + virtual void addU16(const char* varname, U16 i); + virtual void addF32(const char* varname, F32 f); + virtual void addS32(const char* varname, S32 s); + virtual void addU32(const char* varname, U32 u); + virtual void addU64(const char* varname, U64 lu); + virtual void addF64(const char* varname, F64 d); + virtual void addVector3(const char* varname, const LLVector3& vec); + virtual void addVector4(const char* varname, const LLVector4& vec); + virtual void addVector3d(const char* varname, const LLVector3d& vec); + virtual void addQuat(const char* varname, const LLQuaternion& quat); + virtual void addUUID(const char* varname, const LLUUID& uuid); + virtual void addIPAddr(const char* varname, const U32 ip); + virtual void addIPPort(const char* varname, const U16 port); + virtual void addString(const char* varname, const char* s); + virtual void addString(const char* varname, const std::string& s); + + virtual bool isMessageFull(const char* blockname) const; + virtual void compressMessage(U8*& buf_ptr, U32& buffer_length); + + virtual bool isBuilt() const; + virtual bool isClear() const; + virtual U32 buildMessage(U8* buffer, U32 buffer_size, U8 offset_to_data); + /**< Return built message size */ + + virtual void clearMessage(); + + // TODO: babbage: remove this horror. + virtual void setBuilt(bool b); + + virtual S32 getMessageSize(); + virtual const char* getMessageName() const; + + virtual void copyFromMessageData(const LLMsgData& data); + virtual void copyFromLLSD(const LLSD&); + + LLMsgData* getCurrentMessage() const { return mCurrentSMessageData; } +private: + void addData(const char* varname, const void* data, + EMsgVariableType type, S32 size); + + void addData(const char* varname, const void* data, + EMsgVariableType type); + + LLMsgData* mCurrentSMessageData; + const LLMessageTemplate* mCurrentSMessageTemplate; + LLMsgBlkData* mCurrentSDataBlock; + char* mCurrentSMessageName; + char* mCurrentSBlockName; + bool mbSBuilt; + bool mbSClear; + S32 mCurrentSendTotal; + const message_template_name_map_t& mMessageTemplates; +}; + +#endif // LL_LLTEMPLATEMESSAGEBUILDER_H diff --git a/indra/llmessage/lltemplatemessagereader.cpp b/indra/llmessage/lltemplatemessagereader.cpp index 46123f0f55..b62288590e 100644 --- a/indra/llmessage/lltemplatemessagereader.cpp +++ b/indra/llmessage/lltemplatemessagereader.cpp @@ -1,830 +1,830 @@ -/**
- * @file lltemplatemessagereader.cpp
- * @brief LLTemplateMessageReader class implementation.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "lltemplatemessagereader.h"
-
-#include "llfasttimer.h"
-#include "llmessagebuilder.h"
-#include "llmessagetemplate.h"
-#include "llmath.h"
-#include "llquaternion.h"
-#include "message.h"
-#include "u64.h"
-#include "v3dmath.h"
-#include "v3math.h"
-#include "v4math.h"
-
-LLTemplateMessageReader::LLTemplateMessageReader(message_template_number_map_t&
- number_template_map) :
- mReceiveSize(0),
- mCurrentRMessageTemplate(NULL),
- mCurrentRMessageData(NULL),
- mMessageNumbers(number_template_map)
-{
-}
-
-//virtual
-LLTemplateMessageReader::~LLTemplateMessageReader()
-{
- delete mCurrentRMessageData;
- mCurrentRMessageData = NULL;
-}
-
-//virtual
-void LLTemplateMessageReader::clearMessage()
-{
- mReceiveSize = -1;
- mCurrentRMessageTemplate = NULL;
- delete mCurrentRMessageData;
- mCurrentRMessageData = NULL;
-}
-
-void LLTemplateMessageReader::getData(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)
- {
- LL_ERRS() << "No message waiting for decode 2!" << LL_ENDL;
- return;
- }
-
- if (!mCurrentRMessageData)
- {
- LL_ERRS() << "Invalid mCurrentMessageData in getData!" << LL_ENDL;
- 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::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
-
- if (iter == mCurrentRMessageData->mMemberBlocks.end())
- {
- LL_ERRS() << "Block " << blockname << " #" << blocknum
- << " not in message " << mCurrentRMessageData->mName << LL_ENDL;
- return;
- }
-
- LLMsgBlkData *msg_block_data = iter->second;
- LLMsgBlkData::msg_var_data_map_t &var_data_map = msg_block_data->mMemberVarData;
-
- if (var_data_map.find(vnamep) == var_data_map.end())
- {
- LL_ERRS() << "Variable "<< vnamep << " not in message "
- << mCurrentRMessageData->mName<< " block " << bnamep << LL_ENDL;
- return;
- }
-
- LLMsgVarData& vardata = msg_block_data->mMemberVarData[vnamep];
-
- if (size && size != vardata.getSize())
- {
- LL_ERRS() << "Msg " << mCurrentRMessageData->mName
- << " variable " << vnamep
- << " is size " << vardata.getSize()
- << " but copying into buffer of size " << size
- << LL_ENDL;
- 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
- {
- LL_WARNS() << "Msg " << mCurrentRMessageData->mName
- << " variable " << vnamep
- << " is size " << vardata.getSize()
- << " but truncated to max size of " << max_size
- << LL_ENDL;
-
- memcpy(datap, vardata.getData(), max_size);
- }
-}
-
-S32 LLTemplateMessageReader::getNumberOfBlocks(const char *blockname)
-{
- // is there a message ready to go?
- if (mReceiveSize == -1)
- {
- LL_ERRS() << "No message waiting for decode 3!" << LL_ENDL;
- return -1;
- }
-
- if (!mCurrentRMessageData)
- {
- LL_ERRS() << "Invalid mCurrentRMessageData in getData!" << LL_ENDL;
- return -1;
- }
-
- char *bnamep = (char *)blockname;
-
- LLMsgData::msg_blk_data_map_t::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
-
- if (iter == mCurrentRMessageData->mMemberBlocks.end())
- {
- return 0;
- }
-
- return (iter->second)->mBlockNumber;
-}
-
-S32 LLTemplateMessageReader::getSize(const char *blockname, const char *varname)
-{
- // is there a message ready to go?
- if (mReceiveSize == -1)
- { // This is a serious error - crash
- LL_ERRS() << "No message waiting for decode 4!" << LL_ENDL;
- return LL_MESSAGE_ERROR;
- }
-
- if (!mCurrentRMessageData)
- { // This is a serious error - crash
- LL_ERRS() << "Invalid mCurrentRMessageData in getData!" << LL_ENDL;
- return LL_MESSAGE_ERROR;
- }
-
- char *bnamep = (char *)blockname;
-
- LLMsgData::msg_blk_data_map_t::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
-
- if (iter == mCurrentRMessageData->mMemberBlocks.end())
- { // don't crash
- LL_INFOS() << "Block " << bnamep << " not in message "
- << mCurrentRMessageData->mName << LL_ENDL;
- return LL_BLOCK_NOT_IN_MESSAGE;
- }
-
- char *vnamep = (char *)varname;
-
- LLMsgBlkData* msg_data = iter->second;
- LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep];
-
- if (!vardata.getName())
- { // don't crash
- LL_INFOS() << "Variable " << varname << " not in message "
- << mCurrentRMessageData->mName << " block " << bnamep << LL_ENDL;
- return LL_VARIABLE_NOT_IN_BLOCK;
- }
-
- if (mCurrentRMessageTemplate->mMemberBlocks[bnamep]->mType != MBT_SINGLE)
- { // This is a serious error - crash
- LL_ERRS() << "Block " << bnamep << " isn't type MBT_SINGLE,"
- " use getSize with blocknum argument!" << LL_ENDL;
- return LL_MESSAGE_ERROR;
- }
-
- return vardata.getSize();
-}
-
-S32 LLTemplateMessageReader::getSize(const char *blockname, S32 blocknum, const char *varname)
-{
- // is there a message ready to go?
- if (mReceiveSize == -1)
- { // This is a serious error - crash
- LL_ERRS() << "No message waiting for decode 5!" << LL_ENDL;
- return LL_MESSAGE_ERROR;
- }
-
- if (!mCurrentRMessageData)
- { // This is a serious error - crash
- LL_ERRS() << "Invalid mCurrentRMessageData in getData!" << LL_ENDL;
- return LL_MESSAGE_ERROR;
- }
-
- char *bnamep = (char *)blockname + blocknum;
- char *vnamep = (char *)varname;
-
- LLMsgData::msg_blk_data_map_t::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
-
- if (iter == mCurrentRMessageData->mMemberBlocks.end())
- { // don't crash
- LL_INFOS() << "Block " << bnamep << " not in message "
- << mCurrentRMessageData->mName << LL_ENDL;
- return LL_BLOCK_NOT_IN_MESSAGE;
- }
-
- LLMsgBlkData* msg_data = iter->second;
- LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep];
-
- if (!vardata.getName())
- { // don't crash
- LL_INFOS() << "Variable " << vnamep << " not in message "
- << mCurrentRMessageData->mName << " block " << bnamep << LL_ENDL;
- return LL_VARIABLE_NOT_IN_BLOCK;
- }
-
- return vardata.getSize();
-}
-
-void LLTemplateMessageReader::getBinaryData(const char *blockname,
- const char *varname, void *datap,
- S32 size, S32 blocknum,
- S32 max_size)
-{
- getData(blockname, varname, datap, size, blocknum, max_size);
-}
-
-void LLTemplateMessageReader::getS8(const char *block, const char *var,
- S8 &u, S32 blocknum)
-{
- getData(block, var, &u, sizeof(S8), blocknum);
-}
-
-void LLTemplateMessageReader::getU8(const char *block, const char *var,
- U8 &u, S32 blocknum)
-{
- getData(block, var, &u, sizeof(U8), blocknum);
-}
-
-void LLTemplateMessageReader::getBOOL(const char *block, const char *var,
- bool &b, S32 blocknum )
-{
- U8 value;
- getData(block, var, &value, sizeof(U8), blocknum);
- b = (bool)value;
-}
-
-void LLTemplateMessageReader::getS16(const char *block, const char *var,
- S16 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(S16), blocknum);
-}
-
-void LLTemplateMessageReader::getU16(const char *block, const char *var,
- U16 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(U16), blocknum);
-}
-
-void LLTemplateMessageReader::getS32(const char *block, const char *var,
- S32 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(S32), blocknum);
-}
-
-void LLTemplateMessageReader::getU32(const char *block, const char *var,
- U32 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(U32), blocknum);
-}
-
-void LLTemplateMessageReader::getU64(const char *block, const char *var,
- U64 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(U64), blocknum);
-}
-
-void LLTemplateMessageReader::getF32(const char *block, const char *var,
- F32 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(F32), blocknum);
-
- if( !llfinite( d ) )
- {
- LL_WARNS() << "non-finite in getF32Fast " << block << " " << var
- << LL_ENDL;
- d = 0;
- }
-}
-
-void LLTemplateMessageReader::getF64(const char *block, const char *var,
- F64 &d, S32 blocknum)
-{
- getData(block, var, &d, sizeof(F64), blocknum);
-
- if( !llfinite( d ) )
- {
- LL_WARNS() << "non-finite in getF64Fast " << block << " " << var
- << LL_ENDL;
- d = 0;
- }
-}
-
-void LLTemplateMessageReader::getVector3(const char *block, const char *var,
- LLVector3 &v, S32 blocknum )
-{
- getData(block, var, &v.mV[0], sizeof(v.mV), blocknum);
-
- if( !v.isFinite() )
- {
- LL_WARNS() << "non-finite in getVector3Fast " << block << " "
- << var << LL_ENDL;
- v.zeroVec();
- }
-}
-
-void LLTemplateMessageReader::getVector4(const char *block, const char *var,
- LLVector4 &v, S32 blocknum)
-{
- getData(block, var, &v.mV[0], sizeof(v.mV), blocknum);
-
- if( !v.isFinite() )
- {
- LL_WARNS() << "non-finite in getVector4Fast " << block << " "
- << var << LL_ENDL;
- v.zeroVec();
- }
-}
-
-void LLTemplateMessageReader::getVector3d(const char *block, const char *var,
- LLVector3d &v, S32 blocknum )
-{
- getData(block, var, &v.mdV[0], sizeof(v.mdV), blocknum);
-
- if( !v.isFinite() )
- {
- LL_WARNS() << "non-finite in getVector3dFast " << block << " "
- << var << LL_ENDL;
- v.zeroVec();
- }
-
-}
-
-void LLTemplateMessageReader::getQuat(const char *block, const char *var,
- LLQuaternion &q, S32 blocknum)
-{
- LLVector3 vec;
- getData(block, var, &vec.mV[0], sizeof(vec.mV), blocknum);
- if( vec.isFinite() )
- {
- q.unpackFromVector3( vec );
- }
- else
- {
- LL_WARNS() << "non-finite in getQuatFast " << block << " " << var
- << LL_ENDL;
- q.loadIdentity();
- }
-}
-
-void LLTemplateMessageReader::getUUID(const char *block, const char *var,
- LLUUID &u, S32 blocknum)
-{
- getData(block, var, &u.mData[0], sizeof(u.mData), blocknum);
-}
-
-inline void LLTemplateMessageReader::getIPAddr(const char *block, const char *var, U32 &u, S32 blocknum)
-{
- getData(block, var, &u, sizeof(U32), blocknum);
-}
-
-inline void LLTemplateMessageReader::getIPPort(const char *block, const char *var, U16 &u, S32 blocknum)
-{
- getData(block, var, &u, sizeof(U16), blocknum);
- u = ntohs(u);
-}
-
-inline void LLTemplateMessageReader::getString(const char *block, const char *var, S32 buffer_size, char *s, S32 blocknum )
-{
- s[0] = '\0';
- getData(block, var, s, 0, blocknum, buffer_size);
- s[buffer_size - 1] = '\0';
-}
-
-inline void LLTemplateMessageReader::getString(const char *block, const char *var, std::string& outstr, S32 blocknum )
-{
- char s[MTUBYTES + 1]= {0}; // every element is initialized with 0
- getData(block, var, s, 0, blocknum, MTUBYTES);
- s[MTUBYTES] = '\0';
- outstr = s;
-}
-
-//virtual
-S32 LLTemplateMessageReader::getMessageSize() const
-{
- return mReceiveSize;
-}
-
-// Returns template for the message contained in buffer
-bool LLTemplateMessageReader::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)
- {
- LL_WARNS() << "No message waiting for decode!" << LL_ENDL;
- 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)
- {
- LL_WARNS() << "Packet with unusable length received (too short): "
- << buffer_size << LL_ENDL;
- return(false);
- }
-
- LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers,num);
- if (temp)
- {
- *msg_template = temp;
- }
- else
- {
- // MAINT-7482 - make viewer more tolerant of unknown messages.
- LL_WARNS_ONCE() << "Message #" << std::hex << num << std::dec
- << " received but not registered!" << LL_ENDL;
- //gMessageSystem->callExceptionFunc(MX_UNREGISTERED_MESSAGE);
- return(false);
- }
-
- return(true);
-}
-
-void LLTemplateMessageReader::logRanOffEndOfPacket( const LLHost& host, const S32 where, const S32 wanted )
-{
- // we've run off the end of the packet!
- LL_WARNS() << "Ran off end of packet " << mCurrentRMessageTemplate->mName
-// << " with id " << mCurrentRecvPacketID
- << " from " << host
- << " trying to read " << wanted
- << " bytes at position " << where
- << " going past packet end at " << mReceiveSize
- << LL_ENDL;
- if(gMessageSystem->mVerboseLog)
- {
- LL_INFOS() << "MSG: -> " << host << "\tREAD PAST END:\t"
-// << mCurrentRecvPacketID << " "
- << getMessageName() << LL_ENDL;
- }
- gMessageSystem->callExceptionFunc(MX_RAN_OFF_END_OF_PACKET);
-}
-
-static LLTrace::BlockTimerStatHandle FTM_PROCESS_MESSAGES("Process Messages");
-
-// decode a given message
-bool LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender )
-{
- LL_RECORD_BLOCK_TIME(FTM_PROCESS_MESSAGES);
-
- llassert( mReceiveSize >= 0 );
- llassert( mCurrentRMessageTemplate);
- llassert( !mCurrentRMessageData );
- delete mCurrentRMessageData; // just to make sure
-
- // The offset tells us how may bytes to skip after the end of the
- // message name.
- U8 offset = buffer[PHL_OFFSET];
- S32 decode_pos = LL_PACKET_ID_SIZE + (S32)(mCurrentRMessageTemplate->mFrequency) + offset;
-
- // create base working data set
- mCurrentRMessageData = new LLMsgData(mCurrentRMessageTemplate->mName);
-
- // loop through the template building the data structure as we go
- LLMessageTemplate::message_block_map_t::const_iterator iter;
- for(iter = mCurrentRMessageTemplate->mMemberBlocks.begin();
- iter != mCurrentRMessageTemplate->mMemberBlocks.end();
- ++iter)
- {
- LLMessageBlock* mbci = *iter;
- 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)
- {
- // commented out - hetgrid says that missing variable blocks
- // at end of message are legal
- // logRanOffEndOfPacket(sender, decode_pos, 1);
-
- // default to 0 repeats
- repeat_number = 0;
- }
- else
- {
- repeat_number = buffer[decode_pos];
- decode_pos++;
- }
- }
- else
- {
- LL_ERRS() << "Unknown block type" << LL_ENDL;
- 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::const_iterator iter =
- mbci->mMemberVariables.begin();
- iter != mbci->mMemberVariables.end(); iter++)
- {
- const LLMessageVariable& mvci = **iter;
-
- // 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, decode_pos, data_size);
-
- // default to 0 length variable blocks
- tsize = 0;
- }
- else
- {
- switch(data_size)
- {
- case 1:
- htolememcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1);
- tsize = tsizeb;
- break;
- case 2:
- htolememcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2);
- tsize = tsizeh;
- break;
- case 4:
- htolememcpy(&tsize, &buffer[decode_pos], MVT_U32, 4);
- break;
- default:
- LL_ERRS() << "Attempting to read variable field with unknown size of " << data_size << LL_ENDL;
- break;
- }
- }
- decode_pos += data_size;
-
- 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, decode_pos, mvci.getSize());
-
- // default to 0s.
- U32 size = mvci.getSize();
- std::vector<U8> data(size, 0);
- cur_data_block->addData(mvci.getName(), &(data[0]),
- size, mvci.getType());
- }
- else
- {
- cur_data_block->addData(mvci.getName(),
- &buffer[decode_pos],
- mvci.getSize(),
- mvci.getType());
- }
- decode_pos += mvci.getSize();
- }
- }
- }
- }
-
- if (mCurrentRMessageData->mMemberBlocks.empty()
- && !mCurrentRMessageTemplate->mMemberBlocks.empty())
- {
- LL_DEBUGS() << "Empty message '" << mCurrentRMessageTemplate->mName << "' (no blocks)" << LL_ENDL;
- return false;
- }
-
- {
- static LLTimer decode_timer;
-
- if(LLMessageReader::getTimeDecodes() || gMessageSystem->getTimingCallback())
- {
- decode_timer.reset();
- }
-
- if( !mCurrentRMessageTemplate->callHandlerFunc(gMessageSystem) )
- {
- LL_WARNS() << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << LL_ENDL;
- }
-
- if(LLMessageReader::getTimeDecodes() || gMessageSystem->getTimingCallback())
- {
- F32 decode_time = decode_timer.getElapsedTimeF32();
-
- if (gMessageSystem->getTimingCallback())
- {
- (gMessageSystem->getTimingCallback())(mCurrentRMessageTemplate->mName,
- decode_time,
- gMessageSystem->getTimingCallbackData());
- }
-
- if (LLMessageReader::getTimeDecodes())
- {
- mCurrentRMessageTemplate->mDecodeTimeThisFrame += decode_time;
-
- mCurrentRMessageTemplate->mTotalDecoded++;
- mCurrentRMessageTemplate->mTotalDecodeTime += decode_time;
-
- if( mCurrentRMessageTemplate->mMaxDecodeTimePerMsg < decode_time )
- {
- mCurrentRMessageTemplate->mMaxDecodeTimePerMsg = decode_time;
- }
-
-
- if(decode_time > LLMessageReader::getTimeDecodesSpamThreshold())
- {
- LL_DEBUGS() << "--------- Message " << mCurrentRMessageTemplate->mName << " decode took " << decode_time << " seconds. (" <<
- mCurrentRMessageTemplate->mMaxDecodeTimePerMsg << " max, " <<
- (mCurrentRMessageTemplate->mTotalDecodeTime / mCurrentRMessageTemplate->mTotalDecoded) << " avg)" << LL_ENDL;
- }
- }
- }
- }
- return true;
-}
-
-bool LLTemplateMessageReader::validateMessage(const U8* buffer,
- S32 buffer_size,
- const LLHost& sender,
- bool trusted)
-{
- mReceiveSize = buffer_size;
- bool valid = decodeTemplate(buffer, buffer_size, &mCurrentRMessageTemplate );
- if(valid)
- {
- mCurrentRMessageTemplate->mReceiveCount++;
- //LL_DEBUGS() << "MessageRecvd:"
- // << mCurrentRMessageTemplate->mName
- // << " from " << sender << LL_ENDL;
- }
-
- if (valid && isBanned(trusted))
- {
- LL_WARNS("Messaging") << "LLMessageSystem::checkMessages "
- << "received banned message "
- << getMessageName()
- << " from "
- << ((trusted) ? "trusted " : "untrusted ")
- << sender << LL_ENDL;
- valid = false;
- }
-
- if(valid && isUdpBanned())
- {
- LL_WARNS() << "Received UDP black listed message "
- << getMessageName()
- << " from " << sender << LL_ENDL;
- valid = false;
- }
- return valid;
-}
-
-bool LLTemplateMessageReader::readMessage(const U8* buffer,
- const LLHost& sender)
-{
- return decodeData(buffer, sender);
-}
-
-//virtual
-const char* LLTemplateMessageReader::getMessageName() const
-{
- if (!mCurrentRMessageTemplate)
- {
- // no message currently being read
- return "";
- }
- return mCurrentRMessageTemplate->mName;
-}
-
-//virtual
-bool LLTemplateMessageReader::isTrusted() const
-{
- return mCurrentRMessageTemplate->getTrust() == MT_TRUST;
-}
-
-bool LLTemplateMessageReader::isBanned(bool trustedSource) const
-{
- return mCurrentRMessageTemplate->isBanned(trustedSource);
-}
-
-bool LLTemplateMessageReader::isUdpBanned() const
-{
- return mCurrentRMessageTemplate->isUdpBanned();
-}
-
-//virtual
-void LLTemplateMessageReader::copyToBuilder(LLMessageBuilder& builder) const
-{
- if(NULL == mCurrentRMessageTemplate)
- {
- return;
- }
- builder.copyFromMessageData(*mCurrentRMessageData);
-}
+/** + * @file lltemplatemessagereader.cpp + * @brief LLTemplateMessageReader class implementation. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "lltemplatemessagereader.h" + +#include "llfasttimer.h" +#include "llmessagebuilder.h" +#include "llmessagetemplate.h" +#include "llmath.h" +#include "llquaternion.h" +#include "message.h" +#include "u64.h" +#include "v3dmath.h" +#include "v3math.h" +#include "v4math.h" + +LLTemplateMessageReader::LLTemplateMessageReader(message_template_number_map_t& + number_template_map) : + mReceiveSize(0), + mCurrentRMessageTemplate(NULL), + mCurrentRMessageData(NULL), + mMessageNumbers(number_template_map) +{ +} + +//virtual +LLTemplateMessageReader::~LLTemplateMessageReader() +{ + delete mCurrentRMessageData; + mCurrentRMessageData = NULL; +} + +//virtual +void LLTemplateMessageReader::clearMessage() +{ + mReceiveSize = -1; + mCurrentRMessageTemplate = NULL; + delete mCurrentRMessageData; + mCurrentRMessageData = NULL; +} + +void LLTemplateMessageReader::getData(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) + { + LL_ERRS() << "No message waiting for decode 2!" << LL_ENDL; + return; + } + + if (!mCurrentRMessageData) + { + LL_ERRS() << "Invalid mCurrentMessageData in getData!" << LL_ENDL; + 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::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); + + if (iter == mCurrentRMessageData->mMemberBlocks.end()) + { + LL_ERRS() << "Block " << blockname << " #" << blocknum + << " not in message " << mCurrentRMessageData->mName << LL_ENDL; + return; + } + + LLMsgBlkData *msg_block_data = iter->second; + LLMsgBlkData::msg_var_data_map_t &var_data_map = msg_block_data->mMemberVarData; + + if (var_data_map.find(vnamep) == var_data_map.end()) + { + LL_ERRS() << "Variable "<< vnamep << " not in message " + << mCurrentRMessageData->mName<< " block " << bnamep << LL_ENDL; + return; + } + + LLMsgVarData& vardata = msg_block_data->mMemberVarData[vnamep]; + + if (size && size != vardata.getSize()) + { + LL_ERRS() << "Msg " << mCurrentRMessageData->mName + << " variable " << vnamep + << " is size " << vardata.getSize() + << " but copying into buffer of size " << size + << LL_ENDL; + 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 + { + LL_WARNS() << "Msg " << mCurrentRMessageData->mName + << " variable " << vnamep + << " is size " << vardata.getSize() + << " but truncated to max size of " << max_size + << LL_ENDL; + + memcpy(datap, vardata.getData(), max_size); + } +} + +S32 LLTemplateMessageReader::getNumberOfBlocks(const char *blockname) +{ + // is there a message ready to go? + if (mReceiveSize == -1) + { + LL_ERRS() << "No message waiting for decode 3!" << LL_ENDL; + return -1; + } + + if (!mCurrentRMessageData) + { + LL_ERRS() << "Invalid mCurrentRMessageData in getData!" << LL_ENDL; + return -1; + } + + char *bnamep = (char *)blockname; + + LLMsgData::msg_blk_data_map_t::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); + + if (iter == mCurrentRMessageData->mMemberBlocks.end()) + { + return 0; + } + + return (iter->second)->mBlockNumber; +} + +S32 LLTemplateMessageReader::getSize(const char *blockname, const char *varname) +{ + // is there a message ready to go? + if (mReceiveSize == -1) + { // This is a serious error - crash + LL_ERRS() << "No message waiting for decode 4!" << LL_ENDL; + return LL_MESSAGE_ERROR; + } + + if (!mCurrentRMessageData) + { // This is a serious error - crash + LL_ERRS() << "Invalid mCurrentRMessageData in getData!" << LL_ENDL; + return LL_MESSAGE_ERROR; + } + + char *bnamep = (char *)blockname; + + LLMsgData::msg_blk_data_map_t::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); + + if (iter == mCurrentRMessageData->mMemberBlocks.end()) + { // don't crash + LL_INFOS() << "Block " << bnamep << " not in message " + << mCurrentRMessageData->mName << LL_ENDL; + return LL_BLOCK_NOT_IN_MESSAGE; + } + + char *vnamep = (char *)varname; + + LLMsgBlkData* msg_data = iter->second; + LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep]; + + if (!vardata.getName()) + { // don't crash + LL_INFOS() << "Variable " << varname << " not in message " + << mCurrentRMessageData->mName << " block " << bnamep << LL_ENDL; + return LL_VARIABLE_NOT_IN_BLOCK; + } + + if (mCurrentRMessageTemplate->mMemberBlocks[bnamep]->mType != MBT_SINGLE) + { // This is a serious error - crash + LL_ERRS() << "Block " << bnamep << " isn't type MBT_SINGLE," + " use getSize with blocknum argument!" << LL_ENDL; + return LL_MESSAGE_ERROR; + } + + return vardata.getSize(); +} + +S32 LLTemplateMessageReader::getSize(const char *blockname, S32 blocknum, const char *varname) +{ + // is there a message ready to go? + if (mReceiveSize == -1) + { // This is a serious error - crash + LL_ERRS() << "No message waiting for decode 5!" << LL_ENDL; + return LL_MESSAGE_ERROR; + } + + if (!mCurrentRMessageData) + { // This is a serious error - crash + LL_ERRS() << "Invalid mCurrentRMessageData in getData!" << LL_ENDL; + return LL_MESSAGE_ERROR; + } + + char *bnamep = (char *)blockname + blocknum; + char *vnamep = (char *)varname; + + LLMsgData::msg_blk_data_map_t::const_iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); + + if (iter == mCurrentRMessageData->mMemberBlocks.end()) + { // don't crash + LL_INFOS() << "Block " << bnamep << " not in message " + << mCurrentRMessageData->mName << LL_ENDL; + return LL_BLOCK_NOT_IN_MESSAGE; + } + + LLMsgBlkData* msg_data = iter->second; + LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep]; + + if (!vardata.getName()) + { // don't crash + LL_INFOS() << "Variable " << vnamep << " not in message " + << mCurrentRMessageData->mName << " block " << bnamep << LL_ENDL; + return LL_VARIABLE_NOT_IN_BLOCK; + } + + return vardata.getSize(); +} + +void LLTemplateMessageReader::getBinaryData(const char *blockname, + const char *varname, void *datap, + S32 size, S32 blocknum, + S32 max_size) +{ + getData(blockname, varname, datap, size, blocknum, max_size); +} + +void LLTemplateMessageReader::getS8(const char *block, const char *var, + S8 &u, S32 blocknum) +{ + getData(block, var, &u, sizeof(S8), blocknum); +} + +void LLTemplateMessageReader::getU8(const char *block, const char *var, + U8 &u, S32 blocknum) +{ + getData(block, var, &u, sizeof(U8), blocknum); +} + +void LLTemplateMessageReader::getBOOL(const char *block, const char *var, + bool &b, S32 blocknum ) +{ + U8 value; + getData(block, var, &value, sizeof(U8), blocknum); + b = (bool)value; +} + +void LLTemplateMessageReader::getS16(const char *block, const char *var, + S16 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(S16), blocknum); +} + +void LLTemplateMessageReader::getU16(const char *block, const char *var, + U16 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(U16), blocknum); +} + +void LLTemplateMessageReader::getS32(const char *block, const char *var, + S32 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(S32), blocknum); +} + +void LLTemplateMessageReader::getU32(const char *block, const char *var, + U32 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(U32), blocknum); +} + +void LLTemplateMessageReader::getU64(const char *block, const char *var, + U64 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(U64), blocknum); +} + +void LLTemplateMessageReader::getF32(const char *block, const char *var, + F32 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(F32), blocknum); + + if( !llfinite( d ) ) + { + LL_WARNS() << "non-finite in getF32Fast " << block << " " << var + << LL_ENDL; + d = 0; + } +} + +void LLTemplateMessageReader::getF64(const char *block, const char *var, + F64 &d, S32 blocknum) +{ + getData(block, var, &d, sizeof(F64), blocknum); + + if( !llfinite( d ) ) + { + LL_WARNS() << "non-finite in getF64Fast " << block << " " << var + << LL_ENDL; + d = 0; + } +} + +void LLTemplateMessageReader::getVector3(const char *block, const char *var, + LLVector3 &v, S32 blocknum ) +{ + getData(block, var, &v.mV[0], sizeof(v.mV), blocknum); + + if( !v.isFinite() ) + { + LL_WARNS() << "non-finite in getVector3Fast " << block << " " + << var << LL_ENDL; + v.zeroVec(); + } +} + +void LLTemplateMessageReader::getVector4(const char *block, const char *var, + LLVector4 &v, S32 blocknum) +{ + getData(block, var, &v.mV[0], sizeof(v.mV), blocknum); + + if( !v.isFinite() ) + { + LL_WARNS() << "non-finite in getVector4Fast " << block << " " + << var << LL_ENDL; + v.zeroVec(); + } +} + +void LLTemplateMessageReader::getVector3d(const char *block, const char *var, + LLVector3d &v, S32 blocknum ) +{ + getData(block, var, &v.mdV[0], sizeof(v.mdV), blocknum); + + if( !v.isFinite() ) + { + LL_WARNS() << "non-finite in getVector3dFast " << block << " " + << var << LL_ENDL; + v.zeroVec(); + } + +} + +void LLTemplateMessageReader::getQuat(const char *block, const char *var, + LLQuaternion &q, S32 blocknum) +{ + LLVector3 vec; + getData(block, var, &vec.mV[0], sizeof(vec.mV), blocknum); + if( vec.isFinite() ) + { + q.unpackFromVector3( vec ); + } + else + { + LL_WARNS() << "non-finite in getQuatFast " << block << " " << var + << LL_ENDL; + q.loadIdentity(); + } +} + +void LLTemplateMessageReader::getUUID(const char *block, const char *var, + LLUUID &u, S32 blocknum) +{ + getData(block, var, &u.mData[0], sizeof(u.mData), blocknum); +} + +inline void LLTemplateMessageReader::getIPAddr(const char *block, const char *var, U32 &u, S32 blocknum) +{ + getData(block, var, &u, sizeof(U32), blocknum); +} + +inline void LLTemplateMessageReader::getIPPort(const char *block, const char *var, U16 &u, S32 blocknum) +{ + getData(block, var, &u, sizeof(U16), blocknum); + u = ntohs(u); +} + +inline void LLTemplateMessageReader::getString(const char *block, const char *var, S32 buffer_size, char *s, S32 blocknum ) +{ + s[0] = '\0'; + getData(block, var, s, 0, blocknum, buffer_size); + s[buffer_size - 1] = '\0'; +} + +inline void LLTemplateMessageReader::getString(const char *block, const char *var, std::string& outstr, S32 blocknum ) +{ + char s[MTUBYTES + 1]= {0}; // every element is initialized with 0 + getData(block, var, s, 0, blocknum, MTUBYTES); + s[MTUBYTES] = '\0'; + outstr = s; +} + +//virtual +S32 LLTemplateMessageReader::getMessageSize() const +{ + return mReceiveSize; +} + +// Returns template for the message contained in buffer +bool LLTemplateMessageReader::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) + { + LL_WARNS() << "No message waiting for decode!" << LL_ENDL; + 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) + { + LL_WARNS() << "Packet with unusable length received (too short): " + << buffer_size << LL_ENDL; + return(false); + } + + LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers,num); + if (temp) + { + *msg_template = temp; + } + else + { + // MAINT-7482 - make viewer more tolerant of unknown messages. + LL_WARNS_ONCE() << "Message #" << std::hex << num << std::dec + << " received but not registered!" << LL_ENDL; + //gMessageSystem->callExceptionFunc(MX_UNREGISTERED_MESSAGE); + return(false); + } + + return(true); +} + +void LLTemplateMessageReader::logRanOffEndOfPacket( const LLHost& host, const S32 where, const S32 wanted ) +{ + // we've run off the end of the packet! + LL_WARNS() << "Ran off end of packet " << mCurrentRMessageTemplate->mName +// << " with id " << mCurrentRecvPacketID + << " from " << host + << " trying to read " << wanted + << " bytes at position " << where + << " going past packet end at " << mReceiveSize + << LL_ENDL; + if(gMessageSystem->mVerboseLog) + { + LL_INFOS() << "MSG: -> " << host << "\tREAD PAST END:\t" +// << mCurrentRecvPacketID << " " + << getMessageName() << LL_ENDL; + } + gMessageSystem->callExceptionFunc(MX_RAN_OFF_END_OF_PACKET); +} + +static LLTrace::BlockTimerStatHandle FTM_PROCESS_MESSAGES("Process Messages"); + +// decode a given message +bool LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender ) +{ + LL_RECORD_BLOCK_TIME(FTM_PROCESS_MESSAGES); + + llassert( mReceiveSize >= 0 ); + llassert( mCurrentRMessageTemplate); + llassert( !mCurrentRMessageData ); + delete mCurrentRMessageData; // just to make sure + + // The offset tells us how may bytes to skip after the end of the + // message name. + U8 offset = buffer[PHL_OFFSET]; + S32 decode_pos = LL_PACKET_ID_SIZE + (S32)(mCurrentRMessageTemplate->mFrequency) + offset; + + // create base working data set + mCurrentRMessageData = new LLMsgData(mCurrentRMessageTemplate->mName); + + // loop through the template building the data structure as we go + LLMessageTemplate::message_block_map_t::const_iterator iter; + for(iter = mCurrentRMessageTemplate->mMemberBlocks.begin(); + iter != mCurrentRMessageTemplate->mMemberBlocks.end(); + ++iter) + { + LLMessageBlock* mbci = *iter; + 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) + { + // commented out - hetgrid says that missing variable blocks + // at end of message are legal + // logRanOffEndOfPacket(sender, decode_pos, 1); + + // default to 0 repeats + repeat_number = 0; + } + else + { + repeat_number = buffer[decode_pos]; + decode_pos++; + } + } + else + { + LL_ERRS() << "Unknown block type" << LL_ENDL; + 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::const_iterator iter = + mbci->mMemberVariables.begin(); + iter != mbci->mMemberVariables.end(); iter++) + { + const LLMessageVariable& mvci = **iter; + + // 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, decode_pos, data_size); + + // default to 0 length variable blocks + tsize = 0; + } + else + { + switch(data_size) + { + case 1: + htolememcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1); + tsize = tsizeb; + break; + case 2: + htolememcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2); + tsize = tsizeh; + break; + case 4: + htolememcpy(&tsize, &buffer[decode_pos], MVT_U32, 4); + break; + default: + LL_ERRS() << "Attempting to read variable field with unknown size of " << data_size << LL_ENDL; + break; + } + } + decode_pos += data_size; + + 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, decode_pos, mvci.getSize()); + + // default to 0s. + U32 size = mvci.getSize(); + std::vector<U8> data(size, 0); + cur_data_block->addData(mvci.getName(), &(data[0]), + size, mvci.getType()); + } + else + { + cur_data_block->addData(mvci.getName(), + &buffer[decode_pos], + mvci.getSize(), + mvci.getType()); + } + decode_pos += mvci.getSize(); + } + } + } + } + + if (mCurrentRMessageData->mMemberBlocks.empty() + && !mCurrentRMessageTemplate->mMemberBlocks.empty()) + { + LL_DEBUGS() << "Empty message '" << mCurrentRMessageTemplate->mName << "' (no blocks)" << LL_ENDL; + return false; + } + + { + static LLTimer decode_timer; + + if(LLMessageReader::getTimeDecodes() || gMessageSystem->getTimingCallback()) + { + decode_timer.reset(); + } + + if( !mCurrentRMessageTemplate->callHandlerFunc(gMessageSystem) ) + { + LL_WARNS() << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << LL_ENDL; + } + + if(LLMessageReader::getTimeDecodes() || gMessageSystem->getTimingCallback()) + { + F32 decode_time = decode_timer.getElapsedTimeF32(); + + if (gMessageSystem->getTimingCallback()) + { + (gMessageSystem->getTimingCallback())(mCurrentRMessageTemplate->mName, + decode_time, + gMessageSystem->getTimingCallbackData()); + } + + if (LLMessageReader::getTimeDecodes()) + { + mCurrentRMessageTemplate->mDecodeTimeThisFrame += decode_time; + + mCurrentRMessageTemplate->mTotalDecoded++; + mCurrentRMessageTemplate->mTotalDecodeTime += decode_time; + + if( mCurrentRMessageTemplate->mMaxDecodeTimePerMsg < decode_time ) + { + mCurrentRMessageTemplate->mMaxDecodeTimePerMsg = decode_time; + } + + + if(decode_time > LLMessageReader::getTimeDecodesSpamThreshold()) + { + LL_DEBUGS() << "--------- Message " << mCurrentRMessageTemplate->mName << " decode took " << decode_time << " seconds. (" << + mCurrentRMessageTemplate->mMaxDecodeTimePerMsg << " max, " << + (mCurrentRMessageTemplate->mTotalDecodeTime / mCurrentRMessageTemplate->mTotalDecoded) << " avg)" << LL_ENDL; + } + } + } + } + return true; +} + +bool LLTemplateMessageReader::validateMessage(const U8* buffer, + S32 buffer_size, + const LLHost& sender, + bool trusted) +{ + mReceiveSize = buffer_size; + bool valid = decodeTemplate(buffer, buffer_size, &mCurrentRMessageTemplate ); + if(valid) + { + mCurrentRMessageTemplate->mReceiveCount++; + //LL_DEBUGS() << "MessageRecvd:" + // << mCurrentRMessageTemplate->mName + // << " from " << sender << LL_ENDL; + } + + if (valid && isBanned(trusted)) + { + LL_WARNS("Messaging") << "LLMessageSystem::checkMessages " + << "received banned message " + << getMessageName() + << " from " + << ((trusted) ? "trusted " : "untrusted ") + << sender << LL_ENDL; + valid = false; + } + + if(valid && isUdpBanned()) + { + LL_WARNS() << "Received UDP black listed message " + << getMessageName() + << " from " << sender << LL_ENDL; + valid = false; + } + return valid; +} + +bool LLTemplateMessageReader::readMessage(const U8* buffer, + const LLHost& sender) +{ + return decodeData(buffer, sender); +} + +//virtual +const char* LLTemplateMessageReader::getMessageName() const +{ + if (!mCurrentRMessageTemplate) + { + // no message currently being read + return ""; + } + return mCurrentRMessageTemplate->mName; +} + +//virtual +bool LLTemplateMessageReader::isTrusted() const +{ + return mCurrentRMessageTemplate->getTrust() == MT_TRUST; +} + +bool LLTemplateMessageReader::isBanned(bool trustedSource) const +{ + return mCurrentRMessageTemplate->isBanned(trustedSource); +} + +bool LLTemplateMessageReader::isUdpBanned() const +{ + return mCurrentRMessageTemplate->isUdpBanned(); +} + +//virtual +void LLTemplateMessageReader::copyToBuilder(LLMessageBuilder& builder) const +{ + if(NULL == mCurrentRMessageTemplate) + { + return; + } + builder.copyFromMessageData(*mCurrentRMessageData); +} diff --git a/indra/llmessage/lltemplatemessagereader.h b/indra/llmessage/lltemplatemessagereader.h index 0b06f6d23a..6f1977cf83 100644 --- a/indra/llmessage/lltemplatemessagereader.h +++ b/indra/llmessage/lltemplatemessagereader.h @@ -1,127 +1,127 @@ -/**
- * @file lltemplatemessagereader.h
- * @brief Declaration of LLTemplateMessageReader class.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTEMPLATEMESSAGEREADER_H
-#define LL_LLTEMPLATEMESSAGEREADER_H
-
-#include "llmessagereader.h"
-
-#include <map>
-
-class LLMessageTemplate;
-class LLMsgData;
-
-class LLTemplateMessageReader : public LLMessageReader
-{
-public:
-
- typedef std::map<U32, LLMessageTemplate*> message_template_number_map_t;
-
- LLTemplateMessageReader(message_template_number_map_t&);
- virtual ~LLTemplateMessageReader();
-
- /** All get* methods expect pointers to canonical strings. */
- virtual void getBinaryData(const char *blockname, const char *varname,
- void *datap, S32 size, S32 blocknum = 0,
- S32 max_size = S32_MAX);
- virtual void getBOOL(const char *block, const char *var, bool &data,
- S32 blocknum = 0);
- virtual void getS8(const char *block, const char *var, S8 &data,
- S32 blocknum = 0);
- virtual void getU8(const char *block, const char *var, U8 &data,
- S32 blocknum = 0);
- virtual void getS16(const char *block, const char *var, S16 &data,
- S32 blocknum = 0);
- virtual void getU16(const char *block, const char *var, U16 &data,
- S32 blocknum = 0);
- virtual void getS32(const char *block, const char *var, S32 &data,
- S32 blocknum = 0);
- virtual void getF32(const char *block, const char *var, F32 &data,
- S32 blocknum = 0);
- virtual void getU32(const char *block, const char *var, U32 &data,
- S32 blocknum = 0);
- virtual void getU64(const char *block, const char *var, U64 &data,
- S32 blocknum = 0);
- virtual void getF64(const char *block, const char *var, F64 &data,
- S32 blocknum = 0);
- virtual void getVector3(const char *block, const char *var,
- LLVector3 &vec, S32 blocknum = 0);
- virtual void getVector4(const char *block, const char *var,
- LLVector4 &vec, S32 blocknum = 0);
- virtual void getVector3d(const char *block, const char *var,
- LLVector3d &vec, S32 blocknum = 0);
- virtual void getQuat(const char *block, const char *var, LLQuaternion &q,
- S32 blocknum = 0);
- virtual void getUUID(const char *block, const char *var, LLUUID &uuid,
- S32 blocknum = 0);
- virtual void getIPAddr(const char *block, const char *var, U32 &ip,
- S32 blocknum = 0);
- virtual void getIPPort(const char *block, const char *var, U16 &port,
- S32 blocknum = 0);
- virtual void getString(const char *block, const char *var,
- S32 buffer_size, char *buffer, S32 blocknum = 0);
- virtual void getString(const char *block, const char *var, std::string& outstr,
- S32 blocknum = 0);
-
- virtual S32 getNumberOfBlocks(const char *blockname);
- virtual S32 getSize(const char *blockname, const char *varname);
- virtual S32 getSize(const char *blockname, S32 blocknum,
- const char *varname);
-
- virtual void clearMessage();
-
- virtual const char* getMessageName() const;
- virtual S32 getMessageSize() const;
-
- virtual void copyToBuilder(LLMessageBuilder&) const;
-
- bool validateMessage(const U8* buffer, S32 buffer_size,
- const LLHost& sender, bool trusted = false);
- bool readMessage(const U8* buffer, const LLHost& sender);
-
- bool isTrusted() const;
- bool isBanned(bool trusted_source) const;
- bool isUdpBanned() const;
-
-private:
-
- void getData(const char *blockname, const char *varname, void *datap,
- S32 size = 0, S32 blocknum = 0, S32 max_size = S32_MAX);
-
- bool decodeTemplate(const U8* buffer, S32 buffer_size, // inputs
- LLMessageTemplate** msg_template ); // outputs
-
- void logRanOffEndOfPacket( const LLHost& host, const S32 where, const S32 wanted );
-
- bool decodeData(const U8* buffer, const LLHost& sender );
-
- S32 mReceiveSize;
- LLMessageTemplate* mCurrentRMessageTemplate;
- LLMsgData* mCurrentRMessageData;
- message_template_number_map_t& mMessageNumbers;
-};
-
-#endif // LL_LLTEMPLATEMESSAGEREADER_H
+/** + * @file lltemplatemessagereader.h + * @brief Declaration of LLTemplateMessageReader class. + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEMPLATEMESSAGEREADER_H +#define LL_LLTEMPLATEMESSAGEREADER_H + +#include "llmessagereader.h" + +#include <map> + +class LLMessageTemplate; +class LLMsgData; + +class LLTemplateMessageReader : public LLMessageReader +{ +public: + + typedef std::map<U32, LLMessageTemplate*> message_template_number_map_t; + + LLTemplateMessageReader(message_template_number_map_t&); + virtual ~LLTemplateMessageReader(); + + /** All get* methods expect pointers to canonical strings. */ + virtual void getBinaryData(const char *blockname, const char *varname, + void *datap, S32 size, S32 blocknum = 0, + S32 max_size = S32_MAX); + virtual void getBOOL(const char *block, const char *var, bool &data, + S32 blocknum = 0); + virtual void getS8(const char *block, const char *var, S8 &data, + S32 blocknum = 0); + virtual void getU8(const char *block, const char *var, U8 &data, + S32 blocknum = 0); + virtual void getS16(const char *block, const char *var, S16 &data, + S32 blocknum = 0); + virtual void getU16(const char *block, const char *var, U16 &data, + S32 blocknum = 0); + virtual void getS32(const char *block, const char *var, S32 &data, + S32 blocknum = 0); + virtual void getF32(const char *block, const char *var, F32 &data, + S32 blocknum = 0); + virtual void getU32(const char *block, const char *var, U32 &data, + S32 blocknum = 0); + virtual void getU64(const char *block, const char *var, U64 &data, + S32 blocknum = 0); + virtual void getF64(const char *block, const char *var, F64 &data, + S32 blocknum = 0); + virtual void getVector3(const char *block, const char *var, + LLVector3 &vec, S32 blocknum = 0); + virtual void getVector4(const char *block, const char *var, + LLVector4 &vec, S32 blocknum = 0); + virtual void getVector3d(const char *block, const char *var, + LLVector3d &vec, S32 blocknum = 0); + virtual void getQuat(const char *block, const char *var, LLQuaternion &q, + S32 blocknum = 0); + virtual void getUUID(const char *block, const char *var, LLUUID &uuid, + S32 blocknum = 0); + virtual void getIPAddr(const char *block, const char *var, U32 &ip, + S32 blocknum = 0); + virtual void getIPPort(const char *block, const char *var, U16 &port, + S32 blocknum = 0); + virtual void getString(const char *block, const char *var, + S32 buffer_size, char *buffer, S32 blocknum = 0); + virtual void getString(const char *block, const char *var, std::string& outstr, + S32 blocknum = 0); + + virtual S32 getNumberOfBlocks(const char *blockname); + virtual S32 getSize(const char *blockname, const char *varname); + virtual S32 getSize(const char *blockname, S32 blocknum, + const char *varname); + + virtual void clearMessage(); + + virtual const char* getMessageName() const; + virtual S32 getMessageSize() const; + + virtual void copyToBuilder(LLMessageBuilder&) const; + + bool validateMessage(const U8* buffer, S32 buffer_size, + const LLHost& sender, bool trusted = false); + bool readMessage(const U8* buffer, const LLHost& sender); + + bool isTrusted() const; + bool isBanned(bool trusted_source) const; + bool isUdpBanned() const; + +private: + + void getData(const char *blockname, const char *varname, void *datap, + S32 size = 0, S32 blocknum = 0, S32 max_size = S32_MAX); + + bool decodeTemplate(const U8* buffer, S32 buffer_size, // inputs + LLMessageTemplate** msg_template ); // outputs + + void logRanOffEndOfPacket( const LLHost& host, const S32 where, const S32 wanted ); + + bool decodeData(const U8* buffer, const LLHost& sender ); + + S32 mReceiveSize; + LLMessageTemplate* mCurrentRMessageTemplate; + LLMsgData* mCurrentRMessageData; + message_template_number_map_t& mMessageNumbers; +}; + +#endif // LL_LLTEMPLATEMESSAGEREADER_H diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp index a3f02204d0..5ce96fc551 100644 --- a/indra/llmessage/llthrottle.cpp +++ b/indra/llmessage/llthrottle.cpp @@ -1,577 +1,577 @@ -/**
- * @file llthrottle.cpp
- * @brief LLThrottle class used for network bandwidth control.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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
- F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime;
- return mAvailable + (mRate * elapsed_time.value());
-}
-
-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
- F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime;
- F32 amount_available = mAvailable + (mRate * elapsed_time.value());
-
- 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)
-{
- F32Seconds elapsed_time;
- F32 lookahead_amount;
- bool retval = true;
-
- lookahead_amount = mRate * mLookaheadSecs;
-
- F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds();
- elapsed_time = mt_sec - mLastSendTime;
- mLastSendTime = mt_sec;
-
- mAvailable += mRate * elapsed_time.value();
-
- 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()
-{
- F64Seconds 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;
-}
-
-// Return bits available in the channel
-S32 LLThrottleGroup::getAvailable(S32 throttle_cat)
-{
- S32 retval = 0;
-
- 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
- F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat];
- F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time.value());
-
- if (bits_available >= lookahead_bits)
- {
- retval = (S32) gThrottleMaximumBPS[throttle_cat];
- }
- else
- {
- retval = (S32) bits_available;
- }
-
- return retval;
-}
-
-
-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
- F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat];
- F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time.value());
-
- 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)
-{
- F32Seconds elapsed_time;
- F32 category_bps;
- F32 lookahead_bits;
- bool retval = true;
-
- category_bps = mCurrentBPS[throttle_cat];
- lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
-
- F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds();
- elapsed_time = mt_sec - mLastSendTime[throttle_cat];
- mLastSendTime[throttle_cat] = mt_sec;
- mBitsAvailable[throttle_cat] += category_bps * elapsed_time.value();
-
- 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 F32Seconds DYNAMIC_ADJUST_TIME(1.0f);
- 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;
-
- F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds();
-
- // Only dynamically adjust every few seconds
- if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME)
- {
- return false;
- }
- mDynamicAdjustTime = mt_sec;
-
- // 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;
- }
-
- // 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.value() * 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.value() * 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 (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.value();
-
- // 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;
- }
-
- //LL_INFOS() << i << " avail " << avail_bps << LL_ENDL;
-
- // 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;
- }
- }
-
- //LL_INFOS() << "Pool BPS: " << pool_bps << LL_ENDL;
- // 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);
- //LL_INFOS() << "Busy " << i << " gets " << pool_bps << LL_ENDL;
- 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.
- // *TODO: 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;
-}
+/** + * @file llthrottle.cpp + * @brief LLThrottle class used for network bandwidth control. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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 + F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime; + return mAvailable + (mRate * elapsed_time.value()); +} + +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 + F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime; + F32 amount_available = mAvailable + (mRate * elapsed_time.value()); + + 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) +{ + F32Seconds elapsed_time; + F32 lookahead_amount; + bool retval = true; + + lookahead_amount = mRate * mLookaheadSecs; + + F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds(); + elapsed_time = mt_sec - mLastSendTime; + mLastSendTime = mt_sec; + + mAvailable += mRate * elapsed_time.value(); + + 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() +{ + F64Seconds 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; +} + +// Return bits available in the channel +S32 LLThrottleGroup::getAvailable(S32 throttle_cat) +{ + S32 retval = 0; + + 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 + F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]; + F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time.value()); + + if (bits_available >= lookahead_bits) + { + retval = (S32) gThrottleMaximumBPS[throttle_cat]; + } + else + { + retval = (S32) bits_available; + } + + return retval; +} + + +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 + F32Seconds elapsed_time = LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]; + F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time.value()); + + 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) +{ + F32Seconds elapsed_time; + F32 category_bps; + F32 lookahead_bits; + bool retval = true; + + category_bps = mCurrentBPS[throttle_cat]; + lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME; + + F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds(); + elapsed_time = mt_sec - mLastSendTime[throttle_cat]; + mLastSendTime[throttle_cat] = mt_sec; + mBitsAvailable[throttle_cat] += category_bps * elapsed_time.value(); + + 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 F32Seconds DYNAMIC_ADJUST_TIME(1.0f); + 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; + + F64Seconds mt_sec = LLMessageSystem::getMessageTimeSeconds(); + + // Only dynamically adjust every few seconds + if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME) + { + return false; + } + mDynamicAdjustTime = mt_sec; + + // 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; + } + + // 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.value() * 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.value() * 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 (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.value(); + + // 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; + } + + //LL_INFOS() << i << " avail " << avail_bps << LL_ENDL; + + // 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; + } + } + + //LL_INFOS() << "Pool BPS: " << pool_bps << LL_ENDL; + // 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); + //LL_INFOS() << "Busy " << i << " gets " << pool_bps << LL_ENDL; + 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. + // *TODO: 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 index be497d9371..b8a8c9f1f7 100644 --- a/indra/llmessage/llthrottle.h +++ b/indra/llmessage/llthrottle.h @@ -1,101 +1,101 @@ -/**
- * @file llthrottle.h
- * @brief LLThrottle class used for network bandwidth control
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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
- F64Seconds 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
-
- S32 getAvailable(S32 throttle_cat); // Return bits available in the channel
-
- 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
-
- F64Seconds mLastSendTime[TC_EOF]; // Time since last send on this channel
- F64Seconds mDynamicAdjustTime; // Only dynamic adjust every 2 seconds or so.
-
-};
-
-#endif
+/** + * @file llthrottle.h + * @brief LLThrottle class used for network bandwidth control + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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 + F64Seconds 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 + + S32 getAvailable(S32 throttle_cat); // Return bits available in the channel + + 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 + + F64Seconds mLastSendTime[TC_EOF]; // Time since last send on this channel + F64Seconds mDynamicAdjustTime; // Only dynamic adjust every 2 seconds or so. + +}; + +#endif diff --git a/indra/llmessage/lltransfermanager.cpp b/indra/llmessage/lltransfermanager.cpp index f5351c6b58..72d623ea42 100644 --- a/indra/llmessage/lltransfermanager.cpp +++ b/indra/llmessage/lltransfermanager.cpp @@ -1,1401 +1,1401 @@ -/**
- * @file lltransfermanager.cpp
- * @brief Improved transfer mechanism for moving data through the
- * message system.
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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()
-{
- // LLTransferManager should have been cleaned up by message system shutdown process
- llassert(!mValid);
- if (mValid)
- {
- // Usually happens if OS tries to kill viewer
- cleanup();
- }
-}
-
-
-void LLTransferManager::init()
-{
- if (mValid)
- {
- LL_ERRS() << "Double initializing LLTransferManager!" << LL_ENDL;
- }
- 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,cur;
-
- iter = mTransferConnections.begin();
-
- while (iter !=mTransferConnections.end())
- {
- cur = iter;
- iter++;
- cur->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.
- //LL_WARNS() << "Cleaning up nonexistent transfer connection to " << host << LL_ENDL;
- 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 **)
-{
- //LL_INFOS() << "LLTransferManager::processTransferRequest" << LL_ENDL;
-
- 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)
- {
- LL_WARNS() << "Source channel not found" << LL_ENDL;
- return;
- }
-
- if (tscp->findTransferSource(transfer_id))
- {
- LL_WARNS() << "Duplicate request for transfer " << transfer_id << ", aborting!" << LL_ENDL;
- return;
- }
-
- S32 size = msgp->getSize("TransferInfo", "Params");
- if(size > MAX_PARAMS_SIZE)
- {
- LL_WARNS() << "LLTransferManager::processTransferRequest params too big."
- << LL_ENDL;
- return;
- }
-
- //LL_INFOS() << transfer_id << ":" << source_type << ":" << channel_type << ":" << priority << LL_ENDL;
- LLTransferSource* tsp = LLTransferSource::createSource(
- source_type,
- transfer_id,
- priority);
- if(!tsp)
- {
- LL_WARNS() << "LLTransferManager::processTransferRequest couldn't create"
- << " transfer source!" << LL_ENDL;
- return;
- }
- U8 tmp[MAX_PARAMS_SIZE];
- msgp->getBinaryData("TransferInfo", "Params", tmp, size);
-
- LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
- bool unpack_ok = tsp->unpackParams(dpb);
- if (!unpack_ok)
- {
- // This should only happen if the data is corrupt or
- // incorrectly packed.
- // *NOTE: We may want to call abortTransfer().
- LL_WARNS() << "LLTransferManager::processTransferRequest: bad parameters."
- << LL_ENDL;
- delete tsp;
- return;
- }
-
- tscp->addTransferSource(tsp);
- tsp->initTransfer();
-}
-
-
-//static
-void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
-{
- //LL_INFOS() << "LLTransferManager::processTransferInfo" << LL_ENDL;
-
- 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);
-
- //LL_INFOS() << transfer_id << ":" << target_type<< ":" << channel_type << LL_ENDL;
- LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
- if (!ttcp)
- {
- LL_WARNS() << "Target channel not found" << LL_ENDL;
- // Should send a message to abort the transfer.
- return;
- }
-
- LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
- if (!ttp)
- {
- LL_WARNS() << "TransferInfo for unknown transfer! Not able to handle this yet!" << LL_ENDL;
- // 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)
- {
- LL_WARNS() << transfer_id << ": Non-ok status, cleaning up" << LL_ENDL;
- ttp->completionCallback(status);
- // Clean up the transfer.
- ttcp->deleteTransfer(ttp);
- return;
- }
-
- // unpack the params
- S32 params_size = msgp->getSize("TransferInfo", "Params");
- if(params_size > MAX_PARAMS_SIZE)
- {
- LL_WARNS() << "LLTransferManager::processTransferInfo params too big."
- << LL_ENDL;
- return;
- }
- else if(params_size > 0)
- {
- U8 tmp[MAX_PARAMS_SIZE];
- msgp->getBinaryData("TransferInfo", "Params", tmp, params_size);
- LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
- if (!ttp->unpackParams(dpb))
- {
- // This should only happen if the data is corrupt or
- // incorrectly packed.
- LL_WARNS() << "LLTransferManager::processTransferRequest: bad params."
- << LL_ENDL;
- ttp->abortTransfer();
- ttcp->deleteTransfer(ttp);
- return;
- }
- }
-
- //LL_INFOS() << "Receiving " << transfer_id << ", size " << size << " bytes" << LL_ENDL;
- 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.
-// LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL;
- 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); /*Flawfinder: ignore*/
- }
- }
- 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)
- {
- LL_WARNS() << "LLTransferManager::processTransferInfo Error in playback!" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "LLTransferManager::processTransferInfo replay FINISHED for " << transfer_id << LL_ENDL;
- }
- // This transfer is done, either via error or not.
- ttp->completionCallback(status);
- ttcp->deleteTransfer(ttp);
- return;
- }
- }
-}
-
-
-//static
-void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
-{
- //LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL;
-
- 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.
- //LL_INFOS() << transfer_id << ":" << channel_type << LL_ENDL;
- LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
- if (!ttcp)
- {
- LL_WARNS() << "Target channel not found" << LL_ENDL;
- return;
- }
-
- LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
- if (!ttp)
- {
- LL_WARNS() << "Didn't find matching transfer for " << transfer_id
- << " processing packet " << packet_id
- << " from " << msgp->getSender() << LL_ENDL;
- 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))
- {
- LL_WARNS() << "Invalid transfer packet size " << size << LL_ENDL;
- 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))
- {
- // Put this on a list of packets to be delivered later.
- if(!ttp->addDelayedPacket(packet_id, status, tmp_data, size))
- {
- // Whoops - failed to add a delayed packet for some reason.
- LL_WARNS() << "Too many delayed packets processing transfer "
- << transfer_id << " from " << msgp->getSender() << LL_ENDL;
- ttp->abortTransfer();
- ttcp->deleteTransfer(ttp);
- return;
- }
-#if 0
- // Spammy!
- const S32 LL_TRANSFER_WARN_GAP = 10;
- if(!ttp->gotInfo())
- {
- LL_WARNS() << "Got data packet before information in transfer "
- << transfer_id << " from " << msgp->getSender()
- << ", got " << packet_id << LL_ENDL;
- }
- else if((packet_id - ttp->getNextPacketID()) > LL_TRANSFER_WARN_GAP)
- {
- LL_WARNS() << "Out of order packet in transfer " << transfer_id
- << " from " << msgp->getSender() << ", got " << packet_id
- << " expecting " << ttp->getNextPacketID() << LL_ENDL;
- }
-#endif
- 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)
- {
- LL_WARNS() << "LLTransferManager::processTransferPacket Error in transfer!" << LL_ENDL;
- }
- else
- {
-// LL_INFOS() << "LLTransferManager::processTransferPacket done for " << transfer_id << LL_ENDL;
- }
- // 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.
-// LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL;
- 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); /*Flawfinder: ignore*/
- }
- }
- 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 **)
-{
- //LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL;
-
- 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;
- }
- }
-
- LL_WARNS() << "Couldn't find transfer " << transfer_id << " to abort!" << LL_ENDL;
-}
-
-
-//static
-void LLTransferManager::reliablePacketCallback(void **user_data, S32 result)
-{
- LLUUID *transfer_idp = (LLUUID *)user_data;
- if (result &&
- transfer_idp != NULL)
- {
- LLTransferSource *tsp = gTransferManager.findTransferSource(*transfer_idp);
- if (tsp)
- {
- LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " due to failed reliable resends!" << LL_ENDL;
- LLTransferSourceChannel *tscp = tsp->mChannelp;
- tsp->abortTransfer();
- tscp->deleteTransfer(tsp);
- }
- else
- {
- LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " but can't find the LLTransferSource object" << LL_ENDL;
- }
- }
- 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, cur;
- iter = mTransferSourceChannels.begin();
-
- while (iter !=mTransferSourceChannels.end())
- {
- cur = iter;
- iter++;
- (*cur)->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 =
- mTransferSources.mMap.begin();
- LLPriQueueMap<LLTransferSource*>::pqm_iter end =
- mTransferSources.mMap.end();
- for (; iter != 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())
- {
- // *NOTE: 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.
- //LL_WARNS() << "Blocking transfers due to blocked circuit for " << getHost() << LL_ENDL;
- return;
- }
-
- const S32 throttle_id = mThrottleID;
-
- LLThrottleGroup &tg = cdp->getThrottleGroup();
-
- if (tg.checkOverflow(throttle_id, 0.f))
- {
- return;
- }
-
- LLPriQueueMap<LLTransferSource *>::pqm_iter iter, next;
-
- bool done = false;
- for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;)
- {
- //LL_INFOS() << "LLTransferSourceChannel::updateTransfers()" << LL_ENDL;
- // Do stuff.
- next = iter;
- next++;
-
- 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=next;
- continue;
- }
-
- LLUUID *cb_uuid = new LLUUID(tsp->getID());
- LLUUID transaction_id = 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, F32Seconds(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;
- }
-
- if (findTransferSource(transaction_id) == NULL)
- {
- //Warning! In the case of an aborted transfer, the sendReliable call above calls
- //AbortTransfer which in turn calls deleteTransfer which means that somewhere way
- //down the chain our current iter can get invalidated resulting in an infrequent
- //sim crash. This check gets us to a valid transfer source in this event.
- iter=next;
- continue;
- }
-
- // 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:
- LL_WARNS() << "Error in transfer dataCallback!" << LL_ENDL;
- // fall through
- case LLTS_DONE:
- // We need to clean up this transfer source.
- //LL_INFOS() << "LLTransferSourceChannel::updateTransfers() " << tsp->getID() << " done" << LL_ENDL;
- tsp->completionCallback(status);
- delete tsp;
-
- mTransferSources.mMap.erase(iter);
- iter = next;
- break;
- default:
- LL_ERRS() << "Unknown transfer error code!" << LL_ENDL;
- }
-
- // 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;
-}
-
-
-void LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp)
-{
- if (tsp)
- {
- LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
- for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
- {
- if (iter->second == tsp)
- {
- delete tsp;
- mTransferSources.mMap.erase(iter);
- return;
- }
- }
-
- LL_WARNS() << "Unable to find transfer source id "
- << tsp->getID()
- << " to delete!"
- << LL_ENDL;
- }
-}
-
-
-//
-// 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,
- source_params.getType());
- if (!ttp)
- {
- LL_WARNS() << "LLTransferManager::requestTransfer aborting due to target creation failure!" << LL_ENDL;
- return;
- }
-
- 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;
-}
-
-
-void LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp)
-{
- if (ttp)
- {
- tt_iter iter;
- for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
- {
- if (*iter == ttp)
- {
- delete ttp;
- mTransferTargets.erase(iter);
- return;
- }
- }
-
- LL_WARNS() << "Unable to find transfer target id "
- << ttp->getID()
- << " to delete!"
- << LL_ENDL;
- }
-}
-
-
-//
-// 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);
- U8 tmp[MAX_PARAMS_SIZE];
- LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
- packParams(dp);
- S32 len = dp.getCurrentSize();
- gMessageSystem->addBinaryData("Params", tmp, len);
- 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
- LL_INFOS() << "LLTransferSource::Aborting transfer " << getID() << " to " << mChannelp->getHost() << LL_ENDL;
- 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.
- LL_ERRS() << "Reregistering source type " << stype << LL_ENDL;
- }
- 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.
- LL_WARNS() << "Unknown transfer source type: " << stype << LL_ENDL;
- 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); /*Flawfinder: ignore*/
- }
-}
-
-LLTransferPacket::~LLTransferPacket()
-{
- delete[] mDatap;
-}
-
-//
-// LLTransferTarget implementation
-//
-
-LLTransferTarget::LLTransferTarget(
- LLTransferTargetType type,
- const LLUUID& transfer_id,
- LLTransferSourceType source_type) :
- mType(type),
- mSourceType(source_type),
- mID(transfer_id),
- mChannelp(NULL),
- 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
- LL_INFOS() << "LLTransferTarget::Aborting transfer " << getID() << " from " << mChannelp->getHost() << LL_ENDL;
- gMessageSystem->newMessage("TransferAbort");
- gMessageSystem->nextBlock("TransferInfo");
- gMessageSystem->addUUID("TransferID", getID());
- gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
- gMessageSystem->sendReliable(mChannelp->getHost());
-
- completionCallback(LLTS_ABORT);
-}
-
-bool LLTransferTarget::addDelayedPacket(
- const S32 packet_id,
- const LLTSCode status,
- U8* datap,
- const S32 size)
-{
- const transfer_packet_map::size_type LL_MAX_DELAYED_PACKETS = 100;
- if(mDelayedPacketMap.size() > LL_MAX_DELAYED_PACKETS)
- {
- // too many delayed packets
- return false;
- }
-
- LLTransferPacket* tpp = new LLTransferPacket(
- packet_id,
- status,
- datap,
- size);
-
-#ifdef _DEBUG
- transfer_packet_map::iterator iter = mDelayedPacketMap.find(packet_id);
- if (iter != mDelayedPacketMap.end())
- {
- if (!(iter->second->mSize == size) && !(iter->second->mDatap == datap))
- {
- LL_ERRS() << "Packet ALREADY in delayed packet map!" << LL_ENDL;
- }
- }
-#endif
-
- mDelayedPacketMap[packet_id] = tpp;
- return true;
-}
-
-
-LLTransferTarget* LLTransferTarget::createTarget(
- LLTransferTargetType type,
- const LLUUID& id,
- LLTransferSourceType source_type)
-{
- switch (type)
- {
- case LLTTT_FILE:
- return new LLTransferTargetFile(id, source_type);
- case LLTTT_VFILE:
- return new LLTransferTargetVFile(id, source_type);
- default:
- LL_WARNS() << "Unknown transfer target type: " << type << LL_ENDL;
- 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
-{
- LL_DEBUGS() << "LLTransferSourceParamsInvItem::packParams()" << LL_ENDL;
- 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),
- mAssetType(LLAssetType::AT_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");
- // *NOTE: We do not want to pass the session id from the server to
- // the client, but I am not sure if anyone expects this value to
- // be set on the client.
- 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;
-}
+/** + * @file lltransfermanager.cpp + * @brief Improved transfer mechanism for moving data through the + * message system. + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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() +{ + // LLTransferManager should have been cleaned up by message system shutdown process + llassert(!mValid); + if (mValid) + { + // Usually happens if OS tries to kill viewer + cleanup(); + } +} + + +void LLTransferManager::init() +{ + if (mValid) + { + LL_ERRS() << "Double initializing LLTransferManager!" << LL_ENDL; + } + 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,cur; + + iter = mTransferConnections.begin(); + + while (iter !=mTransferConnections.end()) + { + cur = iter; + iter++; + cur->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. + //LL_WARNS() << "Cleaning up nonexistent transfer connection to " << host << LL_ENDL; + 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 **) +{ + //LL_INFOS() << "LLTransferManager::processTransferRequest" << LL_ENDL; + + 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) + { + LL_WARNS() << "Source channel not found" << LL_ENDL; + return; + } + + if (tscp->findTransferSource(transfer_id)) + { + LL_WARNS() << "Duplicate request for transfer " << transfer_id << ", aborting!" << LL_ENDL; + return; + } + + S32 size = msgp->getSize("TransferInfo", "Params"); + if(size > MAX_PARAMS_SIZE) + { + LL_WARNS() << "LLTransferManager::processTransferRequest params too big." + << LL_ENDL; + return; + } + + //LL_INFOS() << transfer_id << ":" << source_type << ":" << channel_type << ":" << priority << LL_ENDL; + LLTransferSource* tsp = LLTransferSource::createSource( + source_type, + transfer_id, + priority); + if(!tsp) + { + LL_WARNS() << "LLTransferManager::processTransferRequest couldn't create" + << " transfer source!" << LL_ENDL; + return; + } + U8 tmp[MAX_PARAMS_SIZE]; + msgp->getBinaryData("TransferInfo", "Params", tmp, size); + + LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE); + bool unpack_ok = tsp->unpackParams(dpb); + if (!unpack_ok) + { + // This should only happen if the data is corrupt or + // incorrectly packed. + // *NOTE: We may want to call abortTransfer(). + LL_WARNS() << "LLTransferManager::processTransferRequest: bad parameters." + << LL_ENDL; + delete tsp; + return; + } + + tscp->addTransferSource(tsp); + tsp->initTransfer(); +} + + +//static +void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **) +{ + //LL_INFOS() << "LLTransferManager::processTransferInfo" << LL_ENDL; + + 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); + + //LL_INFOS() << transfer_id << ":" << target_type<< ":" << channel_type << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type); + if (!ttcp) + { + LL_WARNS() << "Target channel not found" << LL_ENDL; + // Should send a message to abort the transfer. + return; + } + + LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id); + if (!ttp) + { + LL_WARNS() << "TransferInfo for unknown transfer! Not able to handle this yet!" << LL_ENDL; + // 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) + { + LL_WARNS() << transfer_id << ": Non-ok status, cleaning up" << LL_ENDL; + ttp->completionCallback(status); + // Clean up the transfer. + ttcp->deleteTransfer(ttp); + return; + } + + // unpack the params + S32 params_size = msgp->getSize("TransferInfo", "Params"); + if(params_size > MAX_PARAMS_SIZE) + { + LL_WARNS() << "LLTransferManager::processTransferInfo params too big." + << LL_ENDL; + return; + } + else if(params_size > 0) + { + U8 tmp[MAX_PARAMS_SIZE]; + msgp->getBinaryData("TransferInfo", "Params", tmp, params_size); + LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE); + if (!ttp->unpackParams(dpb)) + { + // This should only happen if the data is corrupt or + // incorrectly packed. + LL_WARNS() << "LLTransferManager::processTransferRequest: bad params." + << LL_ENDL; + ttp->abortTransfer(); + ttcp->deleteTransfer(ttp); + return; + } + } + + //LL_INFOS() << "Receiving " << transfer_id << ", size " << size << " bytes" << LL_ENDL; + 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. +// LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL; + 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); /*Flawfinder: ignore*/ + } + } + 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) + { + LL_WARNS() << "LLTransferManager::processTransferInfo Error in playback!" << LL_ENDL; + } + else + { + LL_INFOS() << "LLTransferManager::processTransferInfo replay FINISHED for " << transfer_id << LL_ENDL; + } + // This transfer is done, either via error or not. + ttp->completionCallback(status); + ttcp->deleteTransfer(ttp); + return; + } + } +} + + +//static +void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **) +{ + //LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL; + + 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. + //LL_INFOS() << transfer_id << ":" << channel_type << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type); + if (!ttcp) + { + LL_WARNS() << "Target channel not found" << LL_ENDL; + return; + } + + LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id); + if (!ttp) + { + LL_WARNS() << "Didn't find matching transfer for " << transfer_id + << " processing packet " << packet_id + << " from " << msgp->getSender() << LL_ENDL; + 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)) + { + LL_WARNS() << "Invalid transfer packet size " << size << LL_ENDL; + 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)) + { + // Put this on a list of packets to be delivered later. + if(!ttp->addDelayedPacket(packet_id, status, tmp_data, size)) + { + // Whoops - failed to add a delayed packet for some reason. + LL_WARNS() << "Too many delayed packets processing transfer " + << transfer_id << " from " << msgp->getSender() << LL_ENDL; + ttp->abortTransfer(); + ttcp->deleteTransfer(ttp); + return; + } +#if 0 + // Spammy! + const S32 LL_TRANSFER_WARN_GAP = 10; + if(!ttp->gotInfo()) + { + LL_WARNS() << "Got data packet before information in transfer " + << transfer_id << " from " << msgp->getSender() + << ", got " << packet_id << LL_ENDL; + } + else if((packet_id - ttp->getNextPacketID()) > LL_TRANSFER_WARN_GAP) + { + LL_WARNS() << "Out of order packet in transfer " << transfer_id + << " from " << msgp->getSender() << ", got " << packet_id + << " expecting " << ttp->getNextPacketID() << LL_ENDL; + } +#endif + 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) + { + LL_WARNS() << "LLTransferManager::processTransferPacket Error in transfer!" << LL_ENDL; + } + else + { +// LL_INFOS() << "LLTransferManager::processTransferPacket done for " << transfer_id << LL_ENDL; + } + // 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. +// LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL; + 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); /*Flawfinder: ignore*/ + } + } + 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 **) +{ + //LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL; + + 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; + } + } + + LL_WARNS() << "Couldn't find transfer " << transfer_id << " to abort!" << LL_ENDL; +} + + +//static +void LLTransferManager::reliablePacketCallback(void **user_data, S32 result) +{ + LLUUID *transfer_idp = (LLUUID *)user_data; + if (result && + transfer_idp != NULL) + { + LLTransferSource *tsp = gTransferManager.findTransferSource(*transfer_idp); + if (tsp) + { + LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " due to failed reliable resends!" << LL_ENDL; + LLTransferSourceChannel *tscp = tsp->mChannelp; + tsp->abortTransfer(); + tscp->deleteTransfer(tsp); + } + else + { + LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " but can't find the LLTransferSource object" << LL_ENDL; + } + } + 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, cur; + iter = mTransferSourceChannels.begin(); + + while (iter !=mTransferSourceChannels.end()) + { + cur = iter; + iter++; + (*cur)->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 = + mTransferSources.mMap.begin(); + LLPriQueueMap<LLTransferSource*>::pqm_iter end = + mTransferSources.mMap.end(); + for (; iter != 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()) + { + // *NOTE: 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. + //LL_WARNS() << "Blocking transfers due to blocked circuit for " << getHost() << LL_ENDL; + return; + } + + const S32 throttle_id = mThrottleID; + + LLThrottleGroup &tg = cdp->getThrottleGroup(); + + if (tg.checkOverflow(throttle_id, 0.f)) + { + return; + } + + LLPriQueueMap<LLTransferSource *>::pqm_iter iter, next; + + bool done = false; + for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;) + { + //LL_INFOS() << "LLTransferSourceChannel::updateTransfers()" << LL_ENDL; + // Do stuff. + next = iter; + next++; + + 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=next; + continue; + } + + LLUUID *cb_uuid = new LLUUID(tsp->getID()); + LLUUID transaction_id = 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, F32Seconds(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; + } + + if (findTransferSource(transaction_id) == NULL) + { + //Warning! In the case of an aborted transfer, the sendReliable call above calls + //AbortTransfer which in turn calls deleteTransfer which means that somewhere way + //down the chain our current iter can get invalidated resulting in an infrequent + //sim crash. This check gets us to a valid transfer source in this event. + iter=next; + continue; + } + + // 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: + LL_WARNS() << "Error in transfer dataCallback!" << LL_ENDL; + // fall through + case LLTS_DONE: + // We need to clean up this transfer source. + //LL_INFOS() << "LLTransferSourceChannel::updateTransfers() " << tsp->getID() << " done" << LL_ENDL; + tsp->completionCallback(status); + delete tsp; + + mTransferSources.mMap.erase(iter); + iter = next; + break; + default: + LL_ERRS() << "Unknown transfer error code!" << LL_ENDL; + } + + // 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; +} + + +void LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp) +{ + if (tsp) + { + LLPriQueueMap<LLTransferSource *>::pqm_iter iter; + for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++) + { + if (iter->second == tsp) + { + delete tsp; + mTransferSources.mMap.erase(iter); + return; + } + } + + LL_WARNS() << "Unable to find transfer source id " + << tsp->getID() + << " to delete!" + << LL_ENDL; + } +} + + +// +// 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, + source_params.getType()); + if (!ttp) + { + LL_WARNS() << "LLTransferManager::requestTransfer aborting due to target creation failure!" << LL_ENDL; + return; + } + + 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; +} + + +void LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp) +{ + if (ttp) + { + tt_iter iter; + for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++) + { + if (*iter == ttp) + { + delete ttp; + mTransferTargets.erase(iter); + return; + } + } + + LL_WARNS() << "Unable to find transfer target id " + << ttp->getID() + << " to delete!" + << LL_ENDL; + } +} + + +// +// 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); + U8 tmp[MAX_PARAMS_SIZE]; + LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE); + packParams(dp); + S32 len = dp.getCurrentSize(); + gMessageSystem->addBinaryData("Params", tmp, len); + 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 + LL_INFOS() << "LLTransferSource::Aborting transfer " << getID() << " to " << mChannelp->getHost() << LL_ENDL; + 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. + LL_ERRS() << "Reregistering source type " << stype << LL_ENDL; + } + 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. + LL_WARNS() << "Unknown transfer source type: " << stype << LL_ENDL; + 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); /*Flawfinder: ignore*/ + } +} + +LLTransferPacket::~LLTransferPacket() +{ + delete[] mDatap; +} + +// +// LLTransferTarget implementation +// + +LLTransferTarget::LLTransferTarget( + LLTransferTargetType type, + const LLUUID& transfer_id, + LLTransferSourceType source_type) : + mType(type), + mSourceType(source_type), + mID(transfer_id), + mChannelp(NULL), + 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 + LL_INFOS() << "LLTransferTarget::Aborting transfer " << getID() << " from " << mChannelp->getHost() << LL_ENDL; + gMessageSystem->newMessage("TransferAbort"); + gMessageSystem->nextBlock("TransferInfo"); + gMessageSystem->addUUID("TransferID", getID()); + gMessageSystem->addS32("ChannelType", mChannelp->getChannelType()); + gMessageSystem->sendReliable(mChannelp->getHost()); + + completionCallback(LLTS_ABORT); +} + +bool LLTransferTarget::addDelayedPacket( + const S32 packet_id, + const LLTSCode status, + U8* datap, + const S32 size) +{ + const transfer_packet_map::size_type LL_MAX_DELAYED_PACKETS = 100; + if(mDelayedPacketMap.size() > LL_MAX_DELAYED_PACKETS) + { + // too many delayed packets + return false; + } + + LLTransferPacket* tpp = new LLTransferPacket( + packet_id, + status, + datap, + size); + +#ifdef _DEBUG + transfer_packet_map::iterator iter = mDelayedPacketMap.find(packet_id); + if (iter != mDelayedPacketMap.end()) + { + if (!(iter->second->mSize == size) && !(iter->second->mDatap == datap)) + { + LL_ERRS() << "Packet ALREADY in delayed packet map!" << LL_ENDL; + } + } +#endif + + mDelayedPacketMap[packet_id] = tpp; + return true; +} + + +LLTransferTarget* LLTransferTarget::createTarget( + LLTransferTargetType type, + const LLUUID& id, + LLTransferSourceType source_type) +{ + switch (type) + { + case LLTTT_FILE: + return new LLTransferTargetFile(id, source_type); + case LLTTT_VFILE: + return new LLTransferTargetVFile(id, source_type); + default: + LL_WARNS() << "Unknown transfer target type: " << type << LL_ENDL; + 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 +{ + LL_DEBUGS() << "LLTransferSourceParamsInvItem::packParams()" << LL_ENDL; + 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), + mAssetType(LLAssetType::AT_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"); + // *NOTE: We do not want to pass the session id from the server to + // the client, but I am not sure if anyone expects this value to + // be set on the client. + 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 index 184ff2563a..ada5528b49 100644 --- a/indra/llmessage/lltransfermanager.h +++ b/indra/llmessage/lltransfermanager.h @@ -1,499 +1,499 @@ -/**
- * @file lltransfermanager.h
- * @brief Improved transfer mechanism for moving data through the
- * message system.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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 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);
- void 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);
- void 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 void packParams(LLDataPacker& dp) const = 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,
- LLTransferSourceType source_type);
- virtual ~LLTransferTarget();
-
- // Accessors
- LLUUID getID() const { return mID; }
- LLTransferTargetType getType() const { return mType; }
- LLTransferTargetChannel *getChannel() const { return mChannelp; }
- LLTransferSourceType getSourceType() const { return mSourceType; }
-
- // Static functionality
- static LLTransferTarget* createTarget(
- LLTransferTargetType target_type,
- const LLUUID& request_id,
- LLTransferSourceType source_type);
-
- // friends
- friend class LLTransferManager;
- friend class LLTransferTargetChannel;
-
-protected:
- // Implementation
- virtual bool unpackParams(LLDataPacker& dp) = 0;
- 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; }
-
- bool 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;
- LLTransferSourceType mSourceType;
- 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
+/** + * @file lltransfermanager.h + * @brief Improved transfer mechanism for moving data through the + * message system. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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 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); + void 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); + void 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 void packParams(LLDataPacker& dp) const = 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, + LLTransferSourceType source_type); + virtual ~LLTransferTarget(); + + // Accessors + LLUUID getID() const { return mID; } + LLTransferTargetType getType() const { return mType; } + LLTransferTargetChannel *getChannel() const { return mChannelp; } + LLTransferSourceType getSourceType() const { return mSourceType; } + + // Static functionality + static LLTransferTarget* createTarget( + LLTransferTargetType target_type, + const LLUUID& request_id, + LLTransferSourceType source_type); + + // friends + friend class LLTransferManager; + friend class LLTransferTargetChannel; + +protected: + // Implementation + virtual bool unpackParams(LLDataPacker& dp) = 0; + 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; } + + bool 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; + LLTransferSourceType mSourceType; + 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 index f09855aacd..082f12b8bf 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -1,254 +1,254 @@ -/**
- * @file lltransfersourceasset.cpp
- * @brief Transfer system for sending an asset.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lltransfersourceasset.h"
-
-#include "llerror.h"
-#include "message.h"
-#include "lldatapacker.h"
-#include "lldir.h"
-#include "llfilesystem.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.
- LLUUID* tidp;
- if(LLAssetType::lookupIsAssetFetchByIDAllowed(mParams.getAssetType()))
- {
- tidp = new LLUUID(getID());
- gAssetStorage->getAssetData(
- mParams.getAssetID(),
- mParams.getAssetType(),
- LLTransferSourceAsset::responderCallback,
- tidp,
- false);
- }
- else
- {
- LL_WARNS() << "Attempted to request blocked asset "
- << mParams.getAssetID() << ":"
- << LLAssetType::lookupHumanReadable(mParams.getAssetType())
- << LL_ENDL;
- sendTransferStatus(LLTS_ERROR);
- }
- }
- else
- {
- LL_WARNS() << "Attempted to request asset " << mParams.getAssetID()
- << ":" << LLAssetType::lookupHumanReadable(mParams.getAssetType())
- << " without an asset system!" << LL_ENDL;
- 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)
-{
- //LL_INFOS() << "LLTransferSourceAsset::dataCallback" << LL_ENDL;
- if (!mGotResponse)
- {
- return LLTS_SKIP;
- }
-
- LLFileSystem vf(mParams.getAssetID(), mParams.getAssetType(), LLFileSystem::READ);
-
- if (!vf.getSize())
- {
- // Something bad happened with the asset request!
- return LLTS_ERROR;
- }
-
- if (packet_id != mLastPacketID + 1)
- {
- LL_ERRS() << "Can't handle out of order file transfer yet!" << LL_ENDL;
- }
-
- // grab a buffer from the right place in the file
- if (!vf.seek(mCurPos, 0))
- {
- LL_WARNS() << "LLTransferSourceAsset Can't seek to " << mCurPos << " length " << vf.getSize() << LL_ENDL;
- LL_WARNS() << "While sending " << mParams.getAssetID() << LL_ENDL;
- return LLTS_ERROR;
- }
-
- delete_returned = true;
- U8 *tmpp = new U8[max_bytes];
- *data_handle = tmpp;
- if (!vf.read(tmpp, max_bytes)) /* Flawfinder: Ignore */
- {
- // 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.
-}
-
-void LLTransferSourceAsset::packParams(LLDataPacker& dp) const
-{
- //LL_INFOS() << "LLTransferSourceAsset::packParams" << LL_ENDL;
- mParams.packParams(dp);
-}
-
-bool LLTransferSourceAsset::unpackParams(LLDataPacker &dp)
-{
- //LL_INFOS() << "LLTransferSourceAsset::unpackParams" << LL_ENDL;
- return mParams.unpackParams(dp);
-}
-
-
-void LLTransferSourceAsset::responderCallback(const LLUUID& uuid, LLAssetType::EType type,
- void *user_data, S32 result, LLExtStat ext_status )
-{
- LLUUID *tidp = ((LLUUID*) user_data);
- LLUUID transfer_id = *(tidp);
- delete tidp;
- tidp = NULL;
-
- LLTransferSourceAsset *tsap = (LLTransferSourceAsset *) gTransferManager.findTransferSource(transfer_id);
-
- if (!tsap)
- {
- LL_INFOS() << "Aborting transfer " << transfer_id << " callback, transfer source went away" << LL_ENDL;
- return;
- }
-
- if (result)
- {
- LL_INFOS() << "AssetStorage: Error " << gAssetStorage->getErrorString(result) << " downloading uuid " << uuid << LL_ENDL;
- }
-
- LLTSCode status;
-
- tsap->mGotResponse = true;
- if (LL_ERR_NOERR == result)
- {
- // Everything's OK.
- LLFileSystem vf(uuid, type, LLFileSystem::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),
-
- mAssetType(LLAssetType::AT_NONE)
-{
-}
-
-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;
-}
-
+/** + * @file lltransfersourceasset.cpp + * @brief Transfer system for sending an asset. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lltransfersourceasset.h" + +#include "llerror.h" +#include "message.h" +#include "lldatapacker.h" +#include "lldir.h" +#include "llfilesystem.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. + LLUUID* tidp; + if(LLAssetType::lookupIsAssetFetchByIDAllowed(mParams.getAssetType())) + { + tidp = new LLUUID(getID()); + gAssetStorage->getAssetData( + mParams.getAssetID(), + mParams.getAssetType(), + LLTransferSourceAsset::responderCallback, + tidp, + false); + } + else + { + LL_WARNS() << "Attempted to request blocked asset " + << mParams.getAssetID() << ":" + << LLAssetType::lookupHumanReadable(mParams.getAssetType()) + << LL_ENDL; + sendTransferStatus(LLTS_ERROR); + } + } + else + { + LL_WARNS() << "Attempted to request asset " << mParams.getAssetID() + << ":" << LLAssetType::lookupHumanReadable(mParams.getAssetType()) + << " without an asset system!" << LL_ENDL; + 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) +{ + //LL_INFOS() << "LLTransferSourceAsset::dataCallback" << LL_ENDL; + if (!mGotResponse) + { + return LLTS_SKIP; + } + + LLFileSystem vf(mParams.getAssetID(), mParams.getAssetType(), LLFileSystem::READ); + + if (!vf.getSize()) + { + // Something bad happened with the asset request! + return LLTS_ERROR; + } + + if (packet_id != mLastPacketID + 1) + { + LL_ERRS() << "Can't handle out of order file transfer yet!" << LL_ENDL; + } + + // grab a buffer from the right place in the file + if (!vf.seek(mCurPos, 0)) + { + LL_WARNS() << "LLTransferSourceAsset Can't seek to " << mCurPos << " length " << vf.getSize() << LL_ENDL; + LL_WARNS() << "While sending " << mParams.getAssetID() << LL_ENDL; + return LLTS_ERROR; + } + + delete_returned = true; + U8 *tmpp = new U8[max_bytes]; + *data_handle = tmpp; + if (!vf.read(tmpp, max_bytes)) /* Flawfinder: Ignore */ + { + // 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. +} + +void LLTransferSourceAsset::packParams(LLDataPacker& dp) const +{ + //LL_INFOS() << "LLTransferSourceAsset::packParams" << LL_ENDL; + mParams.packParams(dp); +} + +bool LLTransferSourceAsset::unpackParams(LLDataPacker &dp) +{ + //LL_INFOS() << "LLTransferSourceAsset::unpackParams" << LL_ENDL; + return mParams.unpackParams(dp); +} + + +void LLTransferSourceAsset::responderCallback(const LLUUID& uuid, LLAssetType::EType type, + void *user_data, S32 result, LLExtStat ext_status ) +{ + LLUUID *tidp = ((LLUUID*) user_data); + LLUUID transfer_id = *(tidp); + delete tidp; + tidp = NULL; + + LLTransferSourceAsset *tsap = (LLTransferSourceAsset *) gTransferManager.findTransferSource(transfer_id); + + if (!tsap) + { + LL_INFOS() << "Aborting transfer " << transfer_id << " callback, transfer source went away" << LL_ENDL; + return; + } + + if (result) + { + LL_INFOS() << "AssetStorage: Error " << gAssetStorage->getErrorString(result) << " downloading uuid " << uuid << LL_ENDL; + } + + LLTSCode status; + + tsap->mGotResponse = true; + if (LL_ERR_NOERR == result) + { + // Everything's OK. + LLFileSystem vf(uuid, type, LLFileSystem::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), + + mAssetType(LLAssetType::AT_NONE) +{ +} + +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 index 50755d65ad..6c4c39011a 100644 --- a/indra/llmessage/lltransfersourceasset.h +++ b/indra/llmessage/lltransfersourceasset.h @@ -1,81 +1,81 @@ -/**
- * @file lltransfersourceasset.h
- * @brief Transfer system for sending an asset.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTRANSFERSOURCEASSET_H
-#define LL_LLTRANSFERSOURCEASSET_H
-
-#include "lltransfermanager.h"
-#include "llassetstorage.h"
-
-class LLFileSystem;
-
-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(const LLUUID& uuid, LLAssetType::EType type,
- void *user_data, S32 result, LLExtStat ext_status );
-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 void packParams(LLDataPacker& dp) const;
- /*virtual*/ bool unpackParams(LLDataPacker &dp);
-
-protected:
- LLTransferSourceParamsAsset mParams;
- bool mGotResponse;
-
- S32 mCurPos;
-};
-
-#endif // LL_LLTRANSFERSOURCEASSET_H
+/** + * @file lltransfersourceasset.h + * @brief Transfer system for sending an asset. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTRANSFERSOURCEASSET_H +#define LL_LLTRANSFERSOURCEASSET_H + +#include "lltransfermanager.h" +#include "llassetstorage.h" + +class LLFileSystem; + +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(const LLUUID& uuid, LLAssetType::EType type, + void *user_data, S32 result, LLExtStat ext_status ); +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 void packParams(LLDataPacker& dp) const; + /*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 index 97d5716ca4..77a6c466c7 100644 --- a/indra/llmessage/lltransfersourcefile.cpp +++ b/indra/llmessage/lltransfersourcefile.cpp @@ -1,174 +1,174 @@ -/**
- * @file lltransfersourcefile.cpp
- * @brief Transfer system for sending a file.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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)
- {
- LL_ERRS() << "Destructor called without the completion callback being called!" << LL_ENDL;
- }
-}
-
-void LLTransferSourceFile::initTransfer()
-{
- std::string filename = mParams.getFilename();
- std::string delimiter = gDirUtilp->getDirDelimiter();
-
- if((filename == ".")
- || (filename == "..")
- || (filename.find(delimiter[0]) != std::string::npos))
- {
- LL_WARNS() << "Attempting to transfer file " << filename << " with path delimiter, aborting!" << LL_ENDL;
-
- sendTransferStatus(LLTS_ERROR);
- return;
- }
- // Look for the file.
- mFP = LLFile::fopen(mParams.getFilename(), "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)
-{
- //LL_INFOS() << "LLTransferSourceFile::dataCallback" << LL_ENDL;
-
- if (!mFP)
- {
- LL_ERRS() << "Data callback without file set!" << LL_ENDL;
- return LLTS_ERROR;
- }
-
- if (packet_id != mLastPacketID + 1)
- {
- LL_ERRS() << "Can't handle out of order file transfer yet!" << LL_ENDL;
- }
-
- // 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() && mParams.getFilename().substr(0, 4) == "TEMP")
- {
- LLFile::remove(mParams.getFilename());
- }
-}
-
-void LLTransferSourceFile::packParams(LLDataPacker& dp) const
-{
- //LL_INFOS() << "LLTransferSourceFile::packParams" << LL_ENDL;
- mParams.packParams(dp);
-}
-
-bool LLTransferSourceFile::unpackParams(LLDataPacker &dp)
-{
- //LL_INFOS() << "LLTransferSourceFile::unpackParams" << LL_ENDL;
- return mParams.unpackParams(dp);
-}
-
-
-LLTransferSourceParamsFile::LLTransferSourceParamsFile() :
- LLTransferSourceParams(LLTST_FILE),
- mDeleteOnCompletion(false)
-{
-}
-
-
-void LLTransferSourceParamsFile::packParams(LLDataPacker &dp) const
-{
- dp.packString(mFilename, "Filename");
- dp.packU8((U8)mDeleteOnCompletion, "Delete");
-}
-
-
-bool LLTransferSourceParamsFile::unpackParams(LLDataPacker &dp)
-{
- dp.unpackString(mFilename, "Filename");
- U8 delete_flag;
- dp.unpackU8(delete_flag, "Delete");
- mDeleteOnCompletion = delete_flag;
-
- LL_INFOS() << "Unpacked filename: " << mFilename << LL_ENDL;
- return true;
-}
+/** + * @file lltransfersourcefile.cpp + * @brief Transfer system for sending a file. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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) + { + LL_ERRS() << "Destructor called without the completion callback being called!" << LL_ENDL; + } +} + +void LLTransferSourceFile::initTransfer() +{ + std::string filename = mParams.getFilename(); + std::string delimiter = gDirUtilp->getDirDelimiter(); + + if((filename == ".") + || (filename == "..") + || (filename.find(delimiter[0]) != std::string::npos)) + { + LL_WARNS() << "Attempting to transfer file " << filename << " with path delimiter, aborting!" << LL_ENDL; + + sendTransferStatus(LLTS_ERROR); + return; + } + // Look for the file. + mFP = LLFile::fopen(mParams.getFilename(), "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) +{ + //LL_INFOS() << "LLTransferSourceFile::dataCallback" << LL_ENDL; + + if (!mFP) + { + LL_ERRS() << "Data callback without file set!" << LL_ENDL; + return LLTS_ERROR; + } + + if (packet_id != mLastPacketID + 1) + { + LL_ERRS() << "Can't handle out of order file transfer yet!" << LL_ENDL; + } + + // 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() && mParams.getFilename().substr(0, 4) == "TEMP") + { + LLFile::remove(mParams.getFilename()); + } +} + +void LLTransferSourceFile::packParams(LLDataPacker& dp) const +{ + //LL_INFOS() << "LLTransferSourceFile::packParams" << LL_ENDL; + mParams.packParams(dp); +} + +bool LLTransferSourceFile::unpackParams(LLDataPacker &dp) +{ + //LL_INFOS() << "LLTransferSourceFile::unpackParams" << LL_ENDL; + return mParams.unpackParams(dp); +} + + +LLTransferSourceParamsFile::LLTransferSourceParamsFile() : + LLTransferSourceParams(LLTST_FILE), + mDeleteOnCompletion(false) +{ +} + + +void LLTransferSourceParamsFile::packParams(LLDataPacker &dp) const +{ + dp.packString(mFilename, "Filename"); + dp.packU8((U8)mDeleteOnCompletion, "Delete"); +} + + +bool LLTransferSourceParamsFile::unpackParams(LLDataPacker &dp) +{ + dp.unpackString(mFilename, "Filename"); + U8 delete_flag; + dp.unpackU8(delete_flag, "Delete"); + mDeleteOnCompletion = delete_flag; + + LL_INFOS() << "Unpacked filename: " << mFilename << LL_ENDL; + return true; +} diff --git a/indra/llmessage/lltransfersourcefile.h b/indra/llmessage/lltransfersourcefile.h index 3b9cd7231c..a447ab091b 100644 --- a/indra/llmessage/lltransfersourcefile.h +++ b/indra/llmessage/lltransfersourcefile.h @@ -1,75 +1,75 @@ -/**
- * @file lltransfersourcefile.h
- * @brief Transfer system for sending a file.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTRANSFERSOURCEFILE_H
-#define LL_LLTRANSFERSOURCEFILE_H
-
-#include "lltransfermanager.h"
-
-class LLTransferSourceParamsFile : public LLTransferSourceParams
-{
-public:
- LLTransferSourceParamsFile();
- virtual ~LLTransferSourceParamsFile() {}
- /*virtual*/ void packParams(LLDataPacker &dp) const;
- /*virtual*/ bool unpackParams(LLDataPacker &dp);
-
- void setFilename(const std::string &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 void packParams(LLDataPacker& dp) const;
- /*virtual*/ bool unpackParams(LLDataPacker &dp);
-
-protected:
- LLTransferSourceParamsFile mParams;
- LLFILE *mFP;
-};
-
-#endif // LL_LLTRANSFERSOURCEFILE_H
+/** + * @file lltransfersourcefile.h + * @brief Transfer system for sending a file. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTRANSFERSOURCEFILE_H +#define LL_LLTRANSFERSOURCEFILE_H + +#include "lltransfermanager.h" + +class LLTransferSourceParamsFile : public LLTransferSourceParams +{ +public: + LLTransferSourceParamsFile(); + virtual ~LLTransferSourceParamsFile() {} + /*virtual*/ void packParams(LLDataPacker &dp) const; + /*virtual*/ bool unpackParams(LLDataPacker &dp); + + void setFilename(const std::string &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 void packParams(LLDataPacker& dp) const; + /*virtual*/ bool unpackParams(LLDataPacker &dp); + +protected: + LLTransferSourceParamsFile mParams; + LLFILE *mFP; +}; + +#endif // LL_LLTRANSFERSOURCEFILE_H diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index dbf377bfa2..fcf853845b 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -1,234 +1,234 @@ -/**
- * @file lltransfertargetvfile.cpp
- * @brief Transfer system for receiving a vfile.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lltransfertargetvfile.h"
-
-#include "lldatapacker.h"
-#include "llerror.h"
-#include "llfilesystem.h"
-
-//static
-void LLTransferTargetVFile::updateQueue(bool shutdown)
-{
-}
-
-
-LLTransferTargetParamsVFile::LLTransferTargetParamsVFile() :
- LLTransferTargetParams(LLTTT_VFILE),
- mAssetType(LLAssetType::AT_NONE),
- mCompleteCallback(NULL),
- mRequestDatap(NULL),
- mErrCode(0)
-{
-}
-
-void LLTransferTargetParamsVFile::setAsset(
- const LLUUID& asset_id,
- LLAssetType::EType asset_type)
-{
- mAssetID = asset_id;
- mAssetType = asset_type;
-}
-
-void LLTransferTargetParamsVFile::setCallback(LLTTVFCompleteCallback cb, LLBaseDownloadRequest& request)
-{
- mCompleteCallback = cb;
- if (mRequestDatap)
- {
- delete mRequestDatap;
- }
- mRequestDatap = request.getCopy();
-}
-
-bool LLTransferTargetParamsVFile::unpackParams(LLDataPacker& dp)
-{
- // if the source provided a new key, assign that to the asset id.
- if(dp.hasNext())
- {
- LLUUID dummy_id;
- dp.unpackUUID(dummy_id, "AgentID");
- dp.unpackUUID(dummy_id, "SessionID");
- dp.unpackUUID(dummy_id, "OwnerID");
- dp.unpackUUID(dummy_id, "TaskID");
- dp.unpackUUID(dummy_id, "ItemID");
- dp.unpackUUID(mAssetID, "AssetID");
- S32 dummy_type;
- dp.unpackS32(dummy_type, "AssetType");
- }
-
- // if we never got an asset id, this will always fail.
- if(mAssetID.isNull())
- {
- return false;
- }
- return true;
-}
-
-
-LLTransferTargetVFile::LLTransferTargetVFile(
- const LLUUID& uuid,
- LLTransferSourceType src_type) :
- LLTransferTarget(LLTTT_VFILE, uuid, src_type),
- mNeedsCreate(true)
-{
- mTempID.generate();
-}
-
-
-LLTransferTargetVFile::~LLTransferTargetVFile()
-{
- if (mParams.mRequestDatap)
- {
- // TODO: Consider doing it in LLTransferTargetParamsVFile's destructor
- delete mParams.mRequestDatap;
- mParams.mRequestDatap = NULL;
- }
-}
-
-
-// virtual
-bool LLTransferTargetVFile::unpackParams(LLDataPacker& dp)
-{
- if(LLTST_SIM_INV_ITEM == mSourceType)
- {
- return mParams.unpackParams(dp);
- }
- return true;
-}
-
-void LLTransferTargetVFile::applyParams(const LLTransferTargetParams ¶ms)
-{
- if (params.getType() != mType)
- {
- LL_WARNS() << "Target parameter type doesn't match!" << LL_ENDL;
- return;
- }
-
- mParams = (LLTransferTargetParamsVFile &)params;
-}
-
-
-LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size)
-{
- //LL_INFOS() << "LLTransferTargetFile::dataCallback" << LL_ENDL;
- //LL_INFOS() << "Packet: " << packet_id << LL_ENDL;
-
- LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND);
- if (mNeedsCreate)
- {
- mNeedsCreate = false;
- }
-
- if (!in_size)
- {
- return LLTS_OK;
- }
-
- if (!vf.write(in_datap, in_size))
- {
- LL_WARNS() << "Failure in LLTransferTargetVFile::dataCallback!" << LL_ENDL;
- return LLTS_ERROR;
- }
- return LLTS_OK;
-}
-
-
-void LLTransferTargetVFile::completionCallback(const LLTSCode status)
-{
- //LL_INFOS() << "LLTransferTargetVFile::completionCallback" << LL_ENDL;
-
- if (!gAssetStorage)
- {
- LL_WARNS() << "Aborting vfile transfer after asset storage shut down!" << LL_ENDL;
- return;
- }
-
- // Still need to gracefully handle error conditions.
- S32 err_code = 0;
- switch (status)
- {
- case LLTS_DONE:
- if (!mNeedsCreate)
- {
- LLFileSystem file(mTempID, mParams.getAssetType(), LLFileSystem::WRITE);
- if (!file.rename(mParams.getAssetID(), mParams.getAssetType()))
- {
- LL_ERRS() << "LLTransferTargetVFile: rename failed" << LL_ENDL;
- }
- }
- err_code = LL_ERR_NOERR;
- LL_DEBUGS() << "LLTransferTargetVFile::completionCallback for "
- << mParams.getAssetID() << ","
- << LLAssetType::lookup(mParams.getAssetType())
- << " with temp id " << mTempID << LL_ENDL;
- 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.
- LL_WARNS() << "Aborting vfile transfer for " << mParams.getAssetID() << LL_ENDL;
- LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::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.mRequestDatap)
- {
- if (mParams.mCompleteCallback)
- {
- mParams.mCompleteCallback(err_code,
- mParams.getAssetID(),
- mParams.getAssetType(),
- mParams.mRequestDatap,
- LLExtStat::NONE);
- }
- delete mParams.mRequestDatap;
- mParams.mRequestDatap = NULL;
- }
-}
+/** + * @file lltransfertargetvfile.cpp + * @brief Transfer system for receiving a vfile. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lltransfertargetvfile.h" + +#include "lldatapacker.h" +#include "llerror.h" +#include "llfilesystem.h" + +//static +void LLTransferTargetVFile::updateQueue(bool shutdown) +{ +} + + +LLTransferTargetParamsVFile::LLTransferTargetParamsVFile() : + LLTransferTargetParams(LLTTT_VFILE), + mAssetType(LLAssetType::AT_NONE), + mCompleteCallback(NULL), + mRequestDatap(NULL), + mErrCode(0) +{ +} + +void LLTransferTargetParamsVFile::setAsset( + const LLUUID& asset_id, + LLAssetType::EType asset_type) +{ + mAssetID = asset_id; + mAssetType = asset_type; +} + +void LLTransferTargetParamsVFile::setCallback(LLTTVFCompleteCallback cb, LLBaseDownloadRequest& request) +{ + mCompleteCallback = cb; + if (mRequestDatap) + { + delete mRequestDatap; + } + mRequestDatap = request.getCopy(); +} + +bool LLTransferTargetParamsVFile::unpackParams(LLDataPacker& dp) +{ + // if the source provided a new key, assign that to the asset id. + if(dp.hasNext()) + { + LLUUID dummy_id; + dp.unpackUUID(dummy_id, "AgentID"); + dp.unpackUUID(dummy_id, "SessionID"); + dp.unpackUUID(dummy_id, "OwnerID"); + dp.unpackUUID(dummy_id, "TaskID"); + dp.unpackUUID(dummy_id, "ItemID"); + dp.unpackUUID(mAssetID, "AssetID"); + S32 dummy_type; + dp.unpackS32(dummy_type, "AssetType"); + } + + // if we never got an asset id, this will always fail. + if(mAssetID.isNull()) + { + return false; + } + return true; +} + + +LLTransferTargetVFile::LLTransferTargetVFile( + const LLUUID& uuid, + LLTransferSourceType src_type) : + LLTransferTarget(LLTTT_VFILE, uuid, src_type), + mNeedsCreate(true) +{ + mTempID.generate(); +} + + +LLTransferTargetVFile::~LLTransferTargetVFile() +{ + if (mParams.mRequestDatap) + { + // TODO: Consider doing it in LLTransferTargetParamsVFile's destructor + delete mParams.mRequestDatap; + mParams.mRequestDatap = NULL; + } +} + + +// virtual +bool LLTransferTargetVFile::unpackParams(LLDataPacker& dp) +{ + if(LLTST_SIM_INV_ITEM == mSourceType) + { + return mParams.unpackParams(dp); + } + return true; +} + +void LLTransferTargetVFile::applyParams(const LLTransferTargetParams ¶ms) +{ + if (params.getType() != mType) + { + LL_WARNS() << "Target parameter type doesn't match!" << LL_ENDL; + return; + } + + mParams = (LLTransferTargetParamsVFile &)params; +} + + +LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size) +{ + //LL_INFOS() << "LLTransferTargetFile::dataCallback" << LL_ENDL; + //LL_INFOS() << "Packet: " << packet_id << LL_ENDL; + + LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND); + if (mNeedsCreate) + { + mNeedsCreate = false; + } + + if (!in_size) + { + return LLTS_OK; + } + + if (!vf.write(in_datap, in_size)) + { + LL_WARNS() << "Failure in LLTransferTargetVFile::dataCallback!" << LL_ENDL; + return LLTS_ERROR; + } + return LLTS_OK; +} + + +void LLTransferTargetVFile::completionCallback(const LLTSCode status) +{ + //LL_INFOS() << "LLTransferTargetVFile::completionCallback" << LL_ENDL; + + if (!gAssetStorage) + { + LL_WARNS() << "Aborting vfile transfer after asset storage shut down!" << LL_ENDL; + return; + } + + // Still need to gracefully handle error conditions. + S32 err_code = 0; + switch (status) + { + case LLTS_DONE: + if (!mNeedsCreate) + { + LLFileSystem file(mTempID, mParams.getAssetType(), LLFileSystem::WRITE); + if (!file.rename(mParams.getAssetID(), mParams.getAssetType())) + { + LL_ERRS() << "LLTransferTargetVFile: rename failed" << LL_ENDL; + } + } + err_code = LL_ERR_NOERR; + LL_DEBUGS() << "LLTransferTargetVFile::completionCallback for " + << mParams.getAssetID() << "," + << LLAssetType::lookup(mParams.getAssetType()) + << " with temp id " << mTempID << LL_ENDL; + 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. + LL_WARNS() << "Aborting vfile transfer for " << mParams.getAssetID() << LL_ENDL; + LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::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.mRequestDatap) + { + if (mParams.mCompleteCallback) + { + mParams.mCompleteCallback(err_code, + mParams.getAssetID(), + mParams.getAssetType(), + mParams.mRequestDatap, + LLExtStat::NONE); + } + delete mParams.mRequestDatap; + mParams.mRequestDatap = NULL; + } +} diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h index e1d48ce73d..4a78c3c522 100644 --- a/indra/llmessage/lltransfertargetvfile.h +++ b/indra/llmessage/lltransfertargetvfile.h @@ -1,93 +1,93 @@ -/**
- * @file lltransfertargetvfile.h
- * @brief Transfer system for receiving a vfile.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTRANSFERTARGETVFILE_H
-#define LL_LLTRANSFERTARGETVFILE_H
-
-#include "lltransfermanager.h"
-#include "llassetstorage.h"
-#include "llfilesystem.h"
-
-class LLFileSystem;
-
-// Lame, an S32 for now until I figure out the deal with how we want to do
-// error codes.
-typedef void (*LLTTVFCompleteCallback)(
- S32 status,
- const LLUUID& file_id,
- LLAssetType::EType file_type,
- LLBaseDownloadRequest* user_data, LLExtStat ext_status );
-
-class LLTransferTargetParamsVFile : public LLTransferTargetParams
-{
-public:
- LLTransferTargetParamsVFile();
-
- void setAsset(const LLUUID& asset_id, LLAssetType::EType asset_type);
- void setCallback(LLTTVFCompleteCallback cb, LLBaseDownloadRequest& request);
-
- LLUUID getAssetID() const { return mAssetID; }
- LLAssetType::EType getAssetType() const { return mAssetType; }
-
- friend class LLTransferTargetVFile;
-protected:
- bool unpackParams(LLDataPacker& dp);
-
- LLUUID mAssetID;
- LLAssetType::EType mAssetType;
-
- LLTTVFCompleteCallback mCompleteCallback;
- LLBaseDownloadRequest* mRequestDatap;
- S32 mErrCode;
-};
-
-
-class LLTransferTargetVFile : public LLTransferTarget
-{
-public:
- LLTransferTargetVFile(const LLUUID& uuid, LLTransferSourceType src_type);
- 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 bool unpackParams(LLDataPacker& dp);
- /*virtual*/ void applyParams(const LLTransferTargetParams& params);
- /*virtual*/ LLTSCode dataCallback(const S32 packet_id, U8* in_datap, const S32 in_size);
- /*virtual*/ void completionCallback(const LLTSCode status);
-
- LLTransferTargetParamsVFile mParams;
-
- bool mNeedsCreate;
- LLUUID mTempID;
-};
-
-#endif // LL_LLTRANSFERTARGETFILE_H
+/** + * @file lltransfertargetvfile.h + * @brief Transfer system for receiving a vfile. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTRANSFERTARGETVFILE_H +#define LL_LLTRANSFERTARGETVFILE_H + +#include "lltransfermanager.h" +#include "llassetstorage.h" +#include "llfilesystem.h" + +class LLFileSystem; + +// Lame, an S32 for now until I figure out the deal with how we want to do +// error codes. +typedef void (*LLTTVFCompleteCallback)( + S32 status, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, LLExtStat ext_status ); + +class LLTransferTargetParamsVFile : public LLTransferTargetParams +{ +public: + LLTransferTargetParamsVFile(); + + void setAsset(const LLUUID& asset_id, LLAssetType::EType asset_type); + void setCallback(LLTTVFCompleteCallback cb, LLBaseDownloadRequest& request); + + LLUUID getAssetID() const { return mAssetID; } + LLAssetType::EType getAssetType() const { return mAssetType; } + + friend class LLTransferTargetVFile; +protected: + bool unpackParams(LLDataPacker& dp); + + LLUUID mAssetID; + LLAssetType::EType mAssetType; + + LLTTVFCompleteCallback mCompleteCallback; + LLBaseDownloadRequest* mRequestDatap; + S32 mErrCode; +}; + + +class LLTransferTargetVFile : public LLTransferTarget +{ +public: + LLTransferTargetVFile(const LLUUID& uuid, LLTransferSourceType src_type); + 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 bool unpackParams(LLDataPacker& dp); + /*virtual*/ void applyParams(const LLTransferTargetParams& params); + /*virtual*/ LLTSCode dataCallback(const S32 packet_id, U8* in_datap, const S32 in_size); + /*virtual*/ void completionCallback(const LLTSCode status); + + LLTransferTargetParamsVFile mParams; + + bool mNeedsCreate; + LLUUID mTempID; +}; + +#endif // LL_LLTRANSFERTARGETFILE_H diff --git a/indra/llmessage/lluseroperation.cpp b/indra/llmessage/lluseroperation.cpp index f7897f9754..36ab87086d 100644 --- a/indra/llmessage/lluseroperation.cpp +++ b/indra/llmessage/lluseroperation.cpp @@ -1,190 +1,190 @@ -/**
- * @file lluseroperation.cpp
- * @brief LLUserOperation class definition.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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(),
- mNoExpire(false)
-{
- mTransactionID.generate();
-}
-
-LLUserOperation::LLUserOperation(const LLUUID& agent_id,
- const LLUUID& transaction_id) :
- mAgentID(agent_id),
- mTransactionID(transaction_id),
- mTimer(),
- mNoExpire(false)
-{
-}
-
-// protected constructor which is used by base classes that determine
-// transaction, agent, et. after construction.
-LLUserOperation::LLUserOperation() :
- mTimer(),
- mNoExpire(false)
-{
-}
-
-LLUserOperation::~LLUserOperation()
-{
-}
-
-void LLUserOperation::SetNoExpireFlag(const bool flag)
-{
- mNoExpire = flag;
-}
-
-bool LLUserOperation::isExpired()
-{
- if (!mNoExpire)
- {
- const F32 EXPIRE_TIME_SECS = 10.f;
- return mTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS;
- }
- return false;
-}
-
-void LLUserOperation::expire()
-{
- // by default, do do anything.
-}
-
-///----------------------------------------------------------------------------
-/// Class LLUserOperationMgr
-///----------------------------------------------------------------------------
-
-LLUserOperationMgr::LLUserOperationMgr()
-{
-}
-
-
-LLUserOperationMgr::~LLUserOperationMgr()
-{
- if (mUserOperationList.size() > 0)
- {
- LL_WARNS() << "Exiting with user operations pending." << LL_ENDL;
- }
-}
-
-
-void LLUserOperationMgr::addOperation(LLUserOperation* op)
-{
- if(!op)
- {
- LL_WARNS() << "Tried to add null op" << LL_ENDL;
- 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 != 0;
-}
-
-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())
- {
- LL_DEBUGS() << "expiring: " << (*it).first << LL_ENDL;
- 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
-///----------------------------------------------------------------------------
+/** + * @file lluseroperation.cpp + * @brief LLUserOperation class definition. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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(), + mNoExpire(false) +{ + mTransactionID.generate(); +} + +LLUserOperation::LLUserOperation(const LLUUID& agent_id, + const LLUUID& transaction_id) : + mAgentID(agent_id), + mTransactionID(transaction_id), + mTimer(), + mNoExpire(false) +{ +} + +// protected constructor which is used by base classes that determine +// transaction, agent, et. after construction. +LLUserOperation::LLUserOperation() : + mTimer(), + mNoExpire(false) +{ +} + +LLUserOperation::~LLUserOperation() +{ +} + +void LLUserOperation::SetNoExpireFlag(const bool flag) +{ + mNoExpire = flag; +} + +bool LLUserOperation::isExpired() +{ + if (!mNoExpire) + { + const F32 EXPIRE_TIME_SECS = 10.f; + return mTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS; + } + return false; +} + +void LLUserOperation::expire() +{ + // by default, do do anything. +} + +///---------------------------------------------------------------------------- +/// Class LLUserOperationMgr +///---------------------------------------------------------------------------- + +LLUserOperationMgr::LLUserOperationMgr() +{ +} + + +LLUserOperationMgr::~LLUserOperationMgr() +{ + if (mUserOperationList.size() > 0) + { + LL_WARNS() << "Exiting with user operations pending." << LL_ENDL; + } +} + + +void LLUserOperationMgr::addOperation(LLUserOperation* op) +{ + if(!op) + { + LL_WARNS() << "Tried to add null op" << LL_ENDL; + 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 != 0; +} + +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()) + { + LL_DEBUGS() << "expiring: " << (*it).first << LL_ENDL; + 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 index 384978571c..c3ac2e350c 100644 --- a/indra/llmessage/lluseroperation.h +++ b/indra/llmessage/lluseroperation.h @@ -1,97 +1,97 @@ -/**
- * @file lluseroperation.h
- * @brief LLUserOperation class header file - used for message based
- * transaction. For example, L$ transactions.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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();
-
- // ability to mark this operation as never expiring.
- void SetNoExpireFlag(const bool flag);
-
- // 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;
- bool mNoExpire; // this is used for operations that expect an answer and will wait till it gets one.
-};
-
-
-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
+/** + * @file lluseroperation.h + * @brief LLUserOperation class header file - used for message based + * transaction. For example, L$ transactions. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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(); + + // ability to mark this operation as never expiring. + void SetNoExpireFlag(const bool flag); + + // 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; + bool mNoExpire; // this is used for operations that expect an answer and will wait till it gets one. +}; + + +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/llxfer.cpp b/indra/llmessage/llxfer.cpp index 057f4de0ab..358c8be671 100644 --- a/indra/llmessage/llxfer.cpp +++ b/indra/llmessage/llxfer.cpp @@ -1,388 +1,388 @@ -/**
- * @file llxfer.cpp
- * @brief implementation of LLXfer class for a single xfer.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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 ()
-{
- cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-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;
- mWaitingForACK = false;
-
- mCallback = NULL;
- mCallbackDataHandle = NULL;
- mCallbackResult = 0;
-
- 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::cleanup ()
-{
- if (mBuffer)
- {
- delete[] mBuffer;
- mBuffer = NULL;
- }
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer::startSend (U64 xfer_id, const LLHost &remote_host)
-{
- LL_WARNS("Xfer") << "unexpected call to base class LLXfer::startSend for " << getFileName() << LL_ENDL;
- return (-1);
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer::closeFileHandle()
-{
- LL_WARNS("Xfer") << "unexpected call to base class LLXfer::closeFileHandle for " << getFileName() << LL_ENDL;
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer::reopenFileHandle()
-{
- LL_WARNS("Xfer") << "unexpected call to base class LLXfer::reopenFileHandle for " << getFileName() << LL_ENDL;
- return (-1);
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer::setXferSize (S32 xfer_size)
-{
- mXferSize = xfer_size;
-// cout << "starting transfer of size: " << xfer_size << endl;
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer::startDownload()
-{
- LL_WARNS("Xfer") << "undifferentiated LLXfer::startDownload for " << getFileName()
- << LL_ENDL;
- return (-1);
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer::receiveData (char *datap, S32 data_size)
-{
- S32 retval = 0;
-
- if (((S32) mBufferLength + data_size) > getMaxBufferSize())
- { // Write existing data to disk if it's larger than the buffer size
- retval = flush();
- }
-
- if (!retval)
- {
- if (datap != NULL)
- { // Append new data to mBuffer
- memcpy(&mBuffer[mBufferLength],datap,data_size); /*Flawfinder: ignore*/
- mBufferLength += data_size;
- }
- else
- {
- LL_ERRS("Xfer") << "NULL data passed in receiveData" << LL_ENDL;
- }
- }
-
- 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)
-{
- LL_WARNS("Xfer") << "Attempted to send a packet outside the buffer bounds in LLXfer::suck()" << LL_ENDL;
- 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)
- {
- LL_WARNS("Xfer") << "negative data size in xfer send, aborting" << LL_ENDL;
- 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)(mBufferLength - desired_read_position));
- if (num_copy > 0)
- {
- memcpy(fdata_buf,&mBuffer[desired_read_position],num_copy); /*Flawfinder: ignore*/
- }
- }
- 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)(mBufferLength - desired_read_position));
- if (num_copy > 0)
- {
- memcpy( /*Flawfinder: ignore*/
- fdata_buf + sizeof(S32),
- &mBuffer[desired_read_position],
- num_copy);
- }
- fdata_size += sizeof(S32);
- htolememcpy(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);
-
- S32 sent_something = gMessageSystem->sendMessage(mRemoteHost);
- if (sent_something == 0)
- {
- abort(LL_ERR_CIRCUIT_GONE);
- return;
- }
-
- 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)
- {
- LL_INFOS("Xfer") << "xfer from " << mRemoteHost << " complete: " << getFileName()
- << LL_ENDL;
- }
- else
- {
- LL_INFOS("Xfer") << "xfer from " << mRemoteHost << " failed, code "
- << mCallbackResult << ": " << getFileName() << LL_ENDL;
- }
-
- if (mCallback)
- {
- mCallback(mCallbackDataHandle,mCallbackResult, LLExtStat::NONE);
- }
-
- 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;
-
- LL_INFOS("Xfer") << "Aborting xfer from " << mRemoteHost << " named " << getFileName()
- << " - error: " << result_code << LL_ENDL;
-
- if (result_code != LL_ERR_CIRCUIT_GONE)
- {
- 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;
-}
-
-
-///////////////////////////////////////////////////////////
-
-std::string LLXfer::getFileName()
-{
- return U64_to_str(mID);
-}
-
-///////////////////////////////////////////////////////////
-
-U32 LLXfer::getXferTypeTag()
-{
- return 0;
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer::getMaxBufferSize ()
-{
- return(mXferSize);
-}
-
-
-std::ostream& operator<< (std::ostream& os, LLXfer &hh)
-{
- os << hh.getFileName() ;
- return os;
-}
+/** + * @file llxfer.cpp + * @brief implementation of LLXfer class for a single xfer. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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 () +{ + cleanup(); +} + +/////////////////////////////////////////////////////////// + +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; + mWaitingForACK = false; + + mCallback = NULL; + mCallbackDataHandle = NULL; + mCallbackResult = 0; + + 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::cleanup () +{ + if (mBuffer) + { + delete[] mBuffer; + mBuffer = NULL; + } +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer::startSend (U64 xfer_id, const LLHost &remote_host) +{ + LL_WARNS("Xfer") << "unexpected call to base class LLXfer::startSend for " << getFileName() << LL_ENDL; + return (-1); +} + +/////////////////////////////////////////////////////////// + +void LLXfer::closeFileHandle() +{ + LL_WARNS("Xfer") << "unexpected call to base class LLXfer::closeFileHandle for " << getFileName() << LL_ENDL; +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer::reopenFileHandle() +{ + LL_WARNS("Xfer") << "unexpected call to base class LLXfer::reopenFileHandle for " << getFileName() << LL_ENDL; + return (-1); +} + +/////////////////////////////////////////////////////////// + +void LLXfer::setXferSize (S32 xfer_size) +{ + mXferSize = xfer_size; +// cout << "starting transfer of size: " << xfer_size << endl; +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer::startDownload() +{ + LL_WARNS("Xfer") << "undifferentiated LLXfer::startDownload for " << getFileName() + << LL_ENDL; + return (-1); +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer::receiveData (char *datap, S32 data_size) +{ + S32 retval = 0; + + if (((S32) mBufferLength + data_size) > getMaxBufferSize()) + { // Write existing data to disk if it's larger than the buffer size + retval = flush(); + } + + if (!retval) + { + if (datap != NULL) + { // Append new data to mBuffer + memcpy(&mBuffer[mBufferLength],datap,data_size); /*Flawfinder: ignore*/ + mBufferLength += data_size; + } + else + { + LL_ERRS("Xfer") << "NULL data passed in receiveData" << LL_ENDL; + } + } + + 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) +{ + LL_WARNS("Xfer") << "Attempted to send a packet outside the buffer bounds in LLXfer::suck()" << LL_ENDL; + 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) + { + LL_WARNS("Xfer") << "negative data size in xfer send, aborting" << LL_ENDL; + 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)(mBufferLength - desired_read_position)); + if (num_copy > 0) + { + memcpy(fdata_buf,&mBuffer[desired_read_position],num_copy); /*Flawfinder: ignore*/ + } + } + 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)(mBufferLength - desired_read_position)); + if (num_copy > 0) + { + memcpy( /*Flawfinder: ignore*/ + fdata_buf + sizeof(S32), + &mBuffer[desired_read_position], + num_copy); + } + fdata_size += sizeof(S32); + htolememcpy(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); + + S32 sent_something = gMessageSystem->sendMessage(mRemoteHost); + if (sent_something == 0) + { + abort(LL_ERR_CIRCUIT_GONE); + return; + } + + 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) + { + LL_INFOS("Xfer") << "xfer from " << mRemoteHost << " complete: " << getFileName() + << LL_ENDL; + } + else + { + LL_INFOS("Xfer") << "xfer from " << mRemoteHost << " failed, code " + << mCallbackResult << ": " << getFileName() << LL_ENDL; + } + + if (mCallback) + { + mCallback(mCallbackDataHandle,mCallbackResult, LLExtStat::NONE); + } + + 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; + + LL_INFOS("Xfer") << "Aborting xfer from " << mRemoteHost << " named " << getFileName() + << " - error: " << result_code << LL_ENDL; + + if (result_code != LL_ERR_CIRCUIT_GONE) + { + 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; +} + + +/////////////////////////////////////////////////////////// + +std::string LLXfer::getFileName() +{ + return U64_to_str(mID); +} + +/////////////////////////////////////////////////////////// + +U32 LLXfer::getXferTypeTag() +{ + return 0; +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer::getMaxBufferSize () +{ + return(mXferSize); +} + + +std::ostream& operator<< (std::ostream& os, LLXfer &hh) +{ + os << hh.getFileName() ; + return os; +} diff --git a/indra/llmessage/llxfer.h b/indra/llmessage/llxfer.h index 22cf05ba5a..9a0901a84a 100644 --- a/indra/llmessage/llxfer.h +++ b/indra/llmessage/llxfer.h @@ -1,123 +1,123 @@ -/**
- * @file llxfer.h
- * @brief definition of LLXfer class for a single xfer
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLXFER_H
-#define LL_LLXFER_H
-
-#include "message.h"
-#include "lltimer.h"
-#include "llextendedstatus.h"
-
-const S32 LL_XFER_LARGE_PAYLOAD = 7680;
-const S32 LL_ERR_FILE_EMPTY = -44;
-const int LL_ERR_FILE_NOT_FOUND = -43;
-const int LL_ERR_CANNOT_OPEN_FILE = -42;
-const int LL_ERR_EOF = -39;
-
-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:
- U64 mID;
- S32 mPacketNum;
-
- LLHost mRemoteHost;
- S32 mXferSize;
-
- char *mBuffer;
- U32 mBufferLength; // Size of valid data, not actual allocated buffer size
- U32 mBufferStartOffset;
- bool mBufferContainsEOF;
-
- ELLXferStatus mStatus;
-
- bool mWaitingForACK;
-
- void (*mCallback)(void **,S32,LLExtStat);
- 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 cleanup();
-
- virtual S32 startSend(U64 xfer_id, const LLHost &remote_host);
- virtual void closeFileHandle();
- virtual S32 reopenFileHandle();
- 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 std::string getFileName();
-
- virtual U32 getXferTypeTag();
-
- friend std::ostream& operator<< (std::ostream& os, LLXfer &hh);
-
-};
-
-#endif
-
-
-
-
-
+/** + * @file llxfer.h + * @brief definition of LLXfer class for a single xfer + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLXFER_H +#define LL_LLXFER_H + +#include "message.h" +#include "lltimer.h" +#include "llextendedstatus.h" + +const S32 LL_XFER_LARGE_PAYLOAD = 7680; +const S32 LL_ERR_FILE_EMPTY = -44; +const int LL_ERR_FILE_NOT_FOUND = -43; +const int LL_ERR_CANNOT_OPEN_FILE = -42; +const int LL_ERR_EOF = -39; + +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: + U64 mID; + S32 mPacketNum; + + LLHost mRemoteHost; + S32 mXferSize; + + char *mBuffer; + U32 mBufferLength; // Size of valid data, not actual allocated buffer size + U32 mBufferStartOffset; + bool mBufferContainsEOF; + + ELLXferStatus mStatus; + + bool mWaitingForACK; + + void (*mCallback)(void **,S32,LLExtStat); + 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 cleanup(); + + virtual S32 startSend(U64 xfer_id, const LLHost &remote_host); + virtual void closeFileHandle(); + virtual S32 reopenFileHandle(); + 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 std::string getFileName(); + + 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 index a9450f725b..ad15d5969b 100644 --- a/indra/llmessage/llxfer_file.cpp +++ b/indra/llmessage/llxfer_file.cpp @@ -1,475 +1,475 @@ -/**
- * @file llxfer_file.cpp
- * @brief implementation of LLXfer_File class for a single xfer (file)
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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 std::string& from, const std::string& to);
-
-///////////////////////////////////////////////////////////
-
-LLXfer_File::LLXfer_File (S32 chunk_size)
-: LLXfer(chunk_size)
-{
- init(LLStringUtil::null, false, chunk_size);
-}
-
-LLXfer_File::LLXfer_File (const std::string& 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 ()
-{
- cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_File::init (const std::string& local_filename, bool delete_local_on_completion, S32 chunk_size)
-{
-
- mFp = NULL;
- mLocalFilename.clear();
- mRemoteFilename.clear();
- mRemotePath = LL_PATH_NONE;
- mTempFilename.clear();
- mDeleteLocalOnCompletion = false;
- mDeleteRemoteOnCompletion = false;
-
- if (!local_filename.empty())
- {
- mLocalFilename = local_filename.substr(0,LL_MAX_PATH-1);
-
- // You can only automatically delete .tmp file as a safeguard against nasty messages.
- std::string exten = mLocalFilename.substr(mLocalFilename.length()-4, 4);
- mDeleteLocalOnCompletion = (delete_local_on_completion && exten == ".tmp");
- }
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_File::cleanup ()
-{
- if (mFp)
- {
- fclose(mFp);
- mFp = NULL;
- }
-
- LLFile::remove(mTempFilename, ENOENT);
-
- if (mDeleteLocalOnCompletion)
- {
- LL_DEBUGS("Xfer") << "Removing file: " << mLocalFilename << LL_ENDL;
- LLFile::remove(mLocalFilename, ENOENT);
- }
- else
- {
- LL_DEBUGS("Xfer") << "Keeping local file: " << mLocalFilename << LL_ENDL;
- }
-
- LLXfer::cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer_File::initializeRequest(U64 xfer_id,
- const std::string& local_filename,
- const std::string& remote_filename,
- ELLPath remote_path,
- const LLHost& remote_host,
- bool delete_remote_on_completion,
- void (*callback)(void**,S32,LLExtStat),
- void** user_data)
-{
- S32 retval = 0; // presume success
-
- mID = xfer_id;
- mLocalFilename = local_filename;
- mRemoteFilename = remote_filename;
- mRemotePath = remote_path;
- mRemoteHost = remote_host;
- mDeleteRemoteOnCompletion = delete_remote_on_completion;
-
- mTempFilename = gDirUtilp->getTempFilename();
-
- mCallback = callback;
- mCallbackDataHandle = user_data;
- mCallbackResult = LL_ERR_NOERR;
-
- LL_INFOS("Xfer") << "Requesting xfer from " << remote_host << " for file: " << mLocalFilename << LL_ENDL;
-
- 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;
-
- // tbd - is it premature to send this message if the queue is backed up?
- 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", 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
- {
- LL_WARNS("Xfer") << "Couldn't create file to be received!" << LL_ENDL;
- 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;
-
- // We leave the file open, assuming we'll start reading and sending soon
- 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
- {
- LL_INFOS("Xfer") << "Warning: " << mLocalFilename << " not found." << LL_ENDL;
- return (LL_ERR_FILE_NOT_FOUND);
- }
-
- mStatus = e_LL_XFER_PENDING;
-
- return (retval);
-}
-
-///////////////////////////////////////////////////////////
-void LLXfer_File::closeFileHandle()
-{
- if (mFp)
- {
- fclose(mFp);
- mFp = NULL;
- }
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer_File::reopenFileHandle()
-{
- S32 retval = LL_ERR_NOERR; // presume success
-
- if (mFp == NULL)
- {
- mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */
- if (mFp == NULL)
- {
- LL_INFOS("Xfer") << "Warning: " << mLocalFilename << " not found when re-opening file" << LL_ENDL;
- retval = LL_ERR_FILE_NOT_FOUND;
- }
- }
-
- 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)
- {
- LL_ERRS("Xfer") << "Overwriting open file pointer!" << LL_ENDL;
- }
- mFp = LLFile::fopen(mTempFilename,"a+b"); /* Flawfinder : ignore */
-
- if (mFp)
- {
- S32 write_size = fwrite(mBuffer,1,mBufferLength,mFp);
- if (write_size != mBufferLength)
- {
- LL_WARNS("Xfer") << "Non-matching write size, requested " << mBufferLength
- << " but wrote " << write_size
- << LL_ENDL;
- }
-
-// LL_INFOS("Xfer") << "******* wrote " << mBufferLength << " bytes of file xfer" << LL_ENDL;
- fclose(mFp);
- mFp = NULL;
-
- mBufferLength = 0;
- }
- else
- {
- LL_WARNS("Xfer") << "LLXfer_File::flush() unable to open " << mTempFilename << " for writing!" << LL_ENDL;
- 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, ENOENT);
-
- if (!mCallbackResult)
- {
- if (LLFile::rename(mTempFilename,mLocalFilename))
- {
-#if !LL_WINDOWS
- S32 error_number = errno;
- LL_INFOS("Xfer") << "Rename failure (" << error_number << ") - "
- << mTempFilename << " to " << mLocalFilename << LL_ENDL;
- if(EXDEV == error_number)
- {
- if(copy_file(mTempFilename, mLocalFilename) == 0)
- {
- LL_INFOS("Xfer") << "Rename across mounts; copying+unlinking the file instead." << LL_ENDL;
- unlink(mTempFilename.c_str());
- }
- else
- {
- LL_WARNS("Xfer") << "Copy failure - " << mTempFilename << " to "
- << mLocalFilename << LL_ENDL;
- }
- }
- else
- {
- //LLFILE* fp = LLFile::fopen(mTempFilename, "r");
- //LL_WARNS() << "File " << mTempFilename << " does "
- // << (!fp ? "not" : "" ) << " exit." << LL_ENDL;
- //if(fp) fclose(fp);
- //fp = LLFile::fopen(mLocalFilename, "r");
- //LL_WARNS() << "File " << mLocalFilename << " does "
- // << (!fp ? "not" : "" ) << " exit." << LL_ENDL;
- //if(fp) fclose(fp);
- LL_WARNS("Xfer") << "Rename fatally failed, can only handle EXDEV ("
- << EXDEV << ")" << LL_ENDL;
- }
-#else
- LL_WARNS("Xfer") << "Rename failure - " << mTempFilename << " to "
- << mLocalFilename << LL_ENDL;
-#endif
- }
- }
-
- if (mFp)
- {
- fclose(mFp);
- mFp = NULL;
- }
-
- retval = LLXfer::processEOF();
-
- return(retval);
-}
-
-///////////////////////////////////////////////////////////
-
-bool LLXfer_File::matchesLocalFilename(const std::string& filename)
-{
- return (filename == mLocalFilename);
-}
-
-///////////////////////////////////////////////////////////
-
-bool LLXfer_File::matchesRemoteFilename(const std::string& filename, ELLPath remote_path)
-{
- return ((filename == mRemoteFilename) && (remote_path == mRemotePath));
-}
-
-
-///////////////////////////////////////////////////////////
-
-std::string LLXfer_File::getFileName()
-{
- 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 std::string& from, const std::string& to)
-{
- S32 rv = 0;
- LLFILE* in = LLFile::fopen(from, "rb"); /*Flawfinder: ignore*/
- LLFILE* out = LLFile::fopen(to, "wb"); /*Flawfinder: ignore*/
- 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
-
+/** + * @file llxfer_file.cpp + * @brief implementation of LLXfer_File class for a single xfer (file) + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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 std::string& from, const std::string& to); + +/////////////////////////////////////////////////////////// + +LLXfer_File::LLXfer_File (S32 chunk_size) +: LLXfer(chunk_size) +{ + init(LLStringUtil::null, false, chunk_size); +} + +LLXfer_File::LLXfer_File (const std::string& 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 () +{ + cleanup(); +} + +/////////////////////////////////////////////////////////// + +void LLXfer_File::init (const std::string& local_filename, bool delete_local_on_completion, S32 chunk_size) +{ + + mFp = NULL; + mLocalFilename.clear(); + mRemoteFilename.clear(); + mRemotePath = LL_PATH_NONE; + mTempFilename.clear(); + mDeleteLocalOnCompletion = false; + mDeleteRemoteOnCompletion = false; + + if (!local_filename.empty()) + { + mLocalFilename = local_filename.substr(0,LL_MAX_PATH-1); + + // You can only automatically delete .tmp file as a safeguard against nasty messages. + std::string exten = mLocalFilename.substr(mLocalFilename.length()-4, 4); + mDeleteLocalOnCompletion = (delete_local_on_completion && exten == ".tmp"); + } +} + +/////////////////////////////////////////////////////////// + +void LLXfer_File::cleanup () +{ + if (mFp) + { + fclose(mFp); + mFp = NULL; + } + + LLFile::remove(mTempFilename, ENOENT); + + if (mDeleteLocalOnCompletion) + { + LL_DEBUGS("Xfer") << "Removing file: " << mLocalFilename << LL_ENDL; + LLFile::remove(mLocalFilename, ENOENT); + } + else + { + LL_DEBUGS("Xfer") << "Keeping local file: " << mLocalFilename << LL_ENDL; + } + + LLXfer::cleanup(); +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_File::initializeRequest(U64 xfer_id, + const std::string& local_filename, + const std::string& remote_filename, + ELLPath remote_path, + const LLHost& remote_host, + bool delete_remote_on_completion, + void (*callback)(void**,S32,LLExtStat), + void** user_data) +{ + S32 retval = 0; // presume success + + mID = xfer_id; + mLocalFilename = local_filename; + mRemoteFilename = remote_filename; + mRemotePath = remote_path; + mRemoteHost = remote_host; + mDeleteRemoteOnCompletion = delete_remote_on_completion; + + mTempFilename = gDirUtilp->getTempFilename(); + + mCallback = callback; + mCallbackDataHandle = user_data; + mCallbackResult = LL_ERR_NOERR; + + LL_INFOS("Xfer") << "Requesting xfer from " << remote_host << " for file: " << mLocalFilename << LL_ENDL; + + 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; + + // tbd - is it premature to send this message if the queue is backed up? + 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", 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 + { + LL_WARNS("Xfer") << "Couldn't create file to be received!" << LL_ENDL; + 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; + + // We leave the file open, assuming we'll start reading and sending soon + 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 + { + LL_INFOS("Xfer") << "Warning: " << mLocalFilename << " not found." << LL_ENDL; + return (LL_ERR_FILE_NOT_FOUND); + } + + mStatus = e_LL_XFER_PENDING; + + return (retval); +} + +/////////////////////////////////////////////////////////// +void LLXfer_File::closeFileHandle() +{ + if (mFp) + { + fclose(mFp); + mFp = NULL; + } +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_File::reopenFileHandle() +{ + S32 retval = LL_ERR_NOERR; // presume success + + if (mFp == NULL) + { + mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */ + if (mFp == NULL) + { + LL_INFOS("Xfer") << "Warning: " << mLocalFilename << " not found when re-opening file" << LL_ENDL; + retval = LL_ERR_FILE_NOT_FOUND; + } + } + + 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) + { + LL_ERRS("Xfer") << "Overwriting open file pointer!" << LL_ENDL; + } + mFp = LLFile::fopen(mTempFilename,"a+b"); /* Flawfinder : ignore */ + + if (mFp) + { + S32 write_size = fwrite(mBuffer,1,mBufferLength,mFp); + if (write_size != mBufferLength) + { + LL_WARNS("Xfer") << "Non-matching write size, requested " << mBufferLength + << " but wrote " << write_size + << LL_ENDL; + } + +// LL_INFOS("Xfer") << "******* wrote " << mBufferLength << " bytes of file xfer" << LL_ENDL; + fclose(mFp); + mFp = NULL; + + mBufferLength = 0; + } + else + { + LL_WARNS("Xfer") << "LLXfer_File::flush() unable to open " << mTempFilename << " for writing!" << LL_ENDL; + 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, ENOENT); + + if (!mCallbackResult) + { + if (LLFile::rename(mTempFilename,mLocalFilename)) + { +#if !LL_WINDOWS + S32 error_number = errno; + LL_INFOS("Xfer") << "Rename failure (" << error_number << ") - " + << mTempFilename << " to " << mLocalFilename << LL_ENDL; + if(EXDEV == error_number) + { + if(copy_file(mTempFilename, mLocalFilename) == 0) + { + LL_INFOS("Xfer") << "Rename across mounts; copying+unlinking the file instead." << LL_ENDL; + unlink(mTempFilename.c_str()); + } + else + { + LL_WARNS("Xfer") << "Copy failure - " << mTempFilename << " to " + << mLocalFilename << LL_ENDL; + } + } + else + { + //LLFILE* fp = LLFile::fopen(mTempFilename, "r"); + //LL_WARNS() << "File " << mTempFilename << " does " + // << (!fp ? "not" : "" ) << " exit." << LL_ENDL; + //if(fp) fclose(fp); + //fp = LLFile::fopen(mLocalFilename, "r"); + //LL_WARNS() << "File " << mLocalFilename << " does " + // << (!fp ? "not" : "" ) << " exit." << LL_ENDL; + //if(fp) fclose(fp); + LL_WARNS("Xfer") << "Rename fatally failed, can only handle EXDEV (" + << EXDEV << ")" << LL_ENDL; + } +#else + LL_WARNS("Xfer") << "Rename failure - " << mTempFilename << " to " + << mLocalFilename << LL_ENDL; +#endif + } + } + + if (mFp) + { + fclose(mFp); + mFp = NULL; + } + + retval = LLXfer::processEOF(); + + return(retval); +} + +/////////////////////////////////////////////////////////// + +bool LLXfer_File::matchesLocalFilename(const std::string& filename) +{ + return (filename == mLocalFilename); +} + +/////////////////////////////////////////////////////////// + +bool LLXfer_File::matchesRemoteFilename(const std::string& filename, ELLPath remote_path) +{ + return ((filename == mRemoteFilename) && (remote_path == mRemotePath)); +} + + +/////////////////////////////////////////////////////////// + +std::string LLXfer_File::getFileName() +{ + 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 std::string& from, const std::string& to) +{ + S32 rv = 0; + LLFILE* in = LLFile::fopen(from, "rb"); /*Flawfinder: ignore*/ + LLFILE* out = LLFile::fopen(to, "wb"); /*Flawfinder: ignore*/ + 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 index baf57753ae..f30ef3eed6 100644 --- a/indra/llmessage/llxfer_file.h +++ b/indra/llmessage/llxfer_file.h @@ -1,87 +1,87 @@ -/**
- * @file llxfer_file.h
- * @brief definition of LLXfer_File class for a single xfer_file.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLXFER_FILE_H
-#define LL_LLXFER_FILE_H
-
-#include "llxfer.h"
-#include "lldir.h"
-
-class LLXfer_File : public LLXfer
-{
- protected:
- LLFILE *mFp;
- std::string mLocalFilename;
- std::string mRemoteFilename;
- ELLPath mRemotePath;
- std::string mTempFilename;
-
- bool mDeleteLocalOnCompletion;
- bool mDeleteRemoteOnCompletion;
-
- public:
- LLXfer_File (S32 chunk_size);
- LLXfer_File (const std::string& local_filename, bool delete_local_on_completion, S32 chunk_size);
- virtual ~LLXfer_File();
-
- virtual void init(const std::string& local_filename, bool delete_local_on_completion, S32 chunk_size);
- virtual void cleanup();
-
- virtual S32 initializeRequest(U64 xfer_id,
- const std::string& local_filename,
- const std::string& remote_filename,
- ELLPath remote_path,
- const LLHost& remote_host,
- bool delete_remote_on_completion,
- void (*callback)(void**,S32,LLExtStat),
- void** user_data);
- virtual S32 startDownload();
-
- virtual S32 processEOF();
-
- virtual S32 startSend(U64 xfer_id, const LLHost &remote_host);
- virtual void closeFileHandle();
- virtual S32 reopenFileHandle();
-
- virtual S32 suck(S32 start_position);
- virtual S32 flush();
-
- virtual bool matchesLocalFilename(const std::string& filename);
- virtual bool matchesRemoteFilename(const std::string& filename, ELLPath remote_path);
-
- virtual S32 getMaxBufferSize();
-
- virtual U32 getXferTypeTag();
-
- virtual std::string getFileName();
-};
-
-#endif
-
-
-
-
-
+/** + * @file llxfer_file.h + * @brief definition of LLXfer_File class for a single xfer_file. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLXFER_FILE_H +#define LL_LLXFER_FILE_H + +#include "llxfer.h" +#include "lldir.h" + +class LLXfer_File : public LLXfer +{ + protected: + LLFILE *mFp; + std::string mLocalFilename; + std::string mRemoteFilename; + ELLPath mRemotePath; + std::string mTempFilename; + + bool mDeleteLocalOnCompletion; + bool mDeleteRemoteOnCompletion; + + public: + LLXfer_File (S32 chunk_size); + LLXfer_File (const std::string& local_filename, bool delete_local_on_completion, S32 chunk_size); + virtual ~LLXfer_File(); + + virtual void init(const std::string& local_filename, bool delete_local_on_completion, S32 chunk_size); + virtual void cleanup(); + + virtual S32 initializeRequest(U64 xfer_id, + const std::string& local_filename, + const std::string& remote_filename, + ELLPath remote_path, + const LLHost& remote_host, + bool delete_remote_on_completion, + void (*callback)(void**,S32,LLExtStat), + void** user_data); + virtual S32 startDownload(); + + virtual S32 processEOF(); + + virtual S32 startSend(U64 xfer_id, const LLHost &remote_host); + virtual void closeFileHandle(); + virtual S32 reopenFileHandle(); + + virtual S32 suck(S32 start_position); + virtual S32 flush(); + + virtual bool matchesLocalFilename(const std::string& filename); + virtual bool matchesRemoteFilename(const std::string& filename, ELLPath remote_path); + + virtual S32 getMaxBufferSize(); + + virtual U32 getXferTypeTag(); + + virtual std::string getFileName(); +}; + +#endif + + + + + diff --git a/indra/llmessage/llxfer_mem.cpp b/indra/llmessage/llxfer_mem.cpp index 825bff3820..b619974270 100644 --- a/indra/llmessage/llxfer_mem.cpp +++ b/indra/llmessage/llxfer_mem.cpp @@ -1,196 +1,196 @@ -/**
- * @file llxfer_mem.cpp
- * @brief implementation of LLXfer_Mem class for a single xfer
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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 ()
-{
- cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_Mem::init ()
-{
- mRemoteFilename.clear();
- mRemotePath = LL_PATH_NONE;
- mDeleteRemoteOnCompletion = false;
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_Mem::cleanup ()
-{
- LLXfer::cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-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;
-}
-
-///////////////////////////////////////////////////////////
-
-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: " << getFileName() << endl;
-
- mStatus = e_LL_XFER_PENDING;
-
- return (retval);
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer_Mem::processEOF()
-{
- S32 retval = 0;
-
- mStatus = e_LL_XFER_COMPLETE;
-
- LL_INFOS() << "xfer complete: " << getFileName() << LL_ENDL;
-
- if (mCallback)
- {
- mCallback((void *)mBuffer,mBufferLength,mCallbackDataHandle,mCallbackResult, LLExtStat::NONE);
- }
-
- 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,LLExtStat),
- 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;
-
- mRemoteFilename = remote_filename;
- mRemotePath = remote_path;
- mDeleteRemoteOnCompletion = delete_remote_on_completion;
-
- LL_INFOS() << "Requesting file: " << remote_filename << LL_ENDL;
-
- 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", 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;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+/** + * @file llxfer_mem.cpp + * @brief implementation of LLXfer_Mem class for a single xfer + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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 () +{ + cleanup(); +} + +/////////////////////////////////////////////////////////// + +void LLXfer_Mem::init () +{ + mRemoteFilename.clear(); + mRemotePath = LL_PATH_NONE; + mDeleteRemoteOnCompletion = false; +} + +/////////////////////////////////////////////////////////// + +void LLXfer_Mem::cleanup () +{ + LLXfer::cleanup(); +} + +/////////////////////////////////////////////////////////// + +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; +} + +/////////////////////////////////////////////////////////// + +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: " << getFileName() << endl; + + mStatus = e_LL_XFER_PENDING; + + return (retval); +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_Mem::processEOF() +{ + S32 retval = 0; + + mStatus = e_LL_XFER_COMPLETE; + + LL_INFOS() << "xfer complete: " << getFileName() << LL_ENDL; + + if (mCallback) + { + mCallback((void *)mBuffer,mBufferLength,mCallbackDataHandle,mCallbackResult, LLExtStat::NONE); + } + + 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,LLExtStat), + 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; + + mRemoteFilename = remote_filename; + mRemotePath = remote_path; + mDeleteRemoteOnCompletion = delete_remote_on_completion; + + LL_INFOS() << "Requesting file: " << remote_filename << LL_ENDL; + + 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", 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 index 49c3e8d20d..4e87b0ca5a 100644 --- a/indra/llmessage/llxfer_mem.h +++ b/indra/llmessage/llxfer_mem.h @@ -1,77 +1,77 @@ -/**
- * @file llxfer_mem.h
- * @brief definition of LLXfer_Mem class for a single xfer
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLXFER_MEM_H
-#define LL_LLXFER_MEM_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, LLExtStat);
- std::string mRemoteFilename;
- ELLPath mRemotePath;
- bool mDeleteRemoteOnCompletion;
-
- public:
-
- private:
- protected:
- public:
- LLXfer_Mem ();
- virtual ~LLXfer_Mem();
-
- virtual void init();
- virtual void cleanup();
-
- virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
- 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,LLExtStat),
- void** user_data);
- virtual S32 startDownload();
-
- virtual S32 processEOF();
-
- virtual U32 getXferTypeTag();
-};
-
-#endif
-
-
-
-
-
+/** + * @file llxfer_mem.h + * @brief definition of LLXfer_Mem class for a single xfer + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLXFER_MEM_H +#define LL_LLXFER_MEM_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, LLExtStat); + std::string mRemoteFilename; + ELLPath mRemotePath; + bool mDeleteRemoteOnCompletion; + + public: + + private: + protected: + public: + LLXfer_Mem (); + virtual ~LLXfer_Mem(); + + virtual void init(); + virtual void cleanup(); + + virtual S32 startSend (U64 xfer_id, const LLHost &remote_host); + 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,LLExtStat), + 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 index c37ee1ca2d..4f31973f3d 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -1,397 +1,397 @@ -/**
- * @file llxfer_vfile.cpp
- * @brief implementation of LLXfer_VFile class for a single xfer (vfile).
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llxfer_vfile.h"
-#include "lluuid.h"
-#include "llerror.h"
-#include "llmath.h"
-#include "llfilesystem.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(LLUUID::null, LLAssetType::AT_NONE);
-}
-
-LLXfer_VFile::LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type)
-: LLXfer(-1)
-{
- init(local_id, type);
-}
-
-///////////////////////////////////////////////////////////
-
-LLXfer_VFile::~LLXfer_VFile ()
-{
- cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_VFile::init (const LLUUID &local_id, LLAssetType::EType type)
-{
- mLocalID = local_id;
- mType = type;
-
- mVFile = NULL;
-
- std::string id_string;
- mLocalID.toString(id_string);
-
- mName = llformat("VFile %s:%s", id_string.c_str(), LLAssetType::lookup(mType));
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_VFile::cleanup ()
-{
- if (mTempID.notNull() &&
- mDeleteTempFile)
- {
- if (LLFileSystem::getExists(mTempID, mType))
- {
- LLFileSystem file(mTempID, mType, LLFileSystem::WRITE);
- file.remove();
- }
- else
- {
- LL_WARNS("Xfer") << "LLXfer_VFile::cleanup() can't open to delete cache file " << mTempID << "." << LLAssetType::lookup(mType)
- << ", mRemoteID is " << mRemoteID << LL_ENDL;
- }
- }
-
- delete mVFile;
- mVFile = NULL;
-
- LLXfer::cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer_VFile::initializeRequest(U64 xfer_id,
- const LLUUID& local_id,
- const LLUUID& remote_id,
- LLAssetType::EType type,
- const LLHost& remote_host,
- void (*callback)(void**,S32,LLExtStat),
- void** user_data)
-{
- S32 retval = 0; // presume success
-
- mRemoteHost = remote_host;
-
- mLocalID = local_id;
- mRemoteID = remote_id;
- mType = type;
-
- mID = xfer_id;
- mCallback = callback;
- mCallbackDataHandle = user_data;
- mCallbackResult = LL_ERR_NOERR;
-
- std::string id_string;
- mLocalID.toString(id_string);
-
- mName = llformat("VFile %s:%s", id_string.c_str(), LLAssetType::lookup(mType));
-
- LL_INFOS("Xfer") << "Requesting " << mName << LL_ENDL;
-
- if (mBuffer)
- {
- delete[] mBuffer;
- mBuffer = NULL;
- }
-
- mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
-
- mBufferLength = 0;
- mPacketNum = 0;
- mTempID.generate();
- mDeleteTempFile = true;
- mStatus = e_LL_XFER_PENDING;
- return retval;
-}
-
-//////////////////////////////////////////////////////////
-
-S32 LLXfer_VFile::startDownload()
-{
- S32 retval = 0; // presume success
-
- // Don't need to create the file here, it will happen when data arrives
-
- 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", 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(LLFileSystem::getExists(mLocalID, mType))
- {
- mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ);
-
- if (mVFile->getSize() <= 0)
- {
- LL_WARNS("Xfer") << "LLXfer_VFile::startSend() cache file " << mLocalID << "." << LLAssetType::lookup(mType)
- << " has unexpected file size of " << mVFile->getSize() << LL_ENDL;
- delete mVFile;
- mVFile = NULL;
-
- return LL_ERR_FILE_EMPTY;
- }
- }
-
- if(mVFile)
- {
- setXferSize(mVFile->getSize());
- mStatus = e_LL_XFER_PENDING;
- }
- else
- {
- LL_WARNS("Xfer") << "LLXfer_VFile::startSend() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL;
- retval = LL_ERR_FILE_NOT_FOUND;
- }
-
- return (retval);
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXfer_VFile::closeFileHandle()
-{
- if (mVFile)
- {
- delete mVFile;
- mVFile = NULL;
- }
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXfer_VFile::reopenFileHandle()
-{
- S32 retval = LL_ERR_NOERR; // presume success
-
- if (mVFile == NULL)
- {
- if (LLFileSystem::getExists(mLocalID, mType))
- {
- mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ);
- }
- else
- {
- LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL;
- 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)
- {
- LLFileSystem file(mTempID, mType, LLFileSystem::APPEND);
- }
-}
-
-///////////////////////////////////////////////////////////
-
-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))
- {
- LL_WARNS("Xfer") << "VFile Xfer Can't seek to position " << start_position << ", file length " << mVFile->getSize() << LL_ENDL;
- LL_WARNS("Xfer") << "While sending file " << mLocalID << LL_ENDL;
- 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)
- {
- LLFileSystem file(mTempID, mType, LLFileSystem::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)
- {
- if (LLFileSystem::getExists(mTempID, mType))
- {
- LLFileSystem file(mTempID, mType, LLFileSystem::WRITE);
- if (!file.rename(mLocalID, mType))
- {
- LL_WARNS("Xfer") << "Cache rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL;
- }
- else
- {
- // Rename worked: the original file is gone. Clear mDeleteTempFile
- // so we don't attempt to delete the file in cleanup()
- mDeleteTempFile = false;
- }
- }
- else
- {
- LL_WARNS("Xfer") << "LLXfer_VFile::processEOF() can't open for renaming cache file " << mTempID << "." << LLAssetType::lookup(mType) << LL_ENDL;
- }
- }
-
- 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);
-}
-
-//////////////////////////////////////////////////////////
-
-std::string LLXfer_VFile::getFileName()
-{
- 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;
-}
-
+/** + * @file llxfer_vfile.cpp + * @brief implementation of LLXfer_VFile class for a single xfer (vfile). + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llxfer_vfile.h" +#include "lluuid.h" +#include "llerror.h" +#include "llmath.h" +#include "llfilesystem.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(LLUUID::null, LLAssetType::AT_NONE); +} + +LLXfer_VFile::LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type) +: LLXfer(-1) +{ + init(local_id, type); +} + +/////////////////////////////////////////////////////////// + +LLXfer_VFile::~LLXfer_VFile () +{ + cleanup(); +} + +/////////////////////////////////////////////////////////// + +void LLXfer_VFile::init (const LLUUID &local_id, LLAssetType::EType type) +{ + mLocalID = local_id; + mType = type; + + mVFile = NULL; + + std::string id_string; + mLocalID.toString(id_string); + + mName = llformat("VFile %s:%s", id_string.c_str(), LLAssetType::lookup(mType)); +} + +/////////////////////////////////////////////////////////// + +void LLXfer_VFile::cleanup () +{ + if (mTempID.notNull() && + mDeleteTempFile) + { + if (LLFileSystem::getExists(mTempID, mType)) + { + LLFileSystem file(mTempID, mType, LLFileSystem::WRITE); + file.remove(); + } + else + { + LL_WARNS("Xfer") << "LLXfer_VFile::cleanup() can't open to delete cache file " << mTempID << "." << LLAssetType::lookup(mType) + << ", mRemoteID is " << mRemoteID << LL_ENDL; + } + } + + delete mVFile; + mVFile = NULL; + + LLXfer::cleanup(); +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_VFile::initializeRequest(U64 xfer_id, + const LLUUID& local_id, + const LLUUID& remote_id, + LLAssetType::EType type, + const LLHost& remote_host, + void (*callback)(void**,S32,LLExtStat), + void** user_data) +{ + S32 retval = 0; // presume success + + mRemoteHost = remote_host; + + mLocalID = local_id; + mRemoteID = remote_id; + mType = type; + + mID = xfer_id; + mCallback = callback; + mCallbackDataHandle = user_data; + mCallbackResult = LL_ERR_NOERR; + + std::string id_string; + mLocalID.toString(id_string); + + mName = llformat("VFile %s:%s", id_string.c_str(), LLAssetType::lookup(mType)); + + LL_INFOS("Xfer") << "Requesting " << mName << LL_ENDL; + + if (mBuffer) + { + delete[] mBuffer; + mBuffer = NULL; + } + + mBuffer = new char[LL_MAX_XFER_FILE_BUFFER]; + + mBufferLength = 0; + mPacketNum = 0; + mTempID.generate(); + mDeleteTempFile = true; + mStatus = e_LL_XFER_PENDING; + return retval; +} + +////////////////////////////////////////////////////////// + +S32 LLXfer_VFile::startDownload() +{ + S32 retval = 0; // presume success + + // Don't need to create the file here, it will happen when data arrives + + 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", 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(LLFileSystem::getExists(mLocalID, mType)) + { + mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ); + + if (mVFile->getSize() <= 0) + { + LL_WARNS("Xfer") << "LLXfer_VFile::startSend() cache file " << mLocalID << "." << LLAssetType::lookup(mType) + << " has unexpected file size of " << mVFile->getSize() << LL_ENDL; + delete mVFile; + mVFile = NULL; + + return LL_ERR_FILE_EMPTY; + } + } + + if(mVFile) + { + setXferSize(mVFile->getSize()); + mStatus = e_LL_XFER_PENDING; + } + else + { + LL_WARNS("Xfer") << "LLXfer_VFile::startSend() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + retval = LL_ERR_FILE_NOT_FOUND; + } + + return (retval); +} + +/////////////////////////////////////////////////////////// + +void LLXfer_VFile::closeFileHandle() +{ + if (mVFile) + { + delete mVFile; + mVFile = NULL; + } +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_VFile::reopenFileHandle() +{ + S32 retval = LL_ERR_NOERR; // presume success + + if (mVFile == NULL) + { + if (LLFileSystem::getExists(mLocalID, mType)) + { + mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ); + } + else + { + LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + 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) + { + LLFileSystem file(mTempID, mType, LLFileSystem::APPEND); + } +} + +/////////////////////////////////////////////////////////// + +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)) + { + LL_WARNS("Xfer") << "VFile Xfer Can't seek to position " << start_position << ", file length " << mVFile->getSize() << LL_ENDL; + LL_WARNS("Xfer") << "While sending file " << mLocalID << LL_ENDL; + 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) + { + LLFileSystem file(mTempID, mType, LLFileSystem::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) + { + if (LLFileSystem::getExists(mTempID, mType)) + { + LLFileSystem file(mTempID, mType, LLFileSystem::WRITE); + if (!file.rename(mLocalID, mType)) + { + LL_WARNS("Xfer") << "Cache rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; + } + else + { + // Rename worked: the original file is gone. Clear mDeleteTempFile + // so we don't attempt to delete the file in cleanup() + mDeleteTempFile = false; + } + } + else + { + LL_WARNS("Xfer") << "LLXfer_VFile::processEOF() can't open for renaming cache file " << mTempID << "." << LLAssetType::lookup(mType) << LL_ENDL; + } + } + + 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); +} + +////////////////////////////////////////////////////////// + +std::string LLXfer_VFile::getFileName() +{ + 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 index 33fb63f68c..f62da815f8 100644 --- a/indra/llmessage/llxfer_vfile.h +++ b/indra/llmessage/llxfer_vfile.h @@ -1,91 +1,91 @@ -/**
- * @file llxfer_vfile.h
- * @brief definition of LLXfer_VFile class for a single xfer_vfile.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLXFER_VFILE_H
-#define LL_LLXFER_VFILE_H
-
-#include "llxfer.h"
-#include "llassetstorage.h"
-
-class LLFileSystem;
-
-class LLXfer_VFile : public LLXfer
-{
- protected:
- LLUUID mLocalID;
- LLUUID mRemoteID;
- LLUUID mTempID;
- LLAssetType::EType mType;
-
- LLFileSystem *mVFile;
-
- std::string mName;
-
- bool mDeleteTempFile;
-
- public:
- LLXfer_VFile ();
- LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type);
- virtual ~LLXfer_VFile();
-
- virtual void init(const LLUUID &local_id, LLAssetType::EType type);
- virtual void cleanup();
-
- virtual S32 initializeRequest(U64 xfer_id,
- const LLUUID &local_id,
- const LLUUID &remote_id,
- const LLAssetType::EType type,
- const LLHost &remote_host,
- void (*callback)(void **,S32,LLExtStat),
- void **user_data);
- virtual S32 startDownload();
-
- virtual S32 processEOF();
-
- virtual S32 startSend(U64 xfer_id, const LLHost &remote_host);
- virtual void closeFileHandle();
- virtual S32 reopenFileHandle();
-
- 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 std::string getFileName();
-};
-
-#endif
-
-
-
-
-
+/** + * @file llxfer_vfile.h + * @brief definition of LLXfer_VFile class for a single xfer_vfile. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLXFER_VFILE_H +#define LL_LLXFER_VFILE_H + +#include "llxfer.h" +#include "llassetstorage.h" + +class LLFileSystem; + +class LLXfer_VFile : public LLXfer +{ + protected: + LLUUID mLocalID; + LLUUID mRemoteID; + LLUUID mTempID; + LLAssetType::EType mType; + + LLFileSystem *mVFile; + + std::string mName; + + bool mDeleteTempFile; + + public: + LLXfer_VFile (); + LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type); + virtual ~LLXfer_VFile(); + + virtual void init(const LLUUID &local_id, LLAssetType::EType type); + virtual void cleanup(); + + virtual S32 initializeRequest(U64 xfer_id, + const LLUUID &local_id, + const LLUUID &remote_id, + const LLAssetType::EType type, + const LLHost &remote_host, + void (*callback)(void **,S32,LLExtStat), + void **user_data); + virtual S32 startDownload(); + + virtual S32 processEOF(); + + virtual S32 startSend(U64 xfer_id, const LLHost &remote_host); + virtual void closeFileHandle(); + virtual S32 reopenFileHandle(); + + 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 std::string getFileName(); +}; + +#endif + + + + + diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index d4d12b8ff2..f6ed43a4e4 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -1,1326 +1,1326 @@ -/**
- * @file llxfermanager.cpp
- * @brief implementation of LLXferManager class for a collection of xfers
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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;
-
-// Kills the connection if a viewer download queue hits this many requests backed up
-// Also set in simulator.xml at "hard_limit_outgoing_xfers_per_circuit"
-const S32 LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS = 500;
-
-// Use this to show sending some ConfirmXferPacket messages
-//#define LL_XFER_PROGRESS_MESSAGES 1
-
-// Use this for lots of diagnostic spam
-//#define LL_XFER_DIAGNOISTIC_LOGGING 1
-
-///////////////////////////////////////////////////////////
-
-LLXferManager::LLXferManager ()
-{
- init();
-}
-
-///////////////////////////////////////////////////////////
-
-LLXferManager::~LLXferManager ()
-{
- cleanup();
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::init()
-{
- cleanup();
-
- setMaxOutgoingXfersPerCircuit(LL_DEFAULT_MAX_SIMULTANEOUS_XFERS);
- setHardLimitOutgoingXfersPerCircuit(LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS);
- setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS);
-
- // Turn on or off ack throttling
- mUseAckThrottling = false;
- setAckThrottleBPS(100000);
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::cleanup ()
-{
- for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer());
- mOutgoingHosts.clear();
-
- for_each(mSendList.begin(), mSendList.end(), DeletePointer());
- mSendList.clear();
-
- for_each(mReceiveList.begin(), mReceiveList.end(), DeletePointer());
- mReceiveList.clear();
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::setMaxIncomingXfers(S32 max_num)
-{
- mMaxIncomingXfers = max_num;
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::setMaxOutgoingXfersPerCircuit(S32 max_num)
-{
- mMaxOutgoingXfersPerCircuit = max_num;
-}
-
-void LLXferManager::setHardLimitOutgoingXfersPerCircuit(S32 max_num)
-{
- mHardLimitOutgoingXfersPerCircuit = 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);
- LL_DEBUGS("AppInit") << "LLXferManager ack throttle min rate: " << min_bps << LL_ENDL;
- LL_DEBUGS("AppInit") << "LLXferManager ack throttle actual rate: " << actual_rate << LL_ENDL;
- #ifdef LL_XFER_DIAGNOISTIC_LOGGING
- LL_INFOS("Xfer") << "LLXferManager ack throttle min rate: " << min_bps << LL_ENDL;
- LL_INFOS("Xfer") << "LLXferManager ack throttle actual rate: " << actual_rate << LL_ENDL;
- #endif // LL_XFER_DIAGNOISTIC_LOGGING
-
- mAckThrottle.setRate(actual_rate);
-}
-
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::updateHostStatus()
-{
- // Clear the outgoing host list
- for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer());
- mOutgoingHosts.clear();
-
- // Loop through all outgoing xfers and re-build mOutgoingHosts
- for (xfer_list_t::iterator send_iter = mSendList.begin();
- send_iter != mSendList.end(); ++send_iter)
- {
- LLHostStatus *host_statusp = NULL;
- for (status_list_t::iterator iter = mOutgoingHosts.begin();
- iter != mOutgoingHosts.end(); ++iter)
- {
- if ((*iter)->mHost == (*send_iter)->mRemoteHost)
- { // Already have this host
- host_statusp = *iter;
- break;
- }
- }
- if (!host_statusp)
- { // Don't have this host, so add it
- host_statusp = new LLHostStatus();
- if (host_statusp)
- {
- host_statusp->mHost = (*send_iter)->mRemoteHost;
- mOutgoingHosts.push_front(host_statusp);
- }
- }
- if (host_statusp)
- { // Do the accounting
- if ((*send_iter)->mStatus == e_LL_XFER_PENDING)
- {
- host_statusp->mNumPending++;
- }
- else if ((*send_iter)->mStatus == e_LL_XFER_IN_PROGRESS)
- {
- host_statusp->mNumActive++;
- }
- }
- }
-
-#ifdef LL_XFER_DIAGNOISTIC_LOGGING
- for (xfer_list_t::iterator send_iter = mSendList.begin();
- send_iter != mSendList.end(); ++send_iter)
- {
- LLXfer * xferp = *send_iter;
- LL_INFOS("Xfer") << "xfer to host " << xferp->mRemoteHost
- << " is " << xferp->mXferSize << " bytes"
- << ", status " << (S32)(xferp->mStatus)
- << ", waiting for ACK: " << (S32)(xferp->mWaitingForACK)
- << " in frame " << (S32) LLFrameTimer::getFrameCount()
- << LL_ENDL;
- }
-
- for (status_list_t::iterator iter = mOutgoingHosts.begin();
- iter != mOutgoingHosts.end(); ++iter)
- {
- LL_INFOS("Xfer") << "LLXfer host " << (*iter)->mHost.getIPandPort()
- << " has " << (*iter)->mNumActive
- << " active, " << (*iter)->mNumPending
- << " pending"
- << " in frame " << (S32) LLFrameTimer::getFrameCount()
- << LL_ENDL;
- }
-#endif // LL_XFER_DIAGNOISTIC_LOGGING
-
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::printHostStatus()
-{
- LLHostStatus *host_statusp = NULL;
- if (!mOutgoingHosts.empty())
- {
- LL_INFOS("Xfer") << "Outgoing Xfers:" << LL_ENDL;
-
- for (status_list_t::iterator iter = mOutgoingHosts.begin();
- iter != mOutgoingHosts.end(); ++iter)
- {
- host_statusp = *iter;
- LL_INFOS("Xfer") << " " << host_statusp->mHost << " active: " << host_statusp->mNumActive << " pending: " << host_statusp->mNumPending << LL_ENDL;
- }
- }
-}
-
-///////////////////////////////////////////////////////////
-
-LLXfer * LLXferManager::findXferByID(U64 id, xfer_list_t & xfer_list)
-{
- for (xfer_list_t::iterator iter = xfer_list.begin();
- iter != xfer_list.end();
- ++iter)
- {
- if ((*iter)->mID == id)
- {
- return(*iter);
- }
- }
- return(NULL);
-}
-
-
-///////////////////////////////////////////////////////////
-
-// WARNING: this invalidates iterators from xfer_list
-void LLXferManager::removeXfer(LLXfer *delp, xfer_list_t & xfer_list)
-{
- if (delp)
- {
- std::string direction = "send";
- if (&xfer_list == &mReceiveList)
- {
- direction = "receive";
- }
-
- // This assumes that delp will occur in the list once at most
- // Find the pointer in the list
- for (xfer_list_t::iterator iter = xfer_list.begin();
- iter != xfer_list.end();
- ++iter)
- {
- if ((*iter) == delp)
- {
- LL_DEBUGS("Xfer") << "Deleting xfer to host " << (*iter)->mRemoteHost
- << " of " << (*iter)->mXferSize << " bytes"
- << ", status " << (S32)((*iter)->mStatus)
- << " from the " << direction << " list"
- << LL_ENDL;
-
- xfer_list.erase(iter);
- delete (delp);
- break;
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////
-
-LLHostStatus * LLXferManager::findHostStatus(const LLHost &host)
-{
- LLHostStatus *host_statusp = NULL;
-
- for (status_list_t::iterator iter = mOutgoingHosts.begin();
- iter != mOutgoingHosts.end(); ++iter)
- {
- host_statusp = *iter;
- if (host_statusp->mHost == host)
- {
- return (host_statusp);
- }
- }
- return 0;
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXferManager::numPendingXfers(const LLHost &host)
-{
- LLHostStatus *host_statusp = findHostStatus(host);
- if (host_statusp)
- {
- return host_statusp->mNumPending;
- }
- return 0;
-}
-
-///////////////////////////////////////////////////////////
-
-S32 LLXferManager::numActiveXfers(const LLHost &host)
-{
- LLHostStatus *host_statusp = findHostStatus(host);
- if (host_statusp)
- {
- return host_statusp->mNumActive;
- }
- return 0;
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::changeNumActiveXfers(const LLHost &host, S32 delta)
-{
- LLHostStatus *host_statusp = NULL;
-
- for (status_list_t::iterator iter = mOutgoingHosts.begin();
- iter != mOutgoingHosts.end(); ++iter)
- {
- host_statusp = *iter;
- 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::requestFile(const std::string& local_filename,
- const std::string& remote_filename,
- ELLPath remote_path,
- const LLHost& remote_host,
- bool delete_remote_on_completion,
- void (*callback)(void**,S32,LLExtStat),
- void** user_data,
- bool is_priority,
- bool use_big_packets)
-{
- LLXfer_File* file_xfer_p = NULL;
-
- // First check to see if it's already requested
- for (xfer_list_t::iterator iter = mReceiveList.begin();
- iter != mReceiveList.end(); ++iter)
- {
- if ((*iter)->getXferTypeTag() == LLXfer::XFER_FILE)
- {
- file_xfer_p = (LLXfer_File*)(*iter);
- if (file_xfer_p->matchesLocalFilename(local_filename)
- && file_xfer_p->matchesRemoteFilename(remote_filename, remote_path)
- && (remote_host == file_xfer_p->mRemoteHost)
- && (callback == file_xfer_p->mCallback)
- && (user_data == file_xfer_p->mCallbackDataHandle))
- {
- // Already have the request (already in progress)
- return (*iter)->mID;
- }
- }
- }
-
- U64 xfer_id = 0;
-
- S32 chunk_size = use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1;
- file_xfer_p = new LLXfer_File(chunk_size);
- if (file_xfer_p)
- {
- addToList(file_xfer_p, 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 &&
- (remote_filename.substr(remote_filename.length()-4) == ".tmp"))
- {
- LLFile::remove(local_filename, ENOENT);
- }
- xfer_id = getNextID();
- file_xfer_p->initializeRequest(
- xfer_id,
- local_filename,
- remote_filename,
- remote_path,
- remote_host,
- delete_remote_on_completion,
- callback,user_data);
- startPendingDownloads();
- }
- else
- {
- LL_ERRS("Xfer") << "Xfer allocation error" << LL_ENDL;
- }
- return xfer_id;
-}
-
-void LLXferManager::requestVFile(const LLUUID& local_id,
- const LLUUID& remote_id,
- LLAssetType::EType type,
- const LLHost& remote_host,
- void (*callback)(void**,S32,LLExtStat),
- void** user_data,
- bool is_priority)
-{
- LLXfer_VFile * xfer_p = NULL;
-
- for (xfer_list_t::iterator iter = mReceiveList.begin();
- iter != mReceiveList.end(); ++iter)
- { // Find any matching existing requests
- if ((*iter)->getXferTypeTag() == LLXfer::XFER_VFILE)
- {
- xfer_p = (LLXfer_VFile*) (*iter);
- if (xfer_p->matchesLocalFile(local_id, type)
- && xfer_p->matchesRemoteFile(remote_id, type)
- && (remote_host == xfer_p->mRemoteHost)
- && (callback == xfer_p->mCallback)
- && (user_data == xfer_p->mCallbackDataHandle))
-
- { // Have match, don't add a duplicate
- #ifdef LL_XFER_DIAGNOISTIC_LOGGING
- LL_INFOS("Xfer") << "Dropping duplicate xfer request for " << remote_id
- << " on " << remote_host.getIPandPort()
- << " local id " << local_id
- << LL_ENDL;
- #endif // LL_XFER_DIAGNOISTIC_LOGGING
-
- return;
- }
- }
- }
-
- xfer_p = new LLXfer_VFile();
- if (xfer_p)
- {
- #ifdef LL_XFER_DIAGNOISTIC_LOGGING
- LL_INFOS("Xfer") << "Starting file xfer for " << remote_id
- << " type " << LLAssetType::lookupHumanReadable(type)
- << " from " << xfer_p->mRemoteHost.getIPandPort()
- << ", local id " << local_id
- << LL_ENDL;
- #endif // LL_XFER_DIAGNOISTIC_LOGGING
-
- addToList(xfer_p, mReceiveList, is_priority);
- ((LLXfer_VFile *)xfer_p)->initializeRequest(getNextID(),
- local_id,
- remote_id,
- type,
- remote_host,
- callback,
- user_data);
- startPendingDownloads();
- }
- else
- {
- LL_ERRS("Xfer") << "Xfer allocation error" << LL_ENDL;
- }
-
-}
-
-///////////////////////////////////////////////////////////
-
-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[BUF_SIZE]; /* 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);
- if (fdata_size < 0 ||
- fdata_size > BUF_SIZE)
- {
- char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
- LL_WARNS("Xfer") << "Received invalid xfer data size of " << fdata_size
- << " in packet number " << packetnum
- << " from " << mesgsys->getSender()
- << " for xfer id: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF))
- << LL_ENDL;
- return;
- }
- mesgsys->getBinaryDataFast(_PREHASH_DataPacket, _PREHASH_Data, fdata_buf, fdata_size, 0, BUF_SIZE);
-
- xferp = findXferByID(id, mReceiveList);
- if (!xferp)
- {
- char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
- LL_WARNS("Xfer") << "received xfer data from " << mesgsys->getSender()
- << " for non-existent xfer id: "
- << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << LL_ENDL;
- 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))
- {
- LL_INFOS("Xfer") << "Reconfirming xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet " << packetnum << LL_ENDL; sendConfirmPacket(mesgsys, id, decodePacketNum(packetnum), mesgsys->getSender());
- }
- else
- {
- LL_INFOS("Xfer") << "Ignoring xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " recv'd packet " << packetnum << "; expecting " << xferp->mPacketNum << LL_ENDL;
- }
- 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_back(ack_info);
- }
-
- if (isLastPacket(packetnum))
- {
- xferp->processEOF();
- removeXfer(xferp,mReceiveList);
- startPendingDownloads();
- }
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 packetnum, const LLHost &remote_host)
-{
-#ifdef 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);
-
- // Ignore a circuit failure here, we'll catch it with another message
- mesgsys->sendMessage(remote_host);
-}
-
-///////////////////////////////////////////////////////////
-
-static bool find_and_remove(std::multiset<std::string>& files,
- const std::string& filename)
-{
- std::multiset<std::string>::iterator ptr;
- if ( (ptr = files.find(filename)) != files.end())
- {
- //erase(filename) erases *all* entries with that key
- files.erase(ptr);
- return true;
- }
- return false;
-}
-
-void LLXferManager::expectFileForRequest(const std::string& filename)
-{
- mExpectedRequests.insert(filename);
-}
-
-bool LLXferManager::validateFileForRequest(const std::string& filename)
-{
- return find_and_remove(mExpectedRequests, filename);
-}
-
-void LLXferManager::expectFileForTransfer(const std::string& filename)
-{
- mExpectedTransfers.insert(filename);
-}
-
-bool LLXferManager::validateFileForTransfer(const std::string& filename)
-{
- return find_and_remove(mExpectedTransfers, filename);
-}
-
-/* Present in fireengine, not used by viewer
-void LLXferManager::expectVFileForRequest(const std::string& filename)
-{
- mExpectedVFileRequests.insert(filename);
-}
-
-bool LLXferManager::validateVFileForRequest(const std::string& filename)
-{
- return find_and_remove(mExpectedVFileRequests, filename);
-}
-
-void LLXferManager::expectVFileForTransfer(const std::string& filename)
-{
- mExpectedVFileTransfers.insert(filename);
-}
-
-bool LLXferManager::validateVFileForTransfer(const std::string& filename)
-{
- return find_and_remove(mExpectedVFileTransfers, filename);
-}
-*/
-
-static bool remove_prefix(std::string& filename, const std::string& prefix)
-{
- if (std::equal(prefix.begin(), prefix.end(), filename.begin()))
- {
- filename = filename.substr(prefix.length());
- return true;
- }
- return false;
-}
-
-static bool verify_cache_filename(const std::string& filename)
-{
- //NOTE: This routine is only used to check file names that our own
- // code places in the cache directory. As such, it can be limited
- // to this very restrictive file name pattern. It does not need to
- // handle other characters. The only known uses of this are (with examples):
- // sim to sim object pass: fc0b72d8-9456-63d9-a802-a557ef847313.tmp
- // sim to viewer mute list: mute_b78eacd0-1244-448e-93ca-28ede242f647.tmp
- // sim to viewer task inventory: inventory_d8ab59d2-baf0-0e79-c4c2-a3f99b9fcf45.tmp
-
- //IMPORTANT: Do not broaden the filenames accepted by this routine
- // without careful analysis. Anything allowed by this function can
- // be downloaded by the viewer.
-
- size_t len = filename.size();
- //const boost::regex expr("[0-9a-zA-Z_-]<1,46>\.tmp");
- if (len < 5 || len > 50)
- {
- return false;
- }
- for(size_t i=0; i<(len-4); ++i)
- {
- char c = filename[i];
- bool ok = isalnum(c) || '_'==c || '-'==c;
- if (!ok)
- {
- return false;
- }
- }
- return filename[len-4] == '.'
- && filename[len-3] == 't'
- && filename[len-2] == 'm'
- && filename[len-1] == 'p';
-}
-
-void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user_data*/)
-{
-
- U64 id;
- std::string local_filename;
- 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 */
- LL_INFOS("Xfer") << "xfer request id: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF))
- << " to " << mesgsys->getSender() << LL_ENDL;
-
- mesgsys->getStringFast(_PREHASH_XferID, _PREHASH_Filename, local_filename);
-
- {
- U8 local_path_u8;
- mesgsys->getU8("XferID", "FilePath", local_path_u8);
- local_path = (ELLPath)local_path_u8;
- }
-
- 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)
- { // Request for an asset - use a cache file
- if(NULL == LLAssetType::lookup(type))
- {
- LL_WARNS("Xfer") << "Invalid type for xfer request: " << uuid << ":"
- << type_s16 << " to " << mesgsys->getSender() << LL_ENDL;
- return;
- }
-
- LL_INFOS("Xfer") << "starting vfile transfer: " << uuid << "," << LLAssetType::lookup(type) << " to " << mesgsys->getSender() << LL_ENDL;
-
- xferp = (LLXfer *)new LLXfer_VFile(uuid, type);
- if (xferp)
- {
- mSendList.push_front(xferp);
- result = xferp->startSend(id,mesgsys->getSender());
- }
- else
- {
- LL_ERRS("Xfer") << "Xfer allcoation error" << LL_ENDL;
- }
- }
- else if (!local_filename.empty())
- { // Was given a file name to send
- // See DEV-21775 for detailed security issues
-
- if (local_path == LL_PATH_NONE)
- {
- // this handles legacy simulators that are passing objects
- // by giving a filename that explicitly names the cache directory
- static const std::string legacy_cache_prefix = "data/";
- if (remove_prefix(local_filename, legacy_cache_prefix))
- {
- local_path = LL_PATH_CACHE;
- }
- }
-
- switch (local_path)
- {
- case LL_PATH_NONE:
- if(!validateFileForTransfer(local_filename))
- {
- LL_WARNS("Xfer") << "SECURITY: Unapproved filename '" << local_filename << LL_ENDL;
- return;
- }
- break;
-
- case LL_PATH_CACHE:
- if(!verify_cache_filename(local_filename))
- {
- LL_WARNS("Xfer") << "SECURITY: Illegal cache filename '" << local_filename << LL_ENDL;
- return;
- }
- break;
-
- default:
- LL_WARNS("Xfer") << "SECURITY: Restricted file dir enum: " << (U32)local_path << LL_ENDL;
- return;
- }
-
- // If we want to use a special path (e.g. LL_PATH_CACHE), we want to make sure we create the
- // proper expanded filename.
- std::string expanded_filename;
- if (local_path != LL_PATH_NONE)
- {
- expanded_filename = gDirUtilp->getExpandedFilename( local_path, local_filename );
- }
- else
- {
- expanded_filename = local_filename;
- }
- LL_INFOS("Xfer") << "starting file transfer: " << expanded_filename << " to " << mesgsys->getSender() << LL_ENDL;
-
- 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)
- {
- mSendList.push_front(xferp);
- result = xferp->startSend(id,mesgsys->getSender());
- }
- else
- {
- LL_ERRS("Xfer") << "Xfer allcoation error" << LL_ENDL;
- }
- }
- else
- { // no uuid or filename - use the ID sent
- char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
- LL_INFOS("Xfer") << "starting memory transfer: "
- << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << " to "
- << mesgsys->getSender() << LL_ENDL;
-
- xferp = findXferByID(id, mSendList);
-
- if (xferp)
- {
- result = xferp->startSend(id,mesgsys->getSender());
- }
- else
- {
- LL_INFOS("Xfer") << "Warning: xfer ID " << U64_BUF << " not found." << LL_ENDL;
- 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
- {
- LL_INFOS("Xfer") << "Aborting xfer to " << mesgsys->getSender() << " with error: " << result << LL_ENDL;
-
- 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)
- {
- // Figure out how many transfers the host has requested
- LLHostStatus *host_statusp = findHostStatus(xferp->mRemoteHost);
- if (host_statusp)
- {
- if (host_statusp->mNumActive < mMaxOutgoingXfersPerCircuit)
- { // Not many transfers in progress already, so start immediately
- xferp->sendNextPacket();
- changeNumActiveXfers(xferp->mRemoteHost,1);
- LL_DEBUGS("Xfer") << "Starting xfer ID " << U64_to_str(id) << " immediately" << LL_ENDL;
- }
- else if (mHardLimitOutgoingXfersPerCircuit == 0 ||
- (host_statusp->mNumActive + host_statusp->mNumPending) < mHardLimitOutgoingXfersPerCircuit)
- { // Must close the file handle and wait for earlier ones to complete
- LL_INFOS("Xfer") << " queueing xfer request id " << U64_to_str(id) << ", "
- << host_statusp->mNumActive << " active and "
- << host_statusp->mNumPending << " pending ahead of this one"
- << LL_ENDL;
- xferp->closeFileHandle(); // Close the file handle until we're ready to send again
- }
- else if (mHardLimitOutgoingXfersPerCircuit > 0)
- { // Way too many requested ... it's time to stop being nice and kill the circuit
- xferp->closeFileHandle(); // Close the file handle in any case
- LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(xferp->mRemoteHost);
- if (cdp)
- {
- if (cdp->getTrusted())
- { // Trusted internal circuit - don't kill it
- LL_WARNS("Xfer") << "Trusted circuit to " << xferp->mRemoteHost << " has too many xfer requests in the queue "
- << host_statusp->mNumActive << " active and "
- << host_statusp->mNumPending << " pending ahead of this one"
- << LL_ENDL;
- }
- else
- { // Untrusted circuit - time to stop messing around and kill it
- LL_WARNS("Xfer") << "Killing circuit to " << xferp->mRemoteHost << " for having too many xfer requests in the queue "
- << host_statusp->mNumActive << " active and "
- << host_statusp->mNumPending << " pending ahead of this one"
- << LL_ENDL;
- gMessageSystem->disableCircuit(xferp->mRemoteHost);
- }
- }
- else
- { // WTF? Why can't we find a circuit? Try to kill it off
- LL_WARNS("Xfer") << "Backlog with circuit to " << xferp->mRemoteHost << " with too many xfer requests in the queue "
- << host_statusp->mNumActive << " active and "
- << host_statusp->mNumPending << " pending ahead of this one"
- << " but no LLCircuitData found???"
- << LL_ENDL;
- gMessageSystem->disableCircuit(xferp->mRemoteHost);
- }
- }
- }
- else
- {
- LL_WARNS("Xfer") << "LLXferManager::processFileRequest() - no LLHostStatus found for id " << U64_to_str(id)
- << " host " << xferp->mRemoteHost << LL_ENDL;
- }
- }
- else
- {
- LL_WARNS("Xfer") << "LLXferManager::processFileRequest() - no xfer found for id " << U64_to_str(id) << LL_ENDL;
- }
-}
-
-///////////////////////////////////////////////////////////
-
-// Return true if host is in a transfer-flood sitation. Same check for both internal and external hosts
-bool LLXferManager::isHostFlooded(const LLHost & host)
-{
- bool flooded = false;
- LLHostStatus *host_statusp = findHostStatus(host);
- if (host_statusp)
- {
- flooded = (mHardLimitOutgoingXfersPerCircuit > 0 &&
- (host_statusp->mNumActive + host_statusp->mNumPending) >= (S32)(mHardLimitOutgoingXfersPerCircuit * 0.8f));
- }
-
- return flooded;
-}
-
-
-///////////////////////////////////////////////////////////
-
-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 = findXferByID(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);
- }
- }
-}
-
-///////////////////////////////////////////////////////////
-
-// Called from LLMessageSystem::processAcks()
-void LLXferManager::retransmitUnackedPackets()
-{
- LLXfer *xferp;
-
- xfer_list_t::iterator iter = mReceiveList.begin();
- while (iter != mReceiveList.end())
- {
- xferp = (*iter);
- if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
- {
- // if the circuit dies, abort
- if (! gMessageSystem->mCircuitInfo.isCircuitAlive( xferp->mRemoteHost ))
- {
- LL_WARNS("Xfer") << "Xfer found in progress on dead circuit, aborting transfer to "
- << xferp->mRemoteHost.getIPandPort()
- << LL_ENDL;
- xferp->mCallbackResult = LL_ERR_CIRCUIT_GONE;
- xferp->processEOF();
-
- iter = mReceiveList.erase(iter); // iter is set to next one after the deletion point
- delete (xferp);
- continue;
- }
-
- }
- ++iter;
- }
-
- // Re-build mOutgoingHosts data
- updateHostStatus();
-
- F32 et;
- iter = mSendList.begin();
- while (iter != mSendList.end())
- {
- xferp = (*iter);
- if (xferp->mWaitingForACK && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_PACKET_TIMEOUT))
- {
- if (xferp->mRetries > LL_PACKET_RETRY_LIMIT)
- {
- LL_INFOS("Xfer") << "dropping xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet retransmit limit exceeded, xfer dropped" << LL_ENDL;
- xferp->abort(LL_ERR_TCP_TIMEOUT);
- iter = mSendList.erase(iter);
- delete xferp;
- continue;
- }
- else
- {
- LL_INFOS("Xfer") << "resending xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet unconfirmed after: "<< et << " sec, packet " << xferp->mPacketNum << LL_ENDL;
- xferp->resendLastPacket();
- }
- }
- else if ((xferp->mStatus == e_LL_XFER_REGISTERED) && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_XFER_REGISTRATION_TIMEOUT))
- {
- LL_INFOS("Xfer") << "registered xfer never requested, xfer dropped" << LL_ENDL;
- xferp->abort(LL_ERR_TCP_TIMEOUT);
- iter = mSendList.erase(iter);
- delete xferp;
- continue;
- }
- else if (xferp->mStatus == e_LL_XFER_ABORTED)
- {
- LL_WARNS("Xfer") << "Removing aborted xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << LL_ENDL;
- iter = mSendList.erase(iter);
- delete xferp;
- continue;
- }
- else if (xferp->mStatus == e_LL_XFER_PENDING)
- {
-// LL_INFOS("Xfer") << "*** numActiveXfers = " << numActiveXfers(xferp->mRemoteHost) << " mMaxOutgoingXfersPerCircuit = " << mMaxOutgoingXfersPerCircuit << LL_ENDL;
- if (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit)
- {
- if (xferp->reopenFileHandle())
- {
- LL_WARNS("Xfer") << "Error re-opening file handle for xfer ID " << U64_to_str(xferp->mID)
- << " to host " << xferp->mRemoteHost << LL_ENDL;
- xferp->abort(LL_ERR_CANNOT_OPEN_FILE);
- iter = mSendList.erase(iter);
- delete xferp;
- continue;
- }
- else
- { // No error re-opening the file, send the first packet
- LL_DEBUGS("Xfer") << "Moving pending xfer ID " << U64_to_str(xferp->mID) << " to active" << LL_ENDL;
- xferp->sendNextPacket();
- changeNumActiveXfers(xferp->mRemoteHost,1);
- }
- }
- }
- ++iter;
- } // end while() loop
-
- //
- // HACK - if we're using xfer confirm throttling, throttle our xfer confirms here
- // so we don't blow through bandwidth.
- //
-
- while (mXferAckQueue.size())
- {
- if (mAckThrottle.checkOverflow(1000.0f*8.0f))
- {
- break;
- }
- //LL_INFOS("Xfer") << "Confirm packet queue length:" << mXferAckQueue.size() << LL_ENDL;
- LLXferAckInfo ack_info = mXferAckQueue.front();
- mXferAckQueue.pop_front();
- //LL_INFOS("Xfer") << "Sending confirm packet" << LL_ENDL;
- sendConfirmPacket(gMessageSystem, ack_info.mID, ack_info.mPacketNum, ack_info.mRemoteHost);
- mAckThrottle.throttleOverflow(1000.f*8.f); // Assume 1000 bytes/packet
- }
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::abortRequestById(U64 xfer_id, S32 result_code)
-{
- LLXfer * xferp = findXferByID(xfer_id, mReceiveList);
- if (xferp)
- {
- if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
- {
- // causes processAbort();
- xferp->abort(result_code);
- }
- else
- {
- xferp->mCallbackResult = result_code;
- xferp->processEOF(); //should notify requester
- removeXfer(xferp, mReceiveList);
- }
- // Since already removed or marked as aborted no need
- // to wait for processAbort() to start new download
- startPendingDownloads();
- }
-}
-
-///////////////////////////////////////////////////////////
-
-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 = findXferByID(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;
- std::list<LLXfer*> pending_downloads;
- S32 download_count = 0;
- S32 pending_count = 0;
- for (xfer_list_t::iterator iter = mReceiveList.begin();
- iter != mReceiveList.end();
- ++iter)
- {
- xferp = (*iter);
- if(xferp->mStatus == e_LL_XFER_PENDING)
- { // Count and accumulate pending downloads
- ++pending_count;
- pending_downloads.push_front(xferp);
- }
- else if(xferp->mStatus == e_LL_XFER_IN_PROGRESS)
- { // Count downloads in progress
- ++download_count;
- }
- }
-
- S32 start_count = mMaxIncomingXfers - download_count;
-
- LL_DEBUGS("Xfer") << "LLXferManager::startPendingDownloads() - XFER_IN_PROGRESS: "
- << download_count << " XFER_PENDING: " << pending_count
- << " startring " << llmin(start_count, pending_count) << LL_ENDL;
-
- if((start_count > 0) && (pending_count > 0))
- {
- S32 result;
- for (std::list<LLXfer*>::iterator iter = pending_downloads.begin();
- iter != pending_downloads.end(); ++iter)
- {
- xferp = *iter;
- if (start_count-- <= 0)
- break;
- result = xferp->startDownload();
- if(result)
- {
- xferp->abort(result);
- ++start_count;
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////
-
-void LLXferManager::addToList(LLXfer* xferp, xfer_list_t & xfer_list, bool is_priority)
-{
- if(is_priority)
- {
- xfer_list.push_back(xferp);
- }
- else
- {
- xfer_list.push_front(xferp);
- }
-}
-
-///////////////////////////////////////////////////////////
-// Globals and C routines
-///////////////////////////////////////////////////////////
-
-LLXferManager *gXferManager = NULL;
-
-
-void start_xfer_manager()
-{
- gXferManager = new LLXferManager();
-}
-
-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 (ll_frand() > 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);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+/** + * @file llxfermanager.cpp + * @brief implementation of LLXferManager class for a collection of xfers + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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; + +// Kills the connection if a viewer download queue hits this many requests backed up +// Also set in simulator.xml at "hard_limit_outgoing_xfers_per_circuit" +const S32 LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS = 500; + +// Use this to show sending some ConfirmXferPacket messages +//#define LL_XFER_PROGRESS_MESSAGES 1 + +// Use this for lots of diagnostic spam +//#define LL_XFER_DIAGNOISTIC_LOGGING 1 + +/////////////////////////////////////////////////////////// + +LLXferManager::LLXferManager () +{ + init(); +} + +/////////////////////////////////////////////////////////// + +LLXferManager::~LLXferManager () +{ + cleanup(); +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::init() +{ + cleanup(); + + setMaxOutgoingXfersPerCircuit(LL_DEFAULT_MAX_SIMULTANEOUS_XFERS); + setHardLimitOutgoingXfersPerCircuit(LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS); + setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS); + + // Turn on or off ack throttling + mUseAckThrottling = false; + setAckThrottleBPS(100000); +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::cleanup () +{ + for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer()); + mOutgoingHosts.clear(); + + for_each(mSendList.begin(), mSendList.end(), DeletePointer()); + mSendList.clear(); + + for_each(mReceiveList.begin(), mReceiveList.end(), DeletePointer()); + mReceiveList.clear(); +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::setMaxIncomingXfers(S32 max_num) +{ + mMaxIncomingXfers = max_num; +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::setMaxOutgoingXfersPerCircuit(S32 max_num) +{ + mMaxOutgoingXfersPerCircuit = max_num; +} + +void LLXferManager::setHardLimitOutgoingXfersPerCircuit(S32 max_num) +{ + mHardLimitOutgoingXfersPerCircuit = 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); + LL_DEBUGS("AppInit") << "LLXferManager ack throttle min rate: " << min_bps << LL_ENDL; + LL_DEBUGS("AppInit") << "LLXferManager ack throttle actual rate: " << actual_rate << LL_ENDL; + #ifdef LL_XFER_DIAGNOISTIC_LOGGING + LL_INFOS("Xfer") << "LLXferManager ack throttle min rate: " << min_bps << LL_ENDL; + LL_INFOS("Xfer") << "LLXferManager ack throttle actual rate: " << actual_rate << LL_ENDL; + #endif // LL_XFER_DIAGNOISTIC_LOGGING + + mAckThrottle.setRate(actual_rate); +} + + +/////////////////////////////////////////////////////////// + +void LLXferManager::updateHostStatus() +{ + // Clear the outgoing host list + for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer()); + mOutgoingHosts.clear(); + + // Loop through all outgoing xfers and re-build mOutgoingHosts + for (xfer_list_t::iterator send_iter = mSendList.begin(); + send_iter != mSendList.end(); ++send_iter) + { + LLHostStatus *host_statusp = NULL; + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) + { + if ((*iter)->mHost == (*send_iter)->mRemoteHost) + { // Already have this host + host_statusp = *iter; + break; + } + } + if (!host_statusp) + { // Don't have this host, so add it + host_statusp = new LLHostStatus(); + if (host_statusp) + { + host_statusp->mHost = (*send_iter)->mRemoteHost; + mOutgoingHosts.push_front(host_statusp); + } + } + if (host_statusp) + { // Do the accounting + if ((*send_iter)->mStatus == e_LL_XFER_PENDING) + { + host_statusp->mNumPending++; + } + else if ((*send_iter)->mStatus == e_LL_XFER_IN_PROGRESS) + { + host_statusp->mNumActive++; + } + } + } + +#ifdef LL_XFER_DIAGNOISTIC_LOGGING + for (xfer_list_t::iterator send_iter = mSendList.begin(); + send_iter != mSendList.end(); ++send_iter) + { + LLXfer * xferp = *send_iter; + LL_INFOS("Xfer") << "xfer to host " << xferp->mRemoteHost + << " is " << xferp->mXferSize << " bytes" + << ", status " << (S32)(xferp->mStatus) + << ", waiting for ACK: " << (S32)(xferp->mWaitingForACK) + << " in frame " << (S32) LLFrameTimer::getFrameCount() + << LL_ENDL; + } + + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) + { + LL_INFOS("Xfer") << "LLXfer host " << (*iter)->mHost.getIPandPort() + << " has " << (*iter)->mNumActive + << " active, " << (*iter)->mNumPending + << " pending" + << " in frame " << (S32) LLFrameTimer::getFrameCount() + << LL_ENDL; + } +#endif // LL_XFER_DIAGNOISTIC_LOGGING + +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::printHostStatus() +{ + LLHostStatus *host_statusp = NULL; + if (!mOutgoingHosts.empty()) + { + LL_INFOS("Xfer") << "Outgoing Xfers:" << LL_ENDL; + + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) + { + host_statusp = *iter; + LL_INFOS("Xfer") << " " << host_statusp->mHost << " active: " << host_statusp->mNumActive << " pending: " << host_statusp->mNumPending << LL_ENDL; + } + } +} + +/////////////////////////////////////////////////////////// + +LLXfer * LLXferManager::findXferByID(U64 id, xfer_list_t & xfer_list) +{ + for (xfer_list_t::iterator iter = xfer_list.begin(); + iter != xfer_list.end(); + ++iter) + { + if ((*iter)->mID == id) + { + return(*iter); + } + } + return(NULL); +} + + +/////////////////////////////////////////////////////////// + +// WARNING: this invalidates iterators from xfer_list +void LLXferManager::removeXfer(LLXfer *delp, xfer_list_t & xfer_list) +{ + if (delp) + { + std::string direction = "send"; + if (&xfer_list == &mReceiveList) + { + direction = "receive"; + } + + // This assumes that delp will occur in the list once at most + // Find the pointer in the list + for (xfer_list_t::iterator iter = xfer_list.begin(); + iter != xfer_list.end(); + ++iter) + { + if ((*iter) == delp) + { + LL_DEBUGS("Xfer") << "Deleting xfer to host " << (*iter)->mRemoteHost + << " of " << (*iter)->mXferSize << " bytes" + << ", status " << (S32)((*iter)->mStatus) + << " from the " << direction << " list" + << LL_ENDL; + + xfer_list.erase(iter); + delete (delp); + break; + } + } + } +} + +/////////////////////////////////////////////////////////// + +LLHostStatus * LLXferManager::findHostStatus(const LLHost &host) +{ + LLHostStatus *host_statusp = NULL; + + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) + { + host_statusp = *iter; + if (host_statusp->mHost == host) + { + return (host_statusp); + } + } + return 0; +} + +/////////////////////////////////////////////////////////// + +S32 LLXferManager::numPendingXfers(const LLHost &host) +{ + LLHostStatus *host_statusp = findHostStatus(host); + if (host_statusp) + { + return host_statusp->mNumPending; + } + return 0; +} + +/////////////////////////////////////////////////////////// + +S32 LLXferManager::numActiveXfers(const LLHost &host) +{ + LLHostStatus *host_statusp = findHostStatus(host); + if (host_statusp) + { + return host_statusp->mNumActive; + } + return 0; +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::changeNumActiveXfers(const LLHost &host, S32 delta) +{ + LLHostStatus *host_statusp = NULL; + + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) + { + host_statusp = *iter; + 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::requestFile(const std::string& local_filename, + const std::string& remote_filename, + ELLPath remote_path, + const LLHost& remote_host, + bool delete_remote_on_completion, + void (*callback)(void**,S32,LLExtStat), + void** user_data, + bool is_priority, + bool use_big_packets) +{ + LLXfer_File* file_xfer_p = NULL; + + // First check to see if it's already requested + for (xfer_list_t::iterator iter = mReceiveList.begin(); + iter != mReceiveList.end(); ++iter) + { + if ((*iter)->getXferTypeTag() == LLXfer::XFER_FILE) + { + file_xfer_p = (LLXfer_File*)(*iter); + if (file_xfer_p->matchesLocalFilename(local_filename) + && file_xfer_p->matchesRemoteFilename(remote_filename, remote_path) + && (remote_host == file_xfer_p->mRemoteHost) + && (callback == file_xfer_p->mCallback) + && (user_data == file_xfer_p->mCallbackDataHandle)) + { + // Already have the request (already in progress) + return (*iter)->mID; + } + } + } + + U64 xfer_id = 0; + + S32 chunk_size = use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1; + file_xfer_p = new LLXfer_File(chunk_size); + if (file_xfer_p) + { + addToList(file_xfer_p, 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 && + (remote_filename.substr(remote_filename.length()-4) == ".tmp")) + { + LLFile::remove(local_filename, ENOENT); + } + xfer_id = getNextID(); + file_xfer_p->initializeRequest( + xfer_id, + local_filename, + remote_filename, + remote_path, + remote_host, + delete_remote_on_completion, + callback,user_data); + startPendingDownloads(); + } + else + { + LL_ERRS("Xfer") << "Xfer allocation error" << LL_ENDL; + } + return xfer_id; +} + +void LLXferManager::requestVFile(const LLUUID& local_id, + const LLUUID& remote_id, + LLAssetType::EType type, + const LLHost& remote_host, + void (*callback)(void**,S32,LLExtStat), + void** user_data, + bool is_priority) +{ + LLXfer_VFile * xfer_p = NULL; + + for (xfer_list_t::iterator iter = mReceiveList.begin(); + iter != mReceiveList.end(); ++iter) + { // Find any matching existing requests + if ((*iter)->getXferTypeTag() == LLXfer::XFER_VFILE) + { + xfer_p = (LLXfer_VFile*) (*iter); + if (xfer_p->matchesLocalFile(local_id, type) + && xfer_p->matchesRemoteFile(remote_id, type) + && (remote_host == xfer_p->mRemoteHost) + && (callback == xfer_p->mCallback) + && (user_data == xfer_p->mCallbackDataHandle)) + + { // Have match, don't add a duplicate + #ifdef LL_XFER_DIAGNOISTIC_LOGGING + LL_INFOS("Xfer") << "Dropping duplicate xfer request for " << remote_id + << " on " << remote_host.getIPandPort() + << " local id " << local_id + << LL_ENDL; + #endif // LL_XFER_DIAGNOISTIC_LOGGING + + return; + } + } + } + + xfer_p = new LLXfer_VFile(); + if (xfer_p) + { + #ifdef LL_XFER_DIAGNOISTIC_LOGGING + LL_INFOS("Xfer") << "Starting file xfer for " << remote_id + << " type " << LLAssetType::lookupHumanReadable(type) + << " from " << xfer_p->mRemoteHost.getIPandPort() + << ", local id " << local_id + << LL_ENDL; + #endif // LL_XFER_DIAGNOISTIC_LOGGING + + addToList(xfer_p, mReceiveList, is_priority); + ((LLXfer_VFile *)xfer_p)->initializeRequest(getNextID(), + local_id, + remote_id, + type, + remote_host, + callback, + user_data); + startPendingDownloads(); + } + else + { + LL_ERRS("Xfer") << "Xfer allocation error" << LL_ENDL; + } + +} + +/////////////////////////////////////////////////////////// + +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[BUF_SIZE]; /* 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); + if (fdata_size < 0 || + fdata_size > BUF_SIZE) + { + char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */ + LL_WARNS("Xfer") << "Received invalid xfer data size of " << fdata_size + << " in packet number " << packetnum + << " from " << mesgsys->getSender() + << " for xfer id: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) + << LL_ENDL; + return; + } + mesgsys->getBinaryDataFast(_PREHASH_DataPacket, _PREHASH_Data, fdata_buf, fdata_size, 0, BUF_SIZE); + + xferp = findXferByID(id, mReceiveList); + if (!xferp) + { + char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */ + LL_WARNS("Xfer") << "received xfer data from " << mesgsys->getSender() + << " for non-existent xfer id: " + << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << LL_ENDL; + 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)) + { + LL_INFOS("Xfer") << "Reconfirming xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet " << packetnum << LL_ENDL; sendConfirmPacket(mesgsys, id, decodePacketNum(packetnum), mesgsys->getSender()); + } + else + { + LL_INFOS("Xfer") << "Ignoring xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " recv'd packet " << packetnum << "; expecting " << xferp->mPacketNum << LL_ENDL; + } + 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_back(ack_info); + } + + if (isLastPacket(packetnum)) + { + xferp->processEOF(); + removeXfer(xferp,mReceiveList); + startPendingDownloads(); + } +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 packetnum, const LLHost &remote_host) +{ +#ifdef 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); + + // Ignore a circuit failure here, we'll catch it with another message + mesgsys->sendMessage(remote_host); +} + +/////////////////////////////////////////////////////////// + +static bool find_and_remove(std::multiset<std::string>& files, + const std::string& filename) +{ + std::multiset<std::string>::iterator ptr; + if ( (ptr = files.find(filename)) != files.end()) + { + //erase(filename) erases *all* entries with that key + files.erase(ptr); + return true; + } + return false; +} + +void LLXferManager::expectFileForRequest(const std::string& filename) +{ + mExpectedRequests.insert(filename); +} + +bool LLXferManager::validateFileForRequest(const std::string& filename) +{ + return find_and_remove(mExpectedRequests, filename); +} + +void LLXferManager::expectFileForTransfer(const std::string& filename) +{ + mExpectedTransfers.insert(filename); +} + +bool LLXferManager::validateFileForTransfer(const std::string& filename) +{ + return find_and_remove(mExpectedTransfers, filename); +} + +/* Present in fireengine, not used by viewer +void LLXferManager::expectVFileForRequest(const std::string& filename) +{ + mExpectedVFileRequests.insert(filename); +} + +bool LLXferManager::validateVFileForRequest(const std::string& filename) +{ + return find_and_remove(mExpectedVFileRequests, filename); +} + +void LLXferManager::expectVFileForTransfer(const std::string& filename) +{ + mExpectedVFileTransfers.insert(filename); +} + +bool LLXferManager::validateVFileForTransfer(const std::string& filename) +{ + return find_and_remove(mExpectedVFileTransfers, filename); +} +*/ + +static bool remove_prefix(std::string& filename, const std::string& prefix) +{ + if (std::equal(prefix.begin(), prefix.end(), filename.begin())) + { + filename = filename.substr(prefix.length()); + return true; + } + return false; +} + +static bool verify_cache_filename(const std::string& filename) +{ + //NOTE: This routine is only used to check file names that our own + // code places in the cache directory. As such, it can be limited + // to this very restrictive file name pattern. It does not need to + // handle other characters. The only known uses of this are (with examples): + // sim to sim object pass: fc0b72d8-9456-63d9-a802-a557ef847313.tmp + // sim to viewer mute list: mute_b78eacd0-1244-448e-93ca-28ede242f647.tmp + // sim to viewer task inventory: inventory_d8ab59d2-baf0-0e79-c4c2-a3f99b9fcf45.tmp + + //IMPORTANT: Do not broaden the filenames accepted by this routine + // without careful analysis. Anything allowed by this function can + // be downloaded by the viewer. + + size_t len = filename.size(); + //const boost::regex expr("[0-9a-zA-Z_-]<1,46>\.tmp"); + if (len < 5 || len > 50) + { + return false; + } + for(size_t i=0; i<(len-4); ++i) + { + char c = filename[i]; + bool ok = isalnum(c) || '_'==c || '-'==c; + if (!ok) + { + return false; + } + } + return filename[len-4] == '.' + && filename[len-3] == 't' + && filename[len-2] == 'm' + && filename[len-1] == 'p'; +} + +void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user_data*/) +{ + + U64 id; + std::string local_filename; + 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 */ + LL_INFOS("Xfer") << "xfer request id: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) + << " to " << mesgsys->getSender() << LL_ENDL; + + mesgsys->getStringFast(_PREHASH_XferID, _PREHASH_Filename, local_filename); + + { + U8 local_path_u8; + mesgsys->getU8("XferID", "FilePath", local_path_u8); + local_path = (ELLPath)local_path_u8; + } + + 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) + { // Request for an asset - use a cache file + if(NULL == LLAssetType::lookup(type)) + { + LL_WARNS("Xfer") << "Invalid type for xfer request: " << uuid << ":" + << type_s16 << " to " << mesgsys->getSender() << LL_ENDL; + return; + } + + LL_INFOS("Xfer") << "starting vfile transfer: " << uuid << "," << LLAssetType::lookup(type) << " to " << mesgsys->getSender() << LL_ENDL; + + xferp = (LLXfer *)new LLXfer_VFile(uuid, type); + if (xferp) + { + mSendList.push_front(xferp); + result = xferp->startSend(id,mesgsys->getSender()); + } + else + { + LL_ERRS("Xfer") << "Xfer allcoation error" << LL_ENDL; + } + } + else if (!local_filename.empty()) + { // Was given a file name to send + // See DEV-21775 for detailed security issues + + if (local_path == LL_PATH_NONE) + { + // this handles legacy simulators that are passing objects + // by giving a filename that explicitly names the cache directory + static const std::string legacy_cache_prefix = "data/"; + if (remove_prefix(local_filename, legacy_cache_prefix)) + { + local_path = LL_PATH_CACHE; + } + } + + switch (local_path) + { + case LL_PATH_NONE: + if(!validateFileForTransfer(local_filename)) + { + LL_WARNS("Xfer") << "SECURITY: Unapproved filename '" << local_filename << LL_ENDL; + return; + } + break; + + case LL_PATH_CACHE: + if(!verify_cache_filename(local_filename)) + { + LL_WARNS("Xfer") << "SECURITY: Illegal cache filename '" << local_filename << LL_ENDL; + return; + } + break; + + default: + LL_WARNS("Xfer") << "SECURITY: Restricted file dir enum: " << (U32)local_path << LL_ENDL; + return; + } + + // If we want to use a special path (e.g. LL_PATH_CACHE), we want to make sure we create the + // proper expanded filename. + std::string expanded_filename; + if (local_path != LL_PATH_NONE) + { + expanded_filename = gDirUtilp->getExpandedFilename( local_path, local_filename ); + } + else + { + expanded_filename = local_filename; + } + LL_INFOS("Xfer") << "starting file transfer: " << expanded_filename << " to " << mesgsys->getSender() << LL_ENDL; + + 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) + { + mSendList.push_front(xferp); + result = xferp->startSend(id,mesgsys->getSender()); + } + else + { + LL_ERRS("Xfer") << "Xfer allcoation error" << LL_ENDL; + } + } + else + { // no uuid or filename - use the ID sent + char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */ + LL_INFOS("Xfer") << "starting memory transfer: " + << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << " to " + << mesgsys->getSender() << LL_ENDL; + + xferp = findXferByID(id, mSendList); + + if (xferp) + { + result = xferp->startSend(id,mesgsys->getSender()); + } + else + { + LL_INFOS("Xfer") << "Warning: xfer ID " << U64_BUF << " not found." << LL_ENDL; + 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 + { + LL_INFOS("Xfer") << "Aborting xfer to " << mesgsys->getSender() << " with error: " << result << LL_ENDL; + + 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) + { + // Figure out how many transfers the host has requested + LLHostStatus *host_statusp = findHostStatus(xferp->mRemoteHost); + if (host_statusp) + { + if (host_statusp->mNumActive < mMaxOutgoingXfersPerCircuit) + { // Not many transfers in progress already, so start immediately + xferp->sendNextPacket(); + changeNumActiveXfers(xferp->mRemoteHost,1); + LL_DEBUGS("Xfer") << "Starting xfer ID " << U64_to_str(id) << " immediately" << LL_ENDL; + } + else if (mHardLimitOutgoingXfersPerCircuit == 0 || + (host_statusp->mNumActive + host_statusp->mNumPending) < mHardLimitOutgoingXfersPerCircuit) + { // Must close the file handle and wait for earlier ones to complete + LL_INFOS("Xfer") << " queueing xfer request id " << U64_to_str(id) << ", " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << LL_ENDL; + xferp->closeFileHandle(); // Close the file handle until we're ready to send again + } + else if (mHardLimitOutgoingXfersPerCircuit > 0) + { // Way too many requested ... it's time to stop being nice and kill the circuit + xferp->closeFileHandle(); // Close the file handle in any case + LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(xferp->mRemoteHost); + if (cdp) + { + if (cdp->getTrusted()) + { // Trusted internal circuit - don't kill it + LL_WARNS("Xfer") << "Trusted circuit to " << xferp->mRemoteHost << " has too many xfer requests in the queue " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << LL_ENDL; + } + else + { // Untrusted circuit - time to stop messing around and kill it + LL_WARNS("Xfer") << "Killing circuit to " << xferp->mRemoteHost << " for having too many xfer requests in the queue " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << LL_ENDL; + gMessageSystem->disableCircuit(xferp->mRemoteHost); + } + } + else + { // WTF? Why can't we find a circuit? Try to kill it off + LL_WARNS("Xfer") << "Backlog with circuit to " << xferp->mRemoteHost << " with too many xfer requests in the queue " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << " but no LLCircuitData found???" + << LL_ENDL; + gMessageSystem->disableCircuit(xferp->mRemoteHost); + } + } + } + else + { + LL_WARNS("Xfer") << "LLXferManager::processFileRequest() - no LLHostStatus found for id " << U64_to_str(id) + << " host " << xferp->mRemoteHost << LL_ENDL; + } + } + else + { + LL_WARNS("Xfer") << "LLXferManager::processFileRequest() - no xfer found for id " << U64_to_str(id) << LL_ENDL; + } +} + +/////////////////////////////////////////////////////////// + +// Return true if host is in a transfer-flood sitation. Same check for both internal and external hosts +bool LLXferManager::isHostFlooded(const LLHost & host) +{ + bool flooded = false; + LLHostStatus *host_statusp = findHostStatus(host); + if (host_statusp) + { + flooded = (mHardLimitOutgoingXfersPerCircuit > 0 && + (host_statusp->mNumActive + host_statusp->mNumPending) >= (S32)(mHardLimitOutgoingXfersPerCircuit * 0.8f)); + } + + return flooded; +} + + +/////////////////////////////////////////////////////////// + +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 = findXferByID(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); + } + } +} + +/////////////////////////////////////////////////////////// + +// Called from LLMessageSystem::processAcks() +void LLXferManager::retransmitUnackedPackets() +{ + LLXfer *xferp; + + xfer_list_t::iterator iter = mReceiveList.begin(); + while (iter != mReceiveList.end()) + { + xferp = (*iter); + if (xferp->mStatus == e_LL_XFER_IN_PROGRESS) + { + // if the circuit dies, abort + if (! gMessageSystem->mCircuitInfo.isCircuitAlive( xferp->mRemoteHost )) + { + LL_WARNS("Xfer") << "Xfer found in progress on dead circuit, aborting transfer to " + << xferp->mRemoteHost.getIPandPort() + << LL_ENDL; + xferp->mCallbackResult = LL_ERR_CIRCUIT_GONE; + xferp->processEOF(); + + iter = mReceiveList.erase(iter); // iter is set to next one after the deletion point + delete (xferp); + continue; + } + + } + ++iter; + } + + // Re-build mOutgoingHosts data + updateHostStatus(); + + F32 et; + iter = mSendList.begin(); + while (iter != mSendList.end()) + { + xferp = (*iter); + if (xferp->mWaitingForACK && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_PACKET_TIMEOUT)) + { + if (xferp->mRetries > LL_PACKET_RETRY_LIMIT) + { + LL_INFOS("Xfer") << "dropping xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet retransmit limit exceeded, xfer dropped" << LL_ENDL; + xferp->abort(LL_ERR_TCP_TIMEOUT); + iter = mSendList.erase(iter); + delete xferp; + continue; + } + else + { + LL_INFOS("Xfer") << "resending xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet unconfirmed after: "<< et << " sec, packet " << xferp->mPacketNum << LL_ENDL; + xferp->resendLastPacket(); + } + } + else if ((xferp->mStatus == e_LL_XFER_REGISTERED) && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_XFER_REGISTRATION_TIMEOUT)) + { + LL_INFOS("Xfer") << "registered xfer never requested, xfer dropped" << LL_ENDL; + xferp->abort(LL_ERR_TCP_TIMEOUT); + iter = mSendList.erase(iter); + delete xferp; + continue; + } + else if (xferp->mStatus == e_LL_XFER_ABORTED) + { + LL_WARNS("Xfer") << "Removing aborted xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << LL_ENDL; + iter = mSendList.erase(iter); + delete xferp; + continue; + } + else if (xferp->mStatus == e_LL_XFER_PENDING) + { +// LL_INFOS("Xfer") << "*** numActiveXfers = " << numActiveXfers(xferp->mRemoteHost) << " mMaxOutgoingXfersPerCircuit = " << mMaxOutgoingXfersPerCircuit << LL_ENDL; + if (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit) + { + if (xferp->reopenFileHandle()) + { + LL_WARNS("Xfer") << "Error re-opening file handle for xfer ID " << U64_to_str(xferp->mID) + << " to host " << xferp->mRemoteHost << LL_ENDL; + xferp->abort(LL_ERR_CANNOT_OPEN_FILE); + iter = mSendList.erase(iter); + delete xferp; + continue; + } + else + { // No error re-opening the file, send the first packet + LL_DEBUGS("Xfer") << "Moving pending xfer ID " << U64_to_str(xferp->mID) << " to active" << LL_ENDL; + xferp->sendNextPacket(); + changeNumActiveXfers(xferp->mRemoteHost,1); + } + } + } + ++iter; + } // end while() loop + + // + // HACK - if we're using xfer confirm throttling, throttle our xfer confirms here + // so we don't blow through bandwidth. + // + + while (mXferAckQueue.size()) + { + if (mAckThrottle.checkOverflow(1000.0f*8.0f)) + { + break; + } + //LL_INFOS("Xfer") << "Confirm packet queue length:" << mXferAckQueue.size() << LL_ENDL; + LLXferAckInfo ack_info = mXferAckQueue.front(); + mXferAckQueue.pop_front(); + //LL_INFOS("Xfer") << "Sending confirm packet" << LL_ENDL; + sendConfirmPacket(gMessageSystem, ack_info.mID, ack_info.mPacketNum, ack_info.mRemoteHost); + mAckThrottle.throttleOverflow(1000.f*8.f); // Assume 1000 bytes/packet + } +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::abortRequestById(U64 xfer_id, S32 result_code) +{ + LLXfer * xferp = findXferByID(xfer_id, mReceiveList); + if (xferp) + { + if (xferp->mStatus == e_LL_XFER_IN_PROGRESS) + { + // causes processAbort(); + xferp->abort(result_code); + } + else + { + xferp->mCallbackResult = result_code; + xferp->processEOF(); //should notify requester + removeXfer(xferp, mReceiveList); + } + // Since already removed or marked as aborted no need + // to wait for processAbort() to start new download + startPendingDownloads(); + } +} + +/////////////////////////////////////////////////////////// + +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 = findXferByID(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; + std::list<LLXfer*> pending_downloads; + S32 download_count = 0; + S32 pending_count = 0; + for (xfer_list_t::iterator iter = mReceiveList.begin(); + iter != mReceiveList.end(); + ++iter) + { + xferp = (*iter); + if(xferp->mStatus == e_LL_XFER_PENDING) + { // Count and accumulate pending downloads + ++pending_count; + pending_downloads.push_front(xferp); + } + else if(xferp->mStatus == e_LL_XFER_IN_PROGRESS) + { // Count downloads in progress + ++download_count; + } + } + + S32 start_count = mMaxIncomingXfers - download_count; + + LL_DEBUGS("Xfer") << "LLXferManager::startPendingDownloads() - XFER_IN_PROGRESS: " + << download_count << " XFER_PENDING: " << pending_count + << " startring " << llmin(start_count, pending_count) << LL_ENDL; + + if((start_count > 0) && (pending_count > 0)) + { + S32 result; + for (std::list<LLXfer*>::iterator iter = pending_downloads.begin(); + iter != pending_downloads.end(); ++iter) + { + xferp = *iter; + if (start_count-- <= 0) + break; + result = xferp->startDownload(); + if(result) + { + xferp->abort(result); + ++start_count; + } + } + } +} + +/////////////////////////////////////////////////////////// + +void LLXferManager::addToList(LLXfer* xferp, xfer_list_t & xfer_list, bool is_priority) +{ + if(is_priority) + { + xfer_list.push_back(xferp); + } + else + { + xfer_list.push_front(xferp); + } +} + +/////////////////////////////////////////////////////////// +// Globals and C routines +/////////////////////////////////////////////////////////// + +LLXferManager *gXferManager = NULL; + + +void start_xfer_manager() +{ + gXferManager = new LLXferManager(); +} + +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 (ll_frand() > 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 index ce412ddd26..3be5f5a228 100644 --- a/indra/llmessage/llxfermanager.h +++ b/indra/llmessage/llxfermanager.h @@ -1,223 +1,223 @@ -/**
- * @file llxfermanager.h
- * @brief definition of LLXferManager class for a keeping track of
- * multiple xfers
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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;
-
-#include "llxfer.h"
-#include "message.h"
-#include "llassetstorage.h"
-#include "lldir.h"
-#include <deque>
-#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
-{
- protected:
- S32 mMaxOutgoingXfersPerCircuit;
- S32 mHardLimitOutgoingXfersPerCircuit; // At this limit, kill off the connection
- S32 mMaxIncomingXfers;
-
- bool mUseAckThrottling; // Use ack throttling to cap file xfer bandwidth
- std::deque<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,
- };
-
- // Linked FIFO list, add to the front and pull from back
- typedef std::deque<LLXfer *> xfer_list_t;
- xfer_list_t mSendList;
- xfer_list_t mReceiveList;
-
- typedef std::list<LLHostStatus*> status_list_t;
- status_list_t mOutgoingHosts;
-
- protected:
- // implementation methods
- virtual void startPendingDownloads();
- virtual void addToList(LLXfer* xferp, xfer_list_t & xfer_list, bool is_priority);
- std::multiset<std::string> mExpectedTransfers; // files that are authorized to transfer out
- std::multiset<std::string> mExpectedRequests; // files that are authorized to be downloaded on top of
- std::multiset<std::string> mExpectedVFileTransfers; // files that are authorized to transfer out
- std::multiset<std::string> mExpectedVFileRequests; // files that are authorized to be downloaded on top of
-
- public:
- LLXferManager();
- virtual ~LLXferManager();
-
- virtual void init();
- virtual void cleanup();
-
- void setUseAckThrottling(const bool use);
- void setAckThrottleBPS(const F32 bps);
-
-// list management routines
- virtual LLXfer *findXferByID(U64 id, xfer_list_t & xfer_list);
- virtual void removeXfer (LLXfer *delp, xfer_list_t & xfer_list);
-
- LLHostStatus * findHostStatus(const LLHost &host);
- 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 setHardLimitOutgoingXfersPerCircuit(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);
-
-// file requesting routines
-// .. to file
- virtual U64 requestFile(const std::string& local_filename,
- const std::string& remote_filename,
- ELLPath remote_path,
- const LLHost& remote_host,
- bool delete_remote_on_completion,
- void (*callback)(void**,S32,LLExtStat), void** user_data,
- bool is_priority = false,
- bool use_big_packets = false);
- /*
-// .. to memory
- virtual void requestFile(const std::string& remote_filename,
- ELLPath remote_path,
- const LLHost &remote_host,
- bool delete_remote_on_completion,
- void (*callback)(void*, S32, void**, S32, LLExtStat),
- 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,
- const LLHost& remote_host,
- void (*callback)(void**,S32,LLExtStat), void** user_data,
- bool is_priority = false);
- /**
- When arbitrary files are requested to be transfered (by giving a dir of LL_PATH_NONE)
- they must be "expected", but having something pre-authorize them. This pair of functions
- maintains a pre-authorized list. The first function adds something to the list, the second
- checks if is authorized, removing it if so. In this way, a file is only authorized for
- a single use.
- */
- virtual void expectFileForTransfer(const std::string& filename);
- virtual bool validateFileForTransfer(const std::string& filename);
- /**
- Same idea, but for the viewer about to call InitiateDownload to track what it requested.
- */
- virtual void expectFileForRequest(const std::string& filename);
- virtual bool validateFileForRequest(const std::string& filename);
-
- /**
- Same idea but for VFiles, kept separate to avoid namespace overlap
- */
- /* Present in fireengine, not used by viewer
- virtual void expectVFileForTransfer(const std::string& filename);
- virtual bool validateVFileForTransfer(const std::string& filename);
- virtual void expectVFileForRequest(const std::string& filename);
- virtual bool validateVFileForRequest(const std::string& filename);
- */
-
- 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
- void abortRequestById(U64 xfer_id, S32 result_code);
- virtual void processAbort (LLMessageSystem *mesgsys, void **user_data);
-
- virtual bool isHostFlooded(const LLHost & host);
-};
-
-extern LLXferManager* gXferManager;
-
-// initialization and garbage collection
-void start_xfer_manager();
-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
-
-
-
+/** + * @file llxfermanager.h + * @brief definition of LLXferManager class for a keeping track of + * multiple xfers + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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; + +#include "llxfer.h" +#include "message.h" +#include "llassetstorage.h" +#include "lldir.h" +#include <deque> +#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 +{ + protected: + S32 mMaxOutgoingXfersPerCircuit; + S32 mHardLimitOutgoingXfersPerCircuit; // At this limit, kill off the connection + S32 mMaxIncomingXfers; + + bool mUseAckThrottling; // Use ack throttling to cap file xfer bandwidth + std::deque<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, + }; + + // Linked FIFO list, add to the front and pull from back + typedef std::deque<LLXfer *> xfer_list_t; + xfer_list_t mSendList; + xfer_list_t mReceiveList; + + typedef std::list<LLHostStatus*> status_list_t; + status_list_t mOutgoingHosts; + + protected: + // implementation methods + virtual void startPendingDownloads(); + virtual void addToList(LLXfer* xferp, xfer_list_t & xfer_list, bool is_priority); + std::multiset<std::string> mExpectedTransfers; // files that are authorized to transfer out + std::multiset<std::string> mExpectedRequests; // files that are authorized to be downloaded on top of + std::multiset<std::string> mExpectedVFileTransfers; // files that are authorized to transfer out + std::multiset<std::string> mExpectedVFileRequests; // files that are authorized to be downloaded on top of + + public: + LLXferManager(); + virtual ~LLXferManager(); + + virtual void init(); + virtual void cleanup(); + + void setUseAckThrottling(const bool use); + void setAckThrottleBPS(const F32 bps); + +// list management routines + virtual LLXfer *findXferByID(U64 id, xfer_list_t & xfer_list); + virtual void removeXfer (LLXfer *delp, xfer_list_t & xfer_list); + + LLHostStatus * findHostStatus(const LLHost &host); + 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 setHardLimitOutgoingXfersPerCircuit(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); + +// file requesting routines +// .. to file + virtual U64 requestFile(const std::string& local_filename, + const std::string& remote_filename, + ELLPath remote_path, + const LLHost& remote_host, + bool delete_remote_on_completion, + void (*callback)(void**,S32,LLExtStat), void** user_data, + bool is_priority = false, + bool use_big_packets = false); + /* +// .. to memory + virtual void requestFile(const std::string& remote_filename, + ELLPath remote_path, + const LLHost &remote_host, + bool delete_remote_on_completion, + void (*callback)(void*, S32, void**, S32, LLExtStat), + 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, + const LLHost& remote_host, + void (*callback)(void**,S32,LLExtStat), void** user_data, + bool is_priority = false); + /** + When arbitrary files are requested to be transfered (by giving a dir of LL_PATH_NONE) + they must be "expected", but having something pre-authorize them. This pair of functions + maintains a pre-authorized list. The first function adds something to the list, the second + checks if is authorized, removing it if so. In this way, a file is only authorized for + a single use. + */ + virtual void expectFileForTransfer(const std::string& filename); + virtual bool validateFileForTransfer(const std::string& filename); + /** + Same idea, but for the viewer about to call InitiateDownload to track what it requested. + */ + virtual void expectFileForRequest(const std::string& filename); + virtual bool validateFileForRequest(const std::string& filename); + + /** + Same idea but for VFiles, kept separate to avoid namespace overlap + */ + /* Present in fireengine, not used by viewer + virtual void expectVFileForTransfer(const std::string& filename); + virtual bool validateVFileForTransfer(const std::string& filename); + virtual void expectVFileForRequest(const std::string& filename); + virtual bool validateVFileForRequest(const std::string& filename); + */ + + 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 + void abortRequestById(U64 xfer_id, S32 result_code); + virtual void processAbort (LLMessageSystem *mesgsys, void **user_data); + + virtual bool isHostFlooded(const LLHost & host); +}; + +extern LLXferManager* gXferManager; + +// initialization and garbage collection +void start_xfer_manager(); +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 index 2770200fe5..8db4fca629 100644 --- a/indra/llmessage/llxorcipher.cpp +++ b/indra/llmessage/llxorcipher.cpp @@ -1,128 +1,128 @@ -/**
- * @file llxorcipher.cpp
- * @brief Implementation of LLXORCipher
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llxorcipher.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;
-}
-
-U32 LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
-{
- if(!src || !src_len || !dst || !dst_len || !mPad) return 0;
- U8* pad_end = mPad + mPadLen;
- U32 count = src_len;
- while(count--)
- {
- *dst++ = *src++ ^ *mHead++;
- if(mHead >= pad_end) mHead = mPad;
- }
- return src_len;
-}
-
-U32 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) const
-{
- 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
+/** + * @file llxorcipher.cpp + * @brief Implementation of LLXORCipher + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llxorcipher.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; +} + +U32 LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +{ + if(!src || !src_len || !dst || !dst_len || !mPad) return 0; + U8* pad_end = mPad + mPadLen; + U32 count = src_len; + while(count--) + { + *dst++ = *src++ ^ *mHead++; + if(mHead >= pad_end) mHead = mPad; + } + return src_len; +} + +U32 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) const +{ + 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/llxorcipher.h b/indra/llmessage/llxorcipher.h index 14cea5dc02..50ea997b4d 100644 --- a/indra/llmessage/llxorcipher.h +++ b/indra/llmessage/llxorcipher.h @@ -1,67 +1,67 @@ -/**
- * @file llxorcipher.h
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LLXORCIPHER_H
-#define LLXORCIPHER_H
-
-#include "llcipher.h"
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLXORCipher
-//
-// Implementation of LLCipher which encrypts using a XOR pad.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLXORCipher : public LLCipher
-{
-public:
- LLXORCipher(const U8* pad, U32 pad_len);
- LLXORCipher(const LLXORCipher& cipher);
- virtual ~LLXORCipher();
- LLXORCipher& operator=(const LLXORCipher& cipher);
-
- // Cipher functions
- U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) override;
- U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) override;
- U32 requiredEncryptionSpace(U32 src_len) const override;
-
- // special syntactic-sugar since xor can be performed in place.
- bool encrypt(U8* buf, U32 len) { return encrypt((const U8*)buf, len, buf, len) > 0; }
- bool decrypt(U8* buf, U32 len) { return decrypt((const U8*)buf, len, buf, len) > 0; }
-
-#ifdef _DEBUG
- // This function runs tests to make sure the crc is
- // working. Returns true if it is.
- static bool testHarness();
-#endif
-
-protected:
- void init(const U8* pad, U32 pad_len);
- U8* mPad;
- U8* mHead;
- U32 mPadLen;
-};
-
-#endif
+/** + * @file llxorcipher.h + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLXORCIPHER_H +#define LLXORCIPHER_H + +#include "llcipher.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLXORCipher +// +// Implementation of LLCipher which encrypts using a XOR pad. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLXORCipher : public LLCipher +{ +public: + LLXORCipher(const U8* pad, U32 pad_len); + LLXORCipher(const LLXORCipher& cipher); + virtual ~LLXORCipher(); + LLXORCipher& operator=(const LLXORCipher& cipher); + + // Cipher functions + U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) override; + U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) override; + U32 requiredEncryptionSpace(U32 src_len) const override; + + // special syntactic-sugar since xor can be performed in place. + bool encrypt(U8* buf, U32 len) { return encrypt((const U8*)buf, len, buf, len) > 0; } + bool decrypt(U8* buf, U32 len) { return decrypt((const U8*)buf, len, buf, len) > 0; } + +#ifdef _DEBUG + // This function runs tests to make sure the crc is + // working. Returns true if it is. + static bool testHarness(); +#endif + +protected: + void init(const U8* pad, U32 pad_len); + U8* mPad; + U8* mHead; + U32 mPadLen; +}; + +#endif diff --git a/indra/llmessage/machine.h b/indra/llmessage/machine.h index 67e890d19a..05d366cbc3 100644 --- a/indra/llmessage/machine.h +++ b/indra/llmessage/machine.h @@ -1,95 +1,95 @@ -/**
- * @file machine.h
- * @brief LLMachine class header file
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_MACHINE_H
-#define LL_MACHINE_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; }
- U32 getMachineIP() const { return mHost.getAddress(); }
- 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);
- void setControlPort( S32 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
+/** + * @file machine.h + * @brief LLMachine class header file + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_MACHINE_H +#define LL_MACHINE_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; } + U32 getMachineIP() const { return mHost.getAddress(); } + 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); + void setControlPort( S32 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/message.cpp b/indra/llmessage/message.cpp index b57f8f4513..e705c36ff8 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -1,4055 +1,4055 @@ -/**
- * @file message.cpp
- * @brief LLMessageSystem class implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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 <iomanip>
-#include <iterator>
-#include <sstream>
-
-#include "llapr.h"
-#include "apr_portable.h"
-#include "apr_network_io.h"
-#include "apr_poll.h"
-
-// linden library headers
-#include "llapp.h"
-#include "indra_constants.h"
-#include "lldir.h"
-#include "llerror.h"
-#include "llfasttimer.h"
-#include "llhttpnodeadapter.h"
-#include "llmd5.h"
-#include "llmessagebuilder.h"
-#include "llmessageconfig.h"
-#include "lltemplatemessagedispatcher.h"
-#include "llpumpio.h"
-#include "lltemplatemessagebuilder.h"
-#include "lltemplatemessagereader.h"
-#include "lltrustedmessageservice.h"
-#include "llmessagetemplate.h"
-#include "llmessagetemplateparser.h"
-#include "llsd.h"
-#include "llsdmessagebuilder.h"
-#include "llsdmessagereader.h"
-#include "llsdserialize.h"
-#include "llstring.h"
-#include "lltransfermanager.h"
-#include "lluuid.h"
-#include "llxfermanager.h"
-#include "llquaternion.h"
-#include "u64.h"
-#include "v3dmath.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "lltransfertargetvfile.h"
-#include "llcorehttputil.h"
-#include "llpounceable.h"
-
-// Constants
-//const char* MESSAGE_LOG_FILENAME = "message.log";
-static const F32Seconds CIRCUIT_DUMP_TIMEOUT(30.f);
-static const S32 TRUST_TIME_WINDOW = 3;
-
-// *NOTE: 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;
-};
-
-class LLMessageHandlerBridge : public LLHTTPNode
-{
- virtual bool validate(const std::string& name, LLSD& context) const
- { return true; }
-
- virtual void post(LLHTTPNode::ResponsePtr response, const LLSD& context,
- const LLSD& input) const;
-};
-
-//virtual
-void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response,
- const LLSD& context, const LLSD& input) const
-{
- std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["message-name"];
- char* namePtr = LLMessageStringTable::getInstance()->getString(name.c_str());
-
- LL_DEBUGS() << "Setting mLastSender " << input["sender"].asString() << LL_ENDL;
- gMessageSystem->mLastSender = LLHost(input["sender"].asString());
- gMessageSystem->mPacketsIn += 1;
- gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]);
- LockMessageReader rdr(gMessageSystem->mMessageReader, gMessageSystem->mLLSDMessageReader);
-
- if(gMessageSystem->callHandler(namePtr, false, gMessageSystem))
- {
- response->result(LLSD());
- }
- else
- {
- response->notFound();
- }
-}
-
-LLHTTPRegistration<LLMessageHandlerBridge>
- gHTTPRegistrationMessageWildcard("/message/<message-name>");
-
-//virtual
-LLUseCircuitCodeResponder::~LLUseCircuitCodeResponder()
-{
- // even abstract base classes need a concrete destructor
-}
-
-static const char* nullToEmpty(const char* s)
-{
- static char emptyString[] = "";
- return s? s : emptyString;
-}
-
-void LLMessageSystem::init()
-{
- // initialize member variables
- mVerboseLog = false;
-
- mbError = false;
- mErrorCode = 0;
- mSendReliable = false;
-
- mUnackedListDepth = 0;
- mUnackedListSize = 0;
- mDSMaxListDepth = 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;
-
- mIncomingCompressedSize = 0;
- mCurrentRecvPacketID = 0;
-
- mMessageFileVersionNumber = 0.f;
-
- mTimingCallback = NULL;
- mTimingCallbackData = NULL;
-
- mMessageBuilder = NULL;
- LockMessageReader(mMessageReader, NULL);
-}
-
-// Read file and build message templates
-LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port,
- S32 version_major,
- S32 version_minor,
- S32 version_patch,
- bool failure_is_fatal,
- const F32 circuit_heartbeat_interval, const F32 circuit_timeout) :
- mCircuitInfo(F32Seconds(circuit_heartbeat_interval), F32Seconds(circuit_timeout)),
- mLastMessageFromTrustedMessageService(false)
-{
- init();
-
- mSendSize = 0;
-
- mSystemVersionMajor = version_major;
- mSystemVersionMinor = version_minor;
- mSystemVersionPatch = version_patch;
- mSystemVersionServer = 0;
- mVersionFlags = 0x0;
-
- // default to not accepting packets from not alive circuits
- mbProtected = true;
-
- // default to blocking trusted connections on a public interface if one is specified
- mBlockUntrustedInterface = true;
-
- mSendPacketFailureCount = 0;
-
- mCircuitPrintFreq = F32Seconds(60.f);
-
- loadTemplateFile(filename, failure_is_fatal);
-
- mTemplateMessageBuilder = new LLTemplateMessageBuilder(mMessageTemplates);
- mLLSDMessageBuilder = new LLSDMessageBuilder();
- mMessageBuilder = NULL;
-
- mTemplateMessageReader = new LLTemplateMessageReader(mMessageNumbers);
- mLLSDMessageReader = new LLSDMessageReader();
-
- // initialize various bits of net info
- mSocket = 0;
- mPort = port;
-
- S32 error = start_net(mSocket, mPort);
- if (error != 0)
- {
- mbError = true;
- mErrorCode = error;
- }
-// LL_DEBUGS("Messaging") << << "*** port: " << mPort << LL_ENDL;
-
- //
- // Create the data structure that we can poll on
- //
- if (!gAPRPoolp)
- {
- LL_ERRS("Messaging") << "No APR pool before message system initialization!" << LL_ENDL;
- 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;
-
- F64Seconds mt_sec = getMessageTimeSeconds();
- mResendDumpTime = mt_sec;
- mMessageCountTime = mt_sec;
- mCircuitPrintTime = mt_sec;
- mCurrentMessageTime = F64Seconds(mt_sec);
-
- // Constants for dumping output based on message processing time/count
- mNumMessageCounts = 0;
- mMaxMessageCounts = 200; // >= 0 means dump warnings
- mMaxMessageTime = F32Seconds(1.f);
-
- mTrueReceiveSize = 0;
-
- mReceiveTime = F32Seconds(0.f);
-}
-
-
-
-// Read file and build message templates
-void LLMessageSystem::loadTemplateFile(const std::string& filename, bool failure_is_fatal)
-{
- if(filename.empty())
- {
- LL_ERRS("Messaging") << "No template filename specified" << LL_ENDL;
- mbError = true;
- return;
- }
-
- std::string template_body;
- if(!_read_file_into_string(template_body, filename))
- {
- if (failure_is_fatal) {
- LL_ERRS("Messaging") << "Failed to open template: " << filename << LL_ENDL;
- } else {
- LL_WARNS("Messaging") << "Failed to open template: " << filename << LL_ENDL;
- }
- mbError = true;
- return;
- }
-
- LLTemplateTokenizer tokens(template_body);
- LLTemplateParser parsed(tokens);
- mMessageFileVersionNumber = parsed.getVersion();
- S32 count = 0;
- for(LLTemplateParser::message_iterator iter = parsed.getMessagesBegin();
- iter != parsed.getMessagesEnd();
- iter++)
- {
- addTemplate(*iter);
- count++;
- }
- LL_INFOS("Messaging") << "Read " << count << " messages from " << filename << LL_ENDL;
-}
-
-
-LLMessageSystem::~LLMessageSystem()
-{
- mMessageTemplates.clear(); // don't delete templates.
- for_each(mMessageNumbers.begin(), mMessageNumbers.end(), DeletePairedPointer());
- mMessageNumbers.clear();
-
- if (!mbError)
- {
- end_net(mSocket);
- }
- mSocket = 0;
-
- delete mTemplateMessageReader;
- mTemplateMessageReader = NULL;
-
- delete mTemplateMessageBuilder;
- mTemplateMessageBuilder = NULL;
- mMessageBuilder = NULL;
-
- delete mLLSDMessageReader;
- mLLSDMessageReader = NULL;
-
- delete mLLSDMessageBuilder;
- mLLSDMessageBuilder = NULL;
-
- delete mPollInfop;
- mPollInfop = NULL;
-
- mIncomingCompressedSize = 0;
- mCurrentRecvPacketID = 0;
-}
-
-void LLMessageSystem::clearReceiveState()
-{
- mCurrentRecvPacketID = 0;
- mIncomingCompressedSize = 0;
- mLastSender.invalidate();
- mLastReceivingIF.invalidate();
- mMessageReader->clearMessage();
- mLastMessageFromTrustedMessageService = false;
-}
-
-
-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;
- }
-}
-
-bool LLMessageSystem::isTrustedSender(const LLHost& host) const
-{
- LLCircuitData* cdp = mCircuitInfo.findCircuit(host);
- if(NULL == cdp)
- {
- return false;
- }
- return cdp->getTrusted();
-}
-
-void LLMessageSystem::receivedMessageFromTrustedSender()
-{
- mLastMessageFromTrustedMessageService = true;
-}
-
-bool LLMessageSystem::isTrustedSender() const
-{
- return mLastMessageFromTrustedMessageService ||
- isTrustedSender(getSender());
-}
-
-static LLMessageSystem::message_template_name_map_t::const_iterator
-findTemplate(const LLMessageSystem::message_template_name_map_t& templates,
- std::string name)
-{
- const char* namePrehash = LLMessageStringTable::getInstance()->getString(name.c_str());
- if(NULL == namePrehash) {return templates.end();}
- return templates.find(namePrehash);
-}
-
-bool LLMessageSystem::isTrustedMessage(const std::string& name) const
-{
- message_template_name_map_t::const_iterator iter =
- findTemplate(mMessageTemplates, name);
- if(iter == mMessageTemplates.end()) {return false;}
- return iter->second->getTrust() == MT_TRUST;
-}
-
-bool LLMessageSystem::isUntrustedMessage(const std::string& name) const
-{
- message_template_name_map_t::const_iterator iter =
- findTemplate(mMessageTemplates, name);
- if(iter == mMessageTemplates.end()) {return false;}
- return iter->second->getTrust() == MT_NOTRUST;
-}
-
-LLCircuitData* LLMessageSystem::findCircuit(const LLHost& host,
- bool resetPacketId)
-{
- LLCircuitData* 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);
-
- if(resetPacketId)
- {
- // 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);
-
- if(resetPacketId)
- {
- // reset packet in ID
- cdp->setPacketInID(mCurrentRecvPacketID);
- }
- }
- }
- }
- return cdp;
-}
-
-// Returns true if a valid, on-circuit message has been received.
-// Requiring a non-const LockMessageChecker reference ensures that
-// mMessageReader has been set to mTemplateMessageReader.
-bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )
-{
- // Pump
- 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.
- mCurrentMessageTime = totalTime();
- mMessageCountTime = getMessageTimeSeconds();
- }
-
- // loop until either no packets or a valid packet
- // i.e., burn through packets from unregistered circuits
- S32 receive_size = 0;
- 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();
-
- receive_size = mTrueReceiveSize;
- mLastSender = mPacketRing.getLastSender();
- mLastReceivingIF = mPacketRing.getLastReceivingInterface();
-
- if (receive_size < (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 (receive_size > 0)
- {
- LL_WARNS("Messaging") << "Invalid (too short) packet discarded " << receive_size << LL_ENDL;
- 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[--receive_size];
- true_rcv_size = receive_size;
- if(receive_size >= ((S32)(acks * sizeof(TPACKETID) + LL_MINIMUM_VALID_PACKET_SIZE)))
- {
- receive_size -= acks * sizeof(TPACKETID);
- }
- else
- {
- // mal-formed packet. ignore it and continue with
- // the next one
- LL_WARNS("Messaging") << "Malformed packet received. Packet size "
- << receive_size << " with invalid no. of acks " << acks
- << LL_ENDL;
- valid_packet = false;
- continue;
- }
- }
-
- // process the message as normal
- mIncomingCompressedSize = zeroCodeExpand(&buffer, &receive_size);
- mCurrentRecvPacketID = ntohl(*((U32*)(&buffer[1])));
- host = getSender();
-
- const bool resetPacketId = true;
- cdp = findCircuit(host, resetPacketId);
-
- // 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);
- //LL_INFOS("Messaging") << "got ack: " << packet_id << LL_ENDL;
- 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)
- //{
- // LL_WARNS("Messaging") << "DISCARDED PACKET HOST MISMATCH! HOST: "
- // << host << " CIRCUIT: "
- // << mCircuitInfo.mCurrentCircuit->mHost
- // << LL_ENDL;
- //}
- // ***************************************
- //mCircuitInfo.mCurrentCircuit->mAcks.put(mCurrentRecvPacketID);
- cdp->collectRAck(mCurrentRecvPacketID);
- }
-
- LL_DEBUGS("Messaging") << "Discarding duplicate resend from " << host << LL_ENDL;
- if(mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: <- " << host;
- std::string tbuf;
- tbuf = llformat( "\t%6d\t%6d\t%6d ", receive_size, (mIncomingCompressedSize ? mIncomingCompressedSize : receive_size), mCurrentRecvPacketID);
- str << tbuf << "(unknown)"
- << (recv_reliable ? " reliable" : "")
- << " resent "
- << ((acks > 0) ? "acks" : "")
- << " DISCARD DUPLICATE";
- LL_INFOS("Messaging") << str.str() << LL_ENDL;
- }
- 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
- bool trusted = cdp && cdp->getTrusted();
- valid_packet = mTemplateMessageReader->validateMessage(
- buffer,
- receive_size,
- host,
- trusted);
- if (!valid_packet)
- {
- clearReceiveState();
- }
-
- // UseCircuitCode is allowed in even from an invalid circuit, so that
- // we can toss circuits around.
- if(
- valid_packet &&
- !cdp &&
- (mTemplateMessageReader->getMessageName() !=
- _PREHASH_UseCircuitCode))
- {
- logMsgFromInvalidCircuit( host, recv_reliable );
- clearReceiveState();
- valid_packet = false;
- }
-
- if(
- valid_packet &&
- cdp &&
- !cdp->getTrusted() &&
- mTemplateMessageReader->isTrusted())
- {
- logTrustedMsgFromUntrustedCircuit( host );
- clearReceiveState();
-
- sendDenyTrustedCircuit(host);
- valid_packet = false;
- }
-
- if( valid_packet )
- {
- logValidMsg(cdp, host, recv_reliable, recv_resent, acks>0 );
- valid_packet = mTemplateMessageReader->readMessage(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)
- {
- 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))
- {
- LL_WARNS("Messaging") << "Invalid Packet from invalid circuit " << host << LL_ENDL;
- mOffCircuitPackets++;
- }
- else
- {
- mInvalidOnCircuitPackets++;
- }
- }
- }
- } while (!valid_packet && receive_size > 0);
-
- F64Seconds 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(LockMessageChecker&, F32 collect_time)
-{
- F64Seconds 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(collect_time);
-
- if (!mDenyTrustedCircuitSet.empty())
- {
- LL_INFOS("Messaging") << "Sending queued DenyTrustedCircuit messages." << LL_ENDL;
- 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 >= F32Seconds(0.f))
- {
- // This is one of the only places where we're required to get REAL message system time.
- mReceiveTime = 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::copyMessageReceivedToSend()
-{
- // NOTE: babbage: switch builder to match reader to avoid
- // converting message format
- if(mMessageReader == mTemplateMessageReader)
- {
- mMessageBuilder = mTemplateMessageBuilder;
- }
- else
- {
- mMessageBuilder = mLLSDMessageBuilder;
- }
- mSendReliable = false;
- mMessageBuilder->newMessage(mMessageReader->getMessageName());
- mMessageReader->copyToBuilder(*mMessageBuilder);
-}
-
-LLSD LLMessageSystem::getReceivedMessageLLSD() const
-{
- LLSDMessageBuilder builder;
- mMessageReader->copyToBuilder(builder);
- return builder.getMessage();
-}
-
-LLSD LLMessageSystem::getBuiltMessageLLSD() const
-{
- LLSD result;
- if (mLLSDMessageBuilder == mMessageBuilder)
- {
- result = mLLSDMessageBuilder->getMessage();
- }
- else
- {
- // TODO: implement as below?
- LL_ERRS() << "Message not built as LLSD." << LL_ENDL;
- }
- return result;
-}
-
-LLSD LLMessageSystem::wrapReceivedTemplateData() const
-{
- if(mMessageReader == mTemplateMessageReader)
- {
- LLTemplateMessageBuilder builder(mMessageTemplates);
- builder.newMessage(mMessageReader->getMessageName());
- mMessageReader->copyToBuilder(builder);
- U8 buffer[MAX_BUFFER_SIZE];
- const U8 offset_to_data = 0;
- U32 size = builder.buildMessage(buffer, MAX_BUFFER_SIZE,
- offset_to_data);
- std::vector<U8> binary_data(buffer, buffer+size);
- LLSD wrapped_data = LLSD::emptyMap();
- wrapped_data["binary-template-data"] = binary_data;
- return wrapped_data;
- }
- else
- {
- return getReceivedMessageLLSD();
- }
-}
-
-LLSD LLMessageSystem::wrapBuiltTemplateData() const
-{
- LLSD result;
- if (mLLSDMessageBuilder == mMessageBuilder)
- {
- result = getBuiltMessageLLSD();
- }
- else
- {
- U8 buffer[MAX_BUFFER_SIZE];
- const U8 offset_to_data = 0;
- U32 size = mTemplateMessageBuilder->buildMessage(
- buffer, MAX_BUFFER_SIZE,
- offset_to_data);
- std::vector<U8> binary_data(buffer, buffer+size);
- LLSD wrapped_data = LLSD::emptyMap();
- wrapped_data["binary-template-data"] = binary_data;
- result = wrapped_data;
- }
- return result;
-}
-
-LLStoredMessagePtr LLMessageSystem::getReceivedMessage() const
-{
- const std::string& name = mMessageReader->getMessageName();
- LLSD message = wrapReceivedTemplateData();
-
- return LLStoredMessagePtr(new LLStoredMessage(name, message));
-}
-
-LLStoredMessagePtr LLMessageSystem::getBuiltMessage() const
-{
- const std::string& name = mMessageBuilder->getMessageName();
- LLSD message = wrapBuiltTemplateData();
-
- return LLStoredMessagePtr(new LLStoredMessage(name, message));
-}
-
-S32 LLMessageSystem::sendMessage(const LLHost &host, LLStoredMessagePtr message)
-{
- return sendMessage(host, message->mName.c_str(), message->mMessage);
-}
-
-
-void LLMessageSystem::clearMessage()
-{
- mSendReliable = false;
- mMessageBuilder->clearMessage();
-}
-
-// set block to add data to within current message
-void LLMessageSystem::nextBlockFast(const char *blockname)
-{
- mMessageBuilder->nextBlock(blockname);
-}
-
-void LLMessageSystem::nextBlock(const char *blockname)
-{
- nextBlockFast(LLMessageStringTable::getInstance()->getString(blockname));
-}
-
-bool LLMessageSystem::isSendFull(const char* blockname)
-{
- char* stringTableName = NULL;
- if(NULL != blockname)
- {
- stringTableName = LLMessageStringTable::getInstance()->getString(blockname);
- }
- return isSendFullFast(stringTableName);
-}
-
-bool LLMessageSystem::isSendFullFast(const char* blockname)
-{
- return mMessageBuilder->isMessageFull(blockname);
-}
-
-
-// blow away the last block of a message, return false if that leaves no blocks or there wasn't a block to remove
-// TODO: Babbage: Remove this horror.
-bool LLMessageSystem::removeLastBlock()
-{
- return mMessageBuilder->removeLastBlock();
-}
-
-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)
-{
- F32Seconds timeout;
-
- LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
- if (cdp)
- {
- timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS,
- F32Seconds(LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged()));
- }
- else
- {
- timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX;
- }
-
- constexpr S32 retries = 0;
- constexpr bool ping_based_timeout = false;
- return sendReliable(host, retries, ping_based_timeout, timeout, callback, callback_data);
-}
-
-// send the message via a UDP packet
-S32 LLMessageSystem::sendReliable( const LLHost &host,
- S32 retries,
- bool ping_based_timeout,
- F32Seconds 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, F32Seconds(LL_RELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged()));
- }
- else
- {
- timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, F32Seconds(LL_RELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX));
- }
- }
-
- mSendReliable = true;
- mReliablePacketParams.set(host, retries, ping_based_timeout, timeout,
- callback, callback_data,
- const_cast<char*>(mMessageBuilder->getMessageName()));
- return sendMessage(host);
-}
-
-void LLMessageSystem::forwardMessage(const LLHost &host)
-{
- copyMessageReceivedToSend();
- sendMessage(host);
-}
-
-void LLMessageSystem::forwardReliable(const LLHost &host)
-{
- copyMessageReceivedToSend();
- sendReliable(host);
-}
-
-void LLMessageSystem::forwardReliable(const U32 circuit_code)
-{
- copyMessageReceivedToSend();
- sendReliable(findHost(circuit_code));
-}
-
-S32 LLMessageSystem::forwardReliable( const LLHost &host,
- S32 retries,
- bool ping_based_timeout,
- F32Seconds timeout,
- void (*callback)(void **,S32),
- void ** callback_data)
-{
- copyMessageReceivedToSend();
- return sendReliable(host, retries, ping_based_timeout, timeout, callback, callback_data);
-}
-
-S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data)
-{
- F32Seconds timeout;
-
- LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
- if (cdp)
- {
- timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS,
- F32Seconds(LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged()));
- }
- else
- {
- timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX;
- }
-
- S32 send_bytes = 0;
- if (mMessageBuilder->getMessageSize())
- {
- mSendReliable = true;
- // No need for ping-based retry as not going to retry
- mReliablePacketParams.set(host, 0, false, timeout, callback,
- callback_data,
- const_cast<char*>(mMessageBuilder->getMessageName()));
- send_bytes = sendMessage(host);
- clearMessage();
- }
- else
- {
- delete callback_data;
- }
- return send_bytes;
-}
-
-S32 LLMessageSystem::flushReliable(const LLHost &host)
-{
- S32 send_bytes = 0;
- if (mMessageBuilder->getMessageSize())
- {
- send_bytes = sendReliable(host);
- }
- clearMessage();
- return send_bytes;
-}
-
-// This can be called from signal handlers,
-// so should should not use LL_INFOS().
-S32 LLMessageSystem::sendMessage(const LLHost &host)
-{
- if (! mMessageBuilder->isBuilt())
- {
- mSendSize = mMessageBuilder->buildMessage(
- mSendBuffer,
- MAX_BUFFER_SIZE,
- 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)
- {
- LL_INFOS_ONCE("Messaging") << "MSG: -> " << host << "\tUNKNOWN CIRCUIT:\t"
- << mMessageBuilder->getMessageName() << LL_ENDL;
- }
- LL_WARNS_ONCE("Messaging") << "sendMessage - Trying to send "
- << mMessageBuilder->getMessageName() << " on unknown circuit "
- << host << LL_ENDL;
- 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)
- {
- LL_INFOS("Messaging") << "MSG: -> " << host << "\tDEAD CIRCUIT\t\t"
- << mMessageBuilder->getMessageName() << LL_ENDL;
- }
- LL_WARNS("Messaging") << "sendMessage - Trying to send message "
- << mMessageBuilder->getMessageName() << " to dead circuit "
- << host << LL_ENDL;
- return 0;
- }
- }
-
- // NOTE: babbage: LLSD message -> HTTP, template message -> UDP
- if(mMessageBuilder == mLLSDMessageBuilder)
- {
- LLSD message = mLLSDMessageBuilder->getMessage();
-
- UntrustedCallback_t cb = NULL;
- if ((mSendReliable) && (mReliablePacketParams.mCallback))
- {
- cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1);
- }
-
- LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro",
- boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this,
- host.getUntrustedSimulatorCap(),
- mLLSDMessageBuilder->getMessageName(), message, cb));
-
- mSendReliable = false;
- mReliablePacketParams.clear();
- return 1;
- }
-
- // zero out the flags and packetid. Subtract 1 here so that we do
- // not overwrite the offset if it was set set in buildMessage().
- memset(mSendBuffer, 0, LL_PACKET_ID_SIZE - 1);
-
- // add the send id to the front of the message
- cdp->nextPacketOutID();
-
- // Packet ID size is always 4
- *((S32*)&mSendBuffer[PHL_PACKET_ID]) = htonl(cdp->getPacketOutID());
-
- // Compress the message, which will usually reduce its size.
- U8 * buf_ptr = (U8 *)mSendBuffer;
- U32 buffer_length = mSendSize;
- mMessageBuilder->compressMessage(buf_ptr, buffer_length);
-
- if (buffer_length > 1500)
- {
- if((mMessageBuilder->getMessageName() != _PREHASH_ChildAgentUpdate)
- && (mMessageBuilder->getMessageName() != _PREHASH_SendXferPacket))
- {
- LL_WARNS("Messaging") << "sendMessage - Trying to send "
- << ((buffer_length > 4000) ? "EXTRA " : "")
- << "BIG message " << mMessageBuilder->getMessageName() << " - "
- << buffer_length << LL_ENDL;
- }
- }
- 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) &&
- (mMessageBuilder->getMessageName() != _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)); /* Flawfinder: ignore */
- // 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.
- LL_ERRS("Messaging") << "Buffer packing failed due to size.." << LL_ENDL;
- }
- }
-
- // 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( (S32Bytes)buffer_length );
- }
-
- if(mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: -> " << host;
- std::string buffer;
- buffer = llformat( "\t%6d\t%6d\t%6d ", mSendSize, buffer_length, cdp->getPacketOutID());
- str << buffer
- << mMessageBuilder->getMessageName()
- << (mSendReliable ? " reliable " : "");
- if(is_ack_appended)
- {
- str << "\tACKS:\t";
- std::ostream_iterator<TPACKETID> append(str, " ");
- std::copy(acks.begin(), acks.end(), append);
- }
- LL_INFOS("Messaging") << str.str() << LL_ENDL;
- }
-
-
- mPacketsOut++;
- mTotalBytesOut += buffer_length;
-
- mSendReliable = false;
- mReliablePacketParams.clear();
- return buffer_length;
-}
-
-void LLMessageSystem::logMsgFromInvalidCircuit( const LLHost& host, bool recv_reliable )
-{
- if(mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: <- " << host;
- std::string buffer;
- buffer = llformat( "\t%6d\t%6d\t%6d ", mMessageReader->getMessageSize(), (mIncomingCompressedSize ? mIncomingCompressedSize: mMessageReader->getMessageSize()), mCurrentRecvPacketID);
- str << buffer
- << nullToEmpty(mMessageReader->getMessageName())
- << (recv_reliable ? " reliable" : "")
- << " REJECTED";
- LL_INFOS("Messaging") << str.str() << LL_ENDL;
- }
- // 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)
- {
- LL_WARNS("Messaging") << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << LL_ENDL;
- }
- else
- {
- // TODO: babbage: work out if we need these
- // mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber;
- mMessageCountList[mNumMessageCounts].mMessageBytes = mMessageReader->getMessageSize();
- mMessageCountList[mNumMessageCounts].mInvalid = true;
- mNumMessageCounts++;
- }
-}
-
-S32 LLMessageSystem::sendMessage(
- const LLHost &host,
- const char* name,
- const LLSD& message)
-{
- if (!(host.isOk()))
- {
- LL_WARNS("Messaging") << "trying to send message to invalid host" << LL_ENDL;
- return 0;
- }
-
- UntrustedCallback_t cb = NULL;
- if ((mSendReliable) && (mReliablePacketParams.mCallback))
- {
- cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1);
- }
-
- LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro",
- boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this,
- host.getUntrustedSimulatorCap(), name, message, cb));
- return 1;
-}
-
-void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host )
-{
- // RequestTrustedCircuit is how we establish trust, so don't spam
- // if it's received on a trusted circuit. JC
- if (strcmp(mMessageReader->getMessageName(), "RequestTrustedCircuit"))
- {
- LL_WARNS("Messaging") << "Received trusted message on untrusted circuit. "
- << "Will reply with deny. "
- << "Message: " << nullToEmpty(mMessageReader->getMessageName())
- << " Host: " << host << LL_ENDL;
- }
-
- if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
- {
- LL_WARNS("Messaging") << "got more than " << MAX_MESSAGE_COUNT_NUM
- << " packets without clearing counts"
- << LL_ENDL;
- }
- else
- {
- // TODO: babbage: work out if we need these
- //mMessageCountList[mNumMessageCounts].mMessageNum
- // = mCurrentRMessageTemplate->mMessageNumber;
- mMessageCountList[mNumMessageCounts].mMessageBytes
- = mMessageReader->getMessageSize();
- 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)
- {
- LL_WARNS("Messaging") << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << LL_ENDL;
- }
- else
- {
- // TODO: babbage: work out if we need these
- //mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber;
- mMessageCountList[mNumMessageCounts].mMessageBytes = mMessageReader->getMessageSize();
- mMessageCountList[mNumMessageCounts].mInvalid = false;
- mNumMessageCounts++;
- }
-
- if (cdp)
- {
- // update circuit packet ID tracking (missing/out of order packets)
- cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent );
- cdp->addBytesIn( (S32Bytes)mTrueReceiveSize );
- }
-
- if(mVerboseLog)
- {
- std::ostringstream str;
- str << "MSG: <- " << host;
- std::string buffer;
- buffer = llformat( "\t%6d\t%6d\t%6d ", mMessageReader->getMessageSize(), (mIncomingCompressedSize ? mIncomingCompressedSize : mMessageReader->getMessageSize()), mCurrentRecvPacketID);
- str << buffer
- << nullToEmpty(mMessageReader->getMessageName())
- << (recv_reliable ? " reliable" : "")
- << (recv_resent ? " resent" : "")
- << (recv_acks ? " acks" : "");
- LL_INFOS("Messaging") << str.str() << LL_ENDL;
- }
-}
-
-void LLMessageSystem::sanityCheck()
-{
-// TODO: babbage: reinstate
-
-// if (!mCurrentRMessageData)
-// {
-// LL_ERRS("Messaging") << "mCurrentRMessageData is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentRMessageTemplate)
-// {
-// LL_ERRS("Messaging") << "mCurrentRMessageTemplate is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentRTemplateBlock)
-// {
-// LL_ERRS("Messaging") << "mCurrentRTemplateBlock is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentRDataBlock)
-// {
-// LL_ERRS("Messaging") << "mCurrentRDataBlock is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentSMessageData)
-// {
-// LL_ERRS("Messaging") << "mCurrentSMessageData is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentSMessageTemplate)
-// {
-// LL_ERRS("Messaging") << "mCurrentSMessageTemplate is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentSTemplateBlock)
-// {
-// LL_ERRS("Messaging") << "mCurrentSTemplateBlock is NULL" << LL_ENDL;
-// }
-
-// if (!mCurrentSDataBlock)
-// {
-// LL_ERRS("Messaging") << "mCurrentSDataBlock is NULL" << LL_ENDL;
-// }
-}
-
-void LLMessageSystem::showCircuitInfo()
-{
- LL_INFOS("Messaging") << mCircuitInfo << LL_ENDL;
-}
-
-
-void LLMessageSystem::dumpCircuitInfo()
-{
- LL_DEBUGS("Messaging") << mCircuitInfo << LL_ENDL;
-}
-
-/* virtual */
-U32 LLMessageSystem::getOurCircuitCode()
-{
- return mOurCircuitCode;
-}
-
-void LLMessageSystem::getCircuitInfo(LLSD& info) const
-{
- mCircuitInfo.getInfo(info);
-}
-
-// 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)
-{
- LL_INFOS("Messaging") << "LLMessageSystem::disableCircuit for " << host << LL_ENDL;
- 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())
- {
- LL_INFOS("Messaging") << "Circuit " << code << " removed from list" << LL_ENDL;
- //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);
-
- LL_INFOS("Messaging") << "Host " << LLHost(old_ip, old_port) << " circuit " << code << " removed from lookup table" << LL_ENDL;
- gMessageSystem->mIPPortToCircuitCode.erase(ip_port);
- }
- mCircuitInfo.removeCircuitData(host);
- }
- else
- {
- // Sigh, since we can open circuits which don't have circuit
- // codes, it's possible for this to happen...
-
- LL_WARNS("Messaging") << "Couldn't find circuit code for " << host << LL_ENDL;
- }
-
-}
-
-
-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())
- {
- LL_DEBUGS("Messaging") << "checkCircuitBlocked: Unknown circuit " << circuit << LL_ENDL;
- return true;
- }
-
- LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
- if (cdp)
- {
- return cdp->isBlocked();
- }
- else
- {
- LL_INFOS("Messaging") << "checkCircuitBlocked(circuit): Unknown host - " << host << LL_ENDL;
- return false;
- }
-}
-
-bool LLMessageSystem::checkCircuitAlive(const U32 circuit)
-{
- LLHost host = findHost(circuit);
-
- if (!host.isOk())
- {
- LL_DEBUGS("Messaging") << "checkCircuitAlive: Unknown circuit " << circuit << LL_ENDL;
- return false;
- }
-
- LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
- if (cdp)
- {
- return cdp->isAlive();
- }
- else
- {
- LL_INFOS("Messaging") << "checkCircuitAlive(circuit): Unknown host - " << host << LL_ENDL;
- return false;
- }
-}
-
-bool LLMessageSystem::checkCircuitAlive(const LLHost &host)
-{
- LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
- if (cdp)
- {
- return cdp->isAlive();
- }
- else
- {
- LL_DEBUGS("Messaging") << "checkCircuitAlive(host): Unknown host - " << host << LL_ENDL;
- 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();
- }
-}
-
-void LLMessageSystem::setMaxMessageTime(const F32 seconds)
-{
- mMaxMessageTime = F32Seconds(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;
-}
-
-// LLPounceable supports callWhenReady(), to permit clients to queue up (e.g.)
-// callback registrations for when gMessageSystem is first assigned
-LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
-
-// 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())
- {
- LL_WARNS("Messaging") << "AssignCircuitCode, bad session id. Expecting "
- << msg->getMySessionID() << " but got " << session_id
- << LL_ENDL;
- return;
- }
- U32 code;
- msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code);
- if (!code)
- {
- LL_ERRS("Messaging") << "Assigning circuit code of zero!" << LL_ENDL;
- }
-
- msg->mOurCircuitCode = code;
- LL_INFOS("Messaging") << "Circuit code " << code << " assigned." << LL_ENDL;
-}
-*/
-
-// 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)
- {
- LL_WARNS("Messaging") << "addCircuitCode: zero circuit code" << LL_ENDL;
- return false;
- }
- code_session_map_t::iterator it = mCircuitCodes.find(code);
- if(it == mCircuitCodes.end())
- {
- LL_INFOS("Messaging") << "New circuit code " << code << " added" << LL_ENDL;
- //msg->mCircuitCodes[circuit_code] = circuit_code;
-
- mCircuitCodes.insert(code_session_map_t::value_type(code, session_id));
- }
- else
- {
- LL_INFOS("Messaging") << "Duplicate circuit code " << code << " added" << LL_ENDL;
- }
- 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** user)
-{
- 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.
- LL_WARNS("Messaging") << "UseCircuitCode for " << circuit_code_in
- << " received without AddCircuitCode message - aborting"
- << LL_ENDL;
- 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)
- {
- LL_WARNS("Messaging") << "UseCircuitCode unmatched session id. Got "
- << session_id << " but expected " << (*it).second
- << LL_ENDL;
- 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
- LL_INFOS("Messaging") << "Got duplicate UseCircuitCode for circuit " << circuit_code_in << " to " << msg->getSender() << LL_ENDL;
- 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);
- LL_INFOS("Messaging") << "Removing derelict lookup entry for circuit " << circuit_code_old << " to " << LLHost(old_ip, old_port) << LL_ENDL;
- }
-
- if (circuit_code_old)
- {
- LLHost cur_host(ip, port);
-
- LL_WARNS("Messaging") << "Disabling existing circuit for " << cur_host << LL_ENDL;
- msg->disableCircuit(cur_host);
- if (circuit_code_old == circuit_code_in)
- {
- LL_WARNS("Messaging") << "Asymmetrical circuit to ip/port lookup!" << LL_ENDL;
- LL_WARNS("Messaging") << "Multiple circuit codes for " << cur_host << " probably!" << LL_ENDL;
- LL_WARNS("Messaging") << "Permanently disabling circuit" << LL_ENDL;
- return;
- }
- else
- {
- LL_WARNS("Messaging") << "Circuit code changed for " << msg->getSender()
- << " from " << circuit_code_old << " to "
- << circuit_code_in << LL_ENDL;
- }
- }
-
- // 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 != nullptr;
-
- 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;
-
- LL_INFOS("Messaging") << "Circuit code " << circuit_code_in << " from "
- << msg->getSender() << " for agent " << id << " in session "
- << session_id << LL_ENDL;
-
- const LLUseCircuitCodeResponder* responder =
- (const LLUseCircuitCodeResponder*) user;
- if(responder)
- {
- responder->complete(msg->getSender(), id);
- }
- }
- else
- {
- LL_WARNS("Messaging") << "Got zero circuit code in use_circuit_code" << LL_ENDL;
- }
-}
-
-// static
-void LLMessageSystem::processError(LLMessageSystem* msg, void**)
-{
- S32 error_code = 0;
- msg->getS32("Data", "Code", error_code);
- std::string error_token;
- msg->getString("Data", "Token", error_token);
-
- LLUUID error_id;
- msg->getUUID("Data", "ID", error_id);
- std::string error_system;
- msg->getString("Data", "System", error_system);
-
- std::string error_message;
- msg->getString("Data", "Message", error_message);
-
- LL_WARNS("Messaging") << "Message error from " << msg->getSender() << " - "
- << error_code << " " << error_token << " " << error_id << " \""
- << error_system << "\" \"" << error_message << "\"" << LL_ENDL;
-}
-
-
-static LLHTTPNode& messageRootNode()
-{
- static LLHTTPNode root_node;
- static bool initialized = false;
- if (!initialized) {
- initialized = true;
- LLHTTPRegistrar::buildAllServices(root_node);
- }
-
- return root_node;
-}
-
-//static
-void LLMessageSystem::dispatch(
- const std::string& msg_name,
- const LLSD& message)
-{
- LLPointer<LLSimpleResponse> responsep = LLSimpleResponse::create();
- dispatch(msg_name, message, responsep);
-}
-
-//static
-void LLMessageSystem::dispatch(
- const std::string& msg_name,
- const LLSD& message,
- LLHTTPNode::ResponsePtr responsep)
-{
- if ((gMessageSystem->mMessageTemplates.find
- (LLMessageStringTable::getInstance()->getString(msg_name.c_str())) ==
- gMessageSystem->mMessageTemplates.end()) &&
- !LLMessageConfig::isValidMessage(msg_name))
- {
- LL_WARNS("Messaging") << "Ignoring unknown message " << msg_name << LL_ENDL;
- responsep->notFound("Invalid message name");
- return;
- }
-
- std::string path = "/message/" + msg_name;
- LLSD context;
- const LLHTTPNode* handler = messageRootNode().traverse(path, context);
- if (!handler)
- {
- LL_WARNS("Messaging") << "LLMessageService::dispatch > no handler for "
- << path << LL_ENDL;
- return;
- }
- // enable this for output of message names
- LL_DEBUGS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL;
- LL_DEBUGS("Messaging") << "context: " << context << LL_ENDL;
- LL_DEBUGS("Messaging") << "message: " << message << LL_ENDL;
-
- handler->post(responsep, context, message);
-}
-
-//static
-void LLMessageSystem::dispatchTemplate(const std::string& msg_name,
- const LLSD& message,
- LLHTTPNode::ResponsePtr responsep)
-{
- LLTemplateMessageDispatcher dispatcher(*(gMessageSystem->mTemplateMessageReader));
- dispatcher.dispatch(msg_name, message, responsep);
-}
-
-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 = LLMessageStringTable::getInstance()->getString(iter->first.c_str());
-
- if (templates.find(name) == templates.end())
- {
- LL_INFOS("AppInit") << " " << type
- << " ban list contains unrecognized message "
- << name << LL_ENDL;
- }
- }
-}
-
-void LLMessageSystem::setMessageBans(
- const LLSD& trusted, const LLSD& untrusted)
-{
- LL_DEBUGS("AppInit") << "LLMessageSystem::setMessageBans:" << LL_ENDL;
- 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)
- {
- LL_INFOS("AppInit") << " " << name << " banned from "
- << (ban_from_trusted ? "TRUSTED " : " ")
- << (ban_from_untrusted ? "UNTRUSTED " : " ")
- << LL_ENDL;
- any_set = true;
- }
- }
-
- if (!any_set)
- {
- LL_DEBUGS("AppInit") << " no messages banned" << LL_ENDL;
- }
-
- check_for_unrecognized_messages("trusted", trusted, mMessageTemplates);
- check_for_unrecognized_messages("untrusted", untrusted, mMessageTemplates);
-}
-
-S32 LLMessageSystem::sendError(
- const LLHost& host,
- const LLUUID& agent_id,
- S32 code,
- const std::string& token,
- const LLUUID& id,
- const std::string& system,
- const std::string& message,
- const LLSD& data)
-{
- newMessage("Error");
- nextBlockFast(_PREHASH_AgentData);
- addUUIDFast(_PREHASH_AgentID, agent_id);
- nextBlockFast(_PREHASH_Data);
- addS32("Code", code);
- addString("Token", token);
- addUUID("ID", id);
- addString("System", system);
- std::string temp;
- temp = message;
- if(temp.size() > (size_t)MTUBYTES) temp.resize((size_t)MTUBYTES);
- addString("Message", message);
- LLPointer<LLSDBinaryFormatter> formatter = new LLSDBinaryFormatter;
- std::ostringstream ostr;
- formatter->format(data, ostr);
- temp = ostr.str();
- bool pack_data = true;
- static const std::string ERROR_MESSAGE_NAME("Error");
- if (LLMessageConfig::getMessageFlavor(ERROR_MESSAGE_NAME) ==
- LLMessageConfig::TEMPLATE_FLAVOR)
- {
- S32 msg_size = temp.size() + mMessageBuilder->getMessageSize();
- if(msg_size >= ETHERNET_MTU_BYTES)
- {
- pack_data = false;
- }
- }
- if(pack_data)
- {
- addBinaryData("Data", (void*)temp.c_str(), temp.size());
- }
- else
- {
- LL_WARNS("Messaging") << "Data and message were too large -- data removed."
- << LL_ENDL;
- addBinaryData("Data", NULL, 0);
- }
- return sendReliable(host);
-}
-
-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);
-// LL_DEBUGS("Messaging") << "ack recvd' from " << host << " for packet " << (TPACKETID)packet_id << LL_ENDL;
- cdp->ackReliablePacket(packet_id);
- }
- if (!cdp->getUnackedPacketCount())
- {
- // Remove this circuit from the list of circuits with unacked packets
- gMessageSystem->mCircuitInfo.mUnackedCircuitMap.erase(host);
- }
- }
-}
-
-
-/*
-void process_log_messages(LLMessageSystem* msg, void**)
-{
- U8 log_message;
-
- msg->getU8Fast(_PREHASH_Options, _PREHASH_Enable, log_message);
-
- if (log_message)
- {
- LL_INFOS("Messaging") << "Starting logging via message" << LL_ENDL;
- msg->startLogging();
- }
- else
- {
- LL_INFOS("Messaging") << "Stopping logging via message" << LL_ENDL;
- 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)
- {
- LL_WARNS("Messaging") << "Attempt to create trusted circuit without circuit data: "
- << msg->getSender() << LL_ENDL;
- 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;
- }
-
- U32 untrusted_interface = msg->getUntrustedInterface().getAddress();
- U32 last_interface = msg->getReceivingInterface().getAddress();
- if ( ( untrusted_interface != INVALID_HOST_IP_ADDRESS ) && ( untrusted_interface == last_interface ) )
- {
- if( msg->getBlockUntrustedInterface() )
- {
- LL_WARNS("Messaging") << "Ignoring CreateTrustedCircuit on public interface from host: "
- << msg->getSender() << LL_ENDL;
- return;
- }
- else
- {
- LL_WARNS("Messaging") << "Processing CreateTrustedCircuit on public interface from host: "
- << msg->getSender() << LL_ENDL;
- }
- }
-
- char their_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
- S32 size = msg->getSizeFast(_PREHASH_DataBlock, _PREHASH_Digest);
- if(size != MD5HEX_STR_BYTES)
- {
- // ignore requests which pack the wrong amount of data.
- return;
- }
- msg->getBinaryDataFast(_PREHASH_DataBlock, _PREHASH_Digest, their_digest, MD5HEX_STR_BYTES);
- their_digest[MD5HEX_STR_SIZE - 1] = '\0';
- if(msg->isMatchingDigestForWindowAndUUIDs(their_digest, TRUST_TIME_WINDOW, local_id, remote_id))
- {
- cdp->setTrusted(true);
- LL_INFOS("Messaging") << "Trusted digest from " << msg->getSender() << LL_ENDL;
- 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.
- LL_WARNS("Messaging") << "Ignoring bad digest from known trusted circuit: " << their_digest
- << " host: " << msg->getSender() << LL_ENDL;
- return;
- }
- else
- {
- LL_WARNS("Messaging") << "Bad digest from known circuit: " << their_digest
- << " host: " << msg->getSender() << LL_ENDL;
- 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;
- }
-
- U32 untrusted_interface = msg->getUntrustedInterface().getAddress();
- U32 last_interface = msg->getReceivingInterface().getAddress();
- if ( ( untrusted_interface != INVALID_HOST_IP_ADDRESS ) && ( untrusted_interface == last_interface ) )
- {
- if( msg->getBlockUntrustedInterface() )
- {
- LL_WARNS("Messaging") << "Ignoring DenyTrustedCircuit on public interface from host: "
- << msg->getSender() << LL_ENDL;
- return;
- }
- else
- {
- LL_WARNS("Messaging") << "Processing DenyTrustedCircuit on public interface from host: "
- << msg->getSender() << LL_ENDL;
- }
- }
-
-
- // 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.
- // *TODO: probably should keep a count of number of resends
- // per circuit, and stop resending after a while.
- LL_INFOS("Messaging") << "Got DenyTrustedCircuit. Sending CreateTrustedCircuit to "
- << msg->getSender() << LL_ENDL;
- msg->sendCreateTrustedCircuit(msg->getSender(), local_id, remote_id);
-}
-
-
-void dump_prehash_files()
-{
- U32 i;
- std::string filename("../../indra/llmessage/message_prehash.h");
- LLFILE* fp = LLFile::fopen(filename, "w"); /* Flawfinder: ignore */
- if (fp)
- {
- fprintf(
- fp,
- "/**\n"
- " * @file message_prehash.h\n"
- " * @brief header file of externs of prehashed variables plus defines.\n"
- " *\n"
- " * $LicenseInfo:firstyear=2003&license=viewerlgpl$"
- " * $/LicenseInfo$"
- " */\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 const gPrehashVersionNumber;\n\n");
- for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
- {
- if (!LLMessageStringTable::getInstance()->mEmpty[i] && LLMessageStringTable::getInstance()->mString[i][0] != '.')
- {
- fprintf(fp, "extern char const* const _PREHASH_%s;\n", LLMessageStringTable::getInstance()->mString[i]);
- }
- }
- fprintf(fp, "\n\n#endif\n");
- fclose(fp);
- }
- filename = std::string("../../indra/llmessage/message_prehash.cpp");
- fp = LLFile::fopen(filename, "w"); /* Flawfinder: ignore */
- if (fp)
- {
- fprintf(
- fp,
- "/**\n"
- " * @file message_prehash.cpp\n"
- " * @brief file of prehashed variables\n"
- " *\n"
- " * $LicenseInfo:firstyear=2003&license=viewerlgpl$"
- " * $/LicenseInfo$"
- " */\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 const gPrehashVersionNumber = %.3ff;\n\n", gMessageSystem->mMessageFileVersionNumber);
- for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
- {
- if (!LLMessageStringTable::getInstance()->mEmpty[i] && LLMessageStringTable::getInstance()->mString[i][0] != '.')
- {
- fprintf(fp, "char const* const _PREHASH_%s = LLMessageStringTable::getInstance()->getString(\"%s\");\n", LLMessageStringTable::getInstance()->mString[i], LLMessageStringTable::getInstance()->mString[i]);
- }
- }
- 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,
- const LLUseCircuitCodeResponder* responder,
- bool failure_is_fatal,
- const F32 circuit_heartbeat_interval,
- const F32 circuit_timeout)
-{
- gMessageSystem = new LLMessageSystem(
- template_name,
- port,
- version_major,
- version_minor,
- version_patch,
- failure_is_fatal,
- circuit_heartbeat_interval,
- circuit_timeout);
- g_shared_secret.assign(secret);
-
- if (!gMessageSystem)
- {
- LL_ERRS("AppInit") << "Messaging system initialization failed." << LL_ENDL;
- return false;
- }
-
- // bail if system encountered an error.
- if(!gMessageSystem->isOK())
- {
- return false;
- }
-
- if (b_dump_prehash_file)
- {
- dump_prehash_files();
- exit(0);
- }
- else
- {
- if (gMessageSystem->mMessageFileVersionNumber != gPrehashVersionNumber)
- {
- LL_INFOS("AppInit") << "Message template version does not match prehash version number" << LL_ENDL;
- LL_INFOS("AppInit") << "Run simulator with -prehash command line option to rebuild prehash data" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("AppInit") << "Message template version matches prehash version number" << LL_ENDL;
- }
- }
-
- 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, (void**)responder);
- gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck, process_packet_ack, 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);
- gMessageSystem->setHandlerFunc("Error", LLMessageSystem::processError);
-
- // 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";
- LL_INFOS("Messaging") << str.str() << LL_ENDL;
-}
-
-void LLMessageSystem::stopLogging()
-{
- if(mVerboseLog)
- {
- mVerboseLog = false;
- LL_INFOS("Messaging") << "END MESSAGE LOG" << LL_ENDL;
- }
-}
-
-void LLMessageSystem::summarizeLogs(std::ostream& str)
-{
- std::string buffer;
- std::string tmp_str;
- F32 run_time = mMessageSystemTimer.getElapsedTimeF32();
- str << "START MESSAGE LOG SUMMARY" << std::endl;
- buffer = llformat( "Run time: %12.3f seconds", run_time);
-
- // Incoming
- str << buffer << std::endl << "Incoming:" << std::endl;
- tmp_str = U64_to_str(mTotalBytesIn);
- buffer = llformat( "Total bytes received: %20s (%5.2f kbits per second)", tmp_str.c_str(), ((F32)mTotalBytesIn * 0.008f) / run_time);
- str << buffer << std::endl;
- tmp_str = U64_to_str(mPacketsIn);
- buffer = llformat( "Total packets received: %20s (%5.2f packets per second)", tmp_str.c_str(), ((F32) mPacketsIn / run_time));
- str << buffer << std::endl;
- buffer = llformat( "Average packet size: %20.0f bytes", (F32)mTotalBytesIn / (F32)mPacketsIn);
- str << buffer << std::endl;
- tmp_str = U64_to_str(mReliablePacketsIn);
- buffer = llformat( "Total reliable packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mReliablePacketsIn)/((F32) mPacketsIn + 1));
- str << buffer << std::endl;
- tmp_str = U64_to_str(mCompressedPacketsIn);
- buffer = llformat( "Total compressed packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mCompressedPacketsIn)/((F32) mPacketsIn + 1));
- str << buffer << std::endl;
- S64 savings = mUncompressedBytesIn - mCompressedBytesIn;
- tmp_str = U64_to_str(savings);
- buffer = llformat( "Total compression savings: %20s bytes", tmp_str.c_str());
- str << buffer << std::endl;
- tmp_str = U64_to_str(savings/(mCompressedPacketsIn +1));
- buffer = llformat( "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mUncompressedBytesIn)/((F32) mCompressedBytesIn+1));
- str << buffer << std::endl;
- tmp_str = U64_to_str(savings/(mPacketsIn+1));
- buffer = llformat( "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mTotalBytesIn + (F32) savings)/((F32) mTotalBytesIn + 1.f));
-
- // Outgoing
- str << buffer << std::endl << std::endl << "Outgoing:" << std::endl;
- tmp_str = U64_to_str(mTotalBytesOut);
- buffer = llformat( "Total bytes sent: %20s (%5.2f kbits per second)", tmp_str.c_str(), ((F32)mTotalBytesOut * 0.008f) / run_time );
- str << buffer << std::endl;
- tmp_str = U64_to_str(mPacketsOut);
- buffer = llformat( "Total packets sent: %20s (%5.2f packets per second)", tmp_str.c_str(), ((F32)mPacketsOut / run_time));
- str << buffer << std::endl;
- buffer = llformat( "Average packet size: %20.0f bytes", (F32)mTotalBytesOut / (F32)mPacketsOut);
- str << buffer << std::endl;
- tmp_str = U64_to_str(mReliablePacketsOut);
- buffer = llformat( "Total reliable packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mReliablePacketsOut)/((F32) mPacketsOut + 1));
- str << buffer << std::endl;
- tmp_str = U64_to_str(mCompressedPacketsOut);
- buffer = llformat( "Total compressed packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mCompressedPacketsOut)/((F32) mPacketsOut + 1));
- str << buffer << std::endl;
- savings = mUncompressedBytesOut - mCompressedBytesOut;
- tmp_str = U64_to_str(savings);
- buffer = llformat( "Total compression savings: %20s bytes", tmp_str.c_str());
- str << buffer << std::endl;
- tmp_str = U64_to_str(savings/(mCompressedPacketsOut +1));
- buffer = llformat( "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mUncompressedBytesOut)/((F32) mCompressedBytesOut+1));
- str << buffer << std::endl;
- tmp_str = U64_to_str(savings/(mPacketsOut+1));
- buffer = llformat( "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mTotalBytesOut + (F32) savings)/((F32) mTotalBytesOut + 1.f));
- str << buffer << std::endl << std::endl;
- buffer = llformat( "SendPacket failures: %20d", mSendPacketFailureCount);
- str << buffer << std::endl;
- buffer = llformat( "Dropped packets: %20d", mDroppedPackets);
- str << buffer << std::endl;
- buffer = llformat( "Resent packets: %20d", mResentPackets);
- str << buffer << std::endl;
- buffer = llformat( "Failed reliable resends: %20d", mFailedResendPackets);
- str << buffer << std::endl;
- buffer = llformat( "Off-circuit rejected packets: %17d", mOffCircuitPackets);
- str << buffer << std::endl;
- buffer = llformat( "On-circuit invalid packets: %17d", mInvalidOnCircuitPackets);
- str << buffer << std::endl << std::endl;
-
- str << "Decoding: " << std::endl;
- buffer = llformat( "%35s%10s%10s%10s%10s", "Message", "Count", "Time", "Max", "Avg");
- str << buffer << std:: endl;
- F32 avg;
- for (message_template_name_map_t::const_iterator iter = mMessageTemplates.begin(),
- end = mMessageTemplates.end();
- iter != end; iter++)
- {
- const LLMessageTemplate* mt = iter->second;
- if(mt->mTotalDecoded > 0)
- {
- avg = mt->mTotalDecodeTime / (F32)mt->mTotalDecoded;
- buffer = llformat( "%35s%10u%10f%10f%10f", mt->mName, mt->mTotalDecoded, mt->mTotalDecodeTime, mt->mMaxDecodeTimePerMsg, avg);
- str << buffer << std::endl;
- }
- }
- str << "END MESSAGE LOG SUMMARY" << std::endl;
-}
-
-void end_messaging_system(bool print_summary)
-{
- gTransferManager.cleanup();
- LLTransferTargetVFile::updateQueue(true); // shutdown LLTransferTargetVFile
- if (gMessageSystem)
- {
- gMessageSystem->stopLogging();
-
- if (print_summary)
- {
- std::ostringstream str;
- gMessageSystem->summarizeLogs(str);
- LL_INFOS("Messaging") << str.str().c_str() << LL_ENDL;
- }
-
- delete static_cast<LLMessageSystem*>(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)
- {
- LL_DEBUGS("Messaging") << "Dump: " << mNumMessageCounts << " messages processed in " << mReceiveTime << " seconds" << LL_ENDL;
- for (message_template_name_map_t::const_iterator iter = mMessageTemplates.begin(),
- end = mMessageTemplates.end();
- iter != end; iter++)
- {
- const LLMessageTemplate* mt = iter->second;
- if (mt->mReceiveCount > 0)
- {
- LL_INFOS("Messaging") << "Num: " << std::setw(3) << mt->mReceiveCount << " Bytes: " << std::setw(6) << mt->mReceiveBytes
- << " Invalid: " << std::setw(3) << mt->mReceiveInvalid << " " << mt->mName << " " << ll_round(100 * mt->mDecodeTimeThisFrame / mReceiveTime.value()) << "%" << LL_ENDL;
- }
- }
- }
-}
-
-
-
-bool LLMessageSystem::isClear() const
-{
- return mMessageBuilder->isClear();
-}
-
-
-S32 LLMessageSystem::flush(const LLHost &host)
-{
- if (mMessageBuilder->getMessageSize())
- {
- S32 sentbytes = sendMessage(host);
- clearMessage();
- return sentbytes;
- }
- else
- {
- return 0;
- }
-}
-
-U32 LLMessageSystem::getListenPort( void ) const
-{
- return mPort;
-}
-
-// TODO: babbage: remove this horror!
-S32 LLMessageSystem::zeroCodeAdjustCurrentSendTotal()
-{
- if(mMessageBuilder == mLLSDMessageBuilder)
- {
- // babbage: don't compress LLSD messages, so delta is 0
- return 0;
- }
-
- if (! mMessageBuilder->isBuilt())
- {
- mSendSize = mMessageBuilder->buildMessage(
- mSendBuffer,
- MAX_BUFFER_SIZE,
- 0);
- }
- // TODO: babbage: remove this horror
- mMessageBuilder->setBuilt(false);
-
- S32 count = mSendSize;
-
- S32 net_gain = 0;
- U8 num_zeroes = 0;
-
- U8 *inptr = (U8 *)mSendBuffer;
-
-// skip the packet id field
-
- for (U32 ii = 0; ii < LL_PACKET_ID_SIZE; ++ii)
- {
- 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_MINIMUM_VALID_PACKET_SIZE)
- {
- LL_WARNS("Messaging") << "zeroCodeExpand() called with data_size of " << *data_size
- << LL_ENDL;
- }
-
- mTotalBytesIn += *data_size;
-
- // if we're not zero-coded, simply return.
- if (!(*data[0] & LL_ZERO_CODE_FLAG))
- {
- 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 ii = 0; ii < LL_PACKET_ID_SIZE; ++ii)
- {
- 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]))
- {
- LL_WARNS("Messaging") << "attempt to write past reasonable encoded buffer size 1" << LL_ENDL;
- callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
- outptr = mEncodedRecvBuffer;
- break;
- }
- if (!((*outptr++ = *inptr++)))
- {
- while (((count--)) && (!(*inptr)))
- {
- *outptr++ = *inptr++;
- if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-256]))
- {
- LL_WARNS("Messaging") << "attempt to write past reasonable encoded buffer size 2" << LL_ENDL;
- 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)]))
- {
- LL_WARNS("Messaging") << "attempt to write past reasonable encoded buffer size 3" << LL_ENDL;
- 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)
- {
- LL_ERRS("Messaging") << templatep->mName << " already used as a template name!"
- << LL_ENDL;
- }
- 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
- {
- LL_ERRS("Messaging") << name << " is not a known message name!" << LL_ENDL;
- }
-}
-
-bool LLMessageSystem::callHandler(const char *name,
- bool trustedSource, LLMessageSystem* msg)
-{
- name = LLMessageStringTable::getInstance()->getString(name);
- message_template_name_map_t::const_iterator iter;
- iter = mMessageTemplates.find(name);
- if(iter == mMessageTemplates.end())
- {
- LL_WARNS("Messaging") << "LLMessageSystem::callHandler: unknown message "
- << name << LL_ENDL;
- return false;
- }
-
- const LLMessageTemplate* msg_template = iter->second;
- if (msg_template->isBanned(trustedSource))
- {
- LL_WARNS("Messaging") << "LLMessageSystem::callHandler: banned message "
- << name
- << " from "
- << (trustedSource ? "trusted " : "untrusted ")
- << "source" << LL_ENDL;
- 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())
- {
- return false;
- }
-
- exception_t& ex = it->second;
- msg_exception_callback ex_cb = ex.first;
-
- if (!ex_cb)
- {
- LL_WARNS("Messaging") << "LLMessageSystem::callExceptionFunc: bad message exception callback." << LL_ENDL;
- return false;
- }
-
- (ex_cb)(this, ex.second, exception);
-
- return true;
-}
-
-void LLMessageSystem::setTimingFunc(msg_timing_callback func, void* data)
-{
- mTimingCallback = func;
- mTimingCallbackData = data;
-}
-
-bool LLMessageSystem::isCircuitCodeKnown(U32 code) const
-{
- if(mCircuitCodes.find(code) == mCircuitCodes.end())
- return false;
- return true;
-}
-
-bool LLMessageSystem::isMessageFast(const char *msg)
-{
- return msg == mMessageReader->getMessageName();
-}
-
-
-char* LLMessageSystem::getMessageName()
-{
- return const_cast<char*>(mMessageReader->getMessageName());
-}
-
-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;
-}
-
-bool LLMessageSystem::generateDigestForNumberAndUUIDs(
- char* digest,
- const U32 number,
- const LLUUID& id1,
- const LLUUID& id2) const
-{
- // *NOTE: This method is needlessly inefficient. Instead of
- // calling LLUUID::asString, it should just call
- // LLUUID::toString().
-
- const char *colon = ":";
- char tbuf[16]; /* Flawfinder: ignore */
- LLMD5 d;
- std::string id1string = id1.asString();
- std::string id2string = id2.asString();
- 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)); /* Flawfinder: ignore */
- }
-
- 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)); /* Flawfinder: ignore */
- }
- d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
-
- if( (char*) id2str != NULL)
- {
- d.update(id2str, (U32)strlen((char *) id2str)); /* Flawfinder: ignore */
- }
-
- 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())
- {
- LL_ERRS("Messaging") << "Trying to generate complex digest on a machine without a shared secret!" << LL_ENDL;
- }
-
- U32 now = (U32)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())
- {
- LL_ERRS("Messaging") << "Trying to compare complex digests on a machine without a shared secret!" << LL_ENDL;
- }
-
- char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
- U32 now = (U32)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())
- {
- LL_ERRS("Messaging") << "Trying to generate simple digest on a machine without a shared secret!" << LL_ENDL;
- }
-
- U32 now = (U32)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())
- {
- LL_ERRS("Messaging") << "Trying to compare simple digests on a machine without a shared secret!" << LL_ENDL;
- }
-
- 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())
- {
- LL_WARNS("Messaging") << "Can't send CreateTrustedCircuit to " << host << " because we don't have the local end point ID" << LL_ENDL;
- return;
- }
- if (id2.isNull())
- {
- LL_WARNS("Messaging") << "Can't send CreateTrustedCircuit to " << host << " because we don't have the remote end point ID" << LL_ENDL;
- 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);
- LL_INFOS("Messaging") << "xmitting digest: " << digest << " Host: " << host << LL_ENDL;
- sendMessage(host);
-}
-
-void LLMessageSystem::sendDenyTrustedCircuit(const LLHost &host)
-{
- mDenyTrustedCircuitSet.insert(host);
-}
-
-void LLMessageSystem::reallySendDenyTrustedCircuit(const LLHost &host)
-{
- LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
- if (!cdp)
- {
- LL_WARNS("Messaging") << "Not sending DenyTrustedCircuit to host without a circuit." << LL_ENDL;
- return;
- }
- LL_INFOS("Messaging") << "Sending DenyTrustedCircuit to " << host << LL_ENDL;
- 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 )
-{
- LockMessageChecker lmc(this);
-
- std::string shared_secret = get_shared_secret();
- if(shared_secret.empty())
- {
- LL_ERRS("Messaging") << "Trying to establish bidirectional trust on a machine without a shared secret!" << LL_ENDL;
- }
- 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 (lmc.checkMessages( frame_count ))
- {
- if (isMessageFast(_PREHASH_CompletePingCheck) &&
- (getSender() == host))
- {
- break;
- }
- }
- lmc.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.
- lmc.checkMessages(frame_count);
- lmc.processAcks();
- ms_sleep(1);
- }
-}
-
-
-void LLMessageSystem::dumpPacketToLog()
-{
- LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing.getLastSender() << LL_ENDL;
- LL_WARNS("Messaging") << "Packet Size:" << mTrueReceiveSize << LL_ENDL;
- char line_buffer[256]; /* Flawfinder: ignore */
- S32 i;
- S32 cur_line_pos = 0;
- S32 cur_line = 0;
-
- for (i = 0; i < mTrueReceiveSize; i++)
- {
- S32 offset = cur_line_pos * 3;
- snprintf(line_buffer + offset, sizeof(line_buffer) - offset,
- "%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */
- cur_line_pos++;
- if (cur_line_pos >= 16)
- {
- cur_line_pos = 0;
- LL_WARNS("Messaging") << "PD:" << cur_line << "PD:" << line_buffer << LL_ENDL;
- cur_line++;
- }
- }
- if (cur_line_pos)
- {
- LL_WARNS("Messaging") << "PD:" << cur_line << "PD:" << line_buffer << LL_ENDL;
- }
-}
-
-
-//static
-U64Microseconds LLMessageSystem::getMessageTimeUsecs(const bool update)
-{
- if (gMessageSystem)
- {
- if (update)
- {
- gMessageSystem->mCurrentMessageTime = totalTime();
- }
- return gMessageSystem->mCurrentMessageTime;
- }
- else
- {
- return totalTime();
- }
-}
-
-//static
-F64Seconds LLMessageSystem::getMessageTimeSeconds(const bool update)
-{
- if (gMessageSystem)
- {
- if (update)
- {
- gMessageSystem->mCurrentMessageTime = totalTime();
- }
- return gMessageSystem->mCurrentMessageTime;
- }
- else
- {
- return F64Seconds(totalTime());
- }
-}
-
-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;
-}
-
-typedef std::map<const char*, LLMessageBuilder*> BuilderMap;
-
-void LLMessageSystem::newMessageFast(const char *name)
-{
- //LL_DEBUGS("Messaging") << "creating new message: " << name << LL_ENDL;
- LLMessageConfig::Flavor message_flavor =
- LLMessageConfig::getMessageFlavor(name);
- LLMessageConfig::Flavor server_flavor =
- LLMessageConfig::getServerDefaultFlavor();
-
- if(message_flavor == LLMessageConfig::TEMPLATE_FLAVOR)
- {
- mMessageBuilder = mTemplateMessageBuilder;
- }
- else if (message_flavor == LLMessageConfig::LLSD_FLAVOR)
- {
- mMessageBuilder = mLLSDMessageBuilder;
- }
- // NO_FLAVOR
- else
- {
- if (server_flavor == LLMessageConfig::LLSD_FLAVOR)
- {
- mMessageBuilder = mLLSDMessageBuilder;
- }
- // TEMPLATE_FLAVOR or NO_FLAVOR
- else
- {
- mMessageBuilder = mTemplateMessageBuilder;
- }
- }
- mSendReliable = false;
- mMessageBuilder->newMessage(name);
-}
-
-void LLMessageSystem::newMessage(const char *name)
-{
- newMessageFast(LLMessageStringTable::getInstance()->getString(name));
-}
-
-void LLMessageSystem::addBinaryDataFast(const char *varname, const void *data, S32 size)
-{
- mMessageBuilder->addBinaryData(varname, data, size);
-}
-
-void LLMessageSystem::addBinaryData(const char *varname, const void *data, S32 size)
-{
- mMessageBuilder->addBinaryData(LLMessageStringTable::getInstance()->getString(varname),data, size);
-}
-
-void LLMessageSystem::addS8Fast(const char *varname, S8 v)
-{
- mMessageBuilder->addS8(varname, v);
-}
-
-void LLMessageSystem::addS8(const char *varname, S8 v)
-{
- mMessageBuilder->addS8(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addU8Fast(const char *varname, U8 v)
-{
- mMessageBuilder->addU8(varname, v);
-}
-
-void LLMessageSystem::addU8(const char *varname, U8 v)
-{
- mMessageBuilder->addU8(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addS16Fast(const char *varname, S16 v)
-{
- mMessageBuilder->addS16(varname, v);
-}
-
-void LLMessageSystem::addS16(const char *varname, S16 v)
-{
- mMessageBuilder->addS16(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addU16Fast(const char *varname, U16 v)
-{
- mMessageBuilder->addU16(varname, v);
-}
-
-void LLMessageSystem::addU16(const char *varname, U16 v)
-{
- mMessageBuilder->addU16(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addF32Fast(const char *varname, F32 v)
-{
- mMessageBuilder->addF32(varname, v);
-}
-
-void LLMessageSystem::addF32(const char *varname, F32 v)
-{
- mMessageBuilder->addF32(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addS32Fast(const char *varname, S32 v)
-{
- mMessageBuilder->addS32(varname, v);
-}
-
-void LLMessageSystem::addS32(const char *varname, S32 v)
-{
- mMessageBuilder->addS32(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addU32Fast(const char *varname, U32 v)
-{
- mMessageBuilder->addU32(varname, v);
-}
-
-void LLMessageSystem::addU32(const char *varname, U32 v)
-{
- mMessageBuilder->addU32(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addU64Fast(const char *varname, U64 v)
-{
- mMessageBuilder->addU64(varname, v);
-}
-
-void LLMessageSystem::addU64(const char *varname, U64 v)
-{
- mMessageBuilder->addU64(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addF64Fast(const char *varname, F64 v)
-{
- mMessageBuilder->addF64(varname, v);
-}
-
-void LLMessageSystem::addF64(const char *varname, F64 v)
-{
- mMessageBuilder->addF64(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addIPAddrFast(const char *varname, U32 v)
-{
- mMessageBuilder->addIPAddr(varname, v);
-}
-
-void LLMessageSystem::addIPAddr(const char *varname, U32 v)
-{
- mMessageBuilder->addIPAddr(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addIPPortFast(const char *varname, U16 v)
-{
- mMessageBuilder->addIPPort(varname, v);
-}
-
-void LLMessageSystem::addIPPort(const char *varname, U16 v)
-{
- mMessageBuilder->addIPPort(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addBOOLFast(const char* varname, bool v)
-{
- mMessageBuilder->addBOOL(varname, v);
-}
-
-void LLMessageSystem::addBOOL(const char* varname, bool v)
-{
- mMessageBuilder->addBOOL(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addStringFast(const char* varname, const char* v)
-{
- mMessageBuilder->addString(varname, v);
-}
-
-void LLMessageSystem::addString(const char* varname, const char* v)
-{
- mMessageBuilder->addString(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addStringFast(const char* varname, const std::string& v)
-{
- mMessageBuilder->addString(varname, v);
-}
-
-void LLMessageSystem::addString(const char* varname, const std::string& v)
-{
- mMessageBuilder->addString(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addVector3Fast(const char *varname, const LLVector3& v)
-{
- mMessageBuilder->addVector3(varname, v);
-}
-
-void LLMessageSystem::addVector3(const char *varname, const LLVector3& v)
-{
- mMessageBuilder->addVector3(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addVector4Fast(const char *varname, const LLVector4& v)
-{
- mMessageBuilder->addVector4(varname, v);
-}
-
-void LLMessageSystem::addVector4(const char *varname, const LLVector4& v)
-{
- mMessageBuilder->addVector4(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addVector3dFast(const char *varname, const LLVector3d& v)
-{
- mMessageBuilder->addVector3d(varname, v);
-}
-
-void LLMessageSystem::addVector3d(const char *varname, const LLVector3d& v)
-{
- mMessageBuilder->addVector3d(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-void LLMessageSystem::addQuatFast(const char *varname, const LLQuaternion& v)
-{
- mMessageBuilder->addQuat(varname, v);
-}
-
-void LLMessageSystem::addQuat(const char *varname, const LLQuaternion& v)
-{
- mMessageBuilder->addQuat(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-
-void LLMessageSystem::addUUIDFast(const char *varname, const LLUUID& v)
-{
- mMessageBuilder->addUUID(varname, v);
-}
-
-void LLMessageSystem::addUUID(const char *varname, const LLUUID& v)
-{
- mMessageBuilder->addUUID(LLMessageStringTable::getInstance()->getString(varname), v);
-}
-
-S32 LLMessageSystem::getCurrentSendTotal() const
-{
- return mMessageBuilder->getMessageSize();
-}
-
-void LLMessageSystem::getS8Fast(const char *block, const char *var, S8 &u,
- S32 blocknum)
-{
- mMessageReader->getS8(block, var, u, blocknum);
-}
-
-void LLMessageSystem::getS8(const char *block, const char *var, S8 &u,
- S32 blocknum)
-{
- getS8Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), u, blocknum);
-}
-
-void LLMessageSystem::getU8Fast(const char *block, const char *var, U8 &u,
- S32 blocknum)
-{
- mMessageReader->getU8(block, var, u, blocknum);
-}
-
-void LLMessageSystem::getU8(const char *block, const char *var, U8 &u,
- S32 blocknum)
-{
- getU8Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), u, blocknum);
-}
-
-void LLMessageSystem::getBOOLFast(const char *block, const char *var, bool &b,
- S32 blocknum)
-{
- mMessageReader->getBOOL(block, var, b, blocknum);
-}
-
-void LLMessageSystem::getBOOL(const char *block, const char *var, bool &b,
- S32 blocknum)
-{
- getBOOLFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), b, blocknum);
-}
-
-void LLMessageSystem::getS16Fast(const char *block, const char *var, S16 &d,
- S32 blocknum)
-{
- mMessageReader->getS16(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getS16(const char *block, const char *var, S16 &d,
- S32 blocknum)
-{
- getS16Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-void LLMessageSystem::getU16Fast(const char *block, const char *var, U16 &d,
- S32 blocknum)
-{
- mMessageReader->getU16(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getU16(const char *block, const char *var, U16 &d,
- S32 blocknum)
-{
- getU16Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-void LLMessageSystem::getS32Fast(const char *block, const char *var, S32 &d,
- S32 blocknum)
-{
- mMessageReader->getS32(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getS32(const char *block, const char *var, S32 &d,
- S32 blocknum)
-{
- getS32Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-void LLMessageSystem::getU32Fast(const char *block, const char *var, U32 &d,
- S32 blocknum)
-{
- mMessageReader->getU32(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getU32(const char *block, const char *var, U32 &d,
- S32 blocknum)
-{
- getU32Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-void LLMessageSystem::getU64Fast(const char *block, const char *var, U64 &d,
- S32 blocknum)
-{
- mMessageReader->getU64(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getU64(const char *block, const char *var, U64 &d,
- S32 blocknum)
-{
-
- getU64Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-void LLMessageSystem::getBinaryDataFast(const char *blockname,
- const char *varname,
- void *datap, S32 size,
- S32 blocknum, S32 max_size)
-{
- mMessageReader->getBinaryData(blockname, varname, datap, size, blocknum,
- max_size);
-}
-
-void LLMessageSystem::getBinaryData(const char *blockname,
- const char *varname,
- void *datap, S32 size,
- S32 blocknum, S32 max_size)
-{
- getBinaryDataFast(LLMessageStringTable::getInstance()->getString(blockname),
- LLMessageStringTable::getInstance()->getString(varname),
- datap, size, blocknum, max_size);
-}
-
-void LLMessageSystem::getF32Fast(const char *block, const char *var, F32 &d,
- S32 blocknum)
-{
- mMessageReader->getF32(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getF32(const char *block, const char *var, F32 &d,
- S32 blocknum)
-{
- getF32Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-void LLMessageSystem::getF64Fast(const char *block, const char *var, F64 &d,
- S32 blocknum)
-{
- mMessageReader->getF64(block, var, d, blocknum);
-}
-
-void LLMessageSystem::getF64(const char *block, const char *var, F64 &d,
- S32 blocknum)
-{
- getF64Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), d, blocknum);
-}
-
-
-void LLMessageSystem::getVector3Fast(const char *block, const char *var,
- LLVector3 &v, S32 blocknum )
-{
- mMessageReader->getVector3(block, var, v, blocknum);
-}
-
-void LLMessageSystem::getVector3(const char *block, const char *var,
- LLVector3 &v, S32 blocknum )
-{
- getVector3Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), v, blocknum);
-}
-
-void LLMessageSystem::getVector4Fast(const char *block, const char *var,
- LLVector4 &v, S32 blocknum )
-{
- mMessageReader->getVector4(block, var, v, blocknum);
-}
-
-void LLMessageSystem::getVector4(const char *block, const char *var,
- LLVector4 &v, S32 blocknum )
-{
- getVector4Fast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), v, blocknum);
-}
-
-void LLMessageSystem::getVector3dFast(const char *block, const char *var,
- LLVector3d &v, S32 blocknum )
-{
- mMessageReader->getVector3d(block, var, v, blocknum);
-}
-
-void LLMessageSystem::getVector3d(const char *block, const char *var,
- LLVector3d &v, S32 blocknum )
-{
- getVector3dFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), v, blocknum);
-}
-
-void LLMessageSystem::getQuatFast(const char *block, const char *var,
- LLQuaternion &q, S32 blocknum )
-{
- mMessageReader->getQuat(block, var, q, blocknum);
-}
-
-void LLMessageSystem::getQuat(const char *block, const char *var,
- LLQuaternion &q, S32 blocknum)
-{
- getQuatFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), q, blocknum);
-}
-
-void LLMessageSystem::getUUIDFast(const char *block, const char *var,
- LLUUID &u, S32 blocknum )
-{
- mMessageReader->getUUID(block, var, u, blocknum);
-}
-
-void LLMessageSystem::getUUID(const char *block, const char *var, LLUUID &u,
- S32 blocknum )
-{
- getUUIDFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), u, blocknum);
-}
-
-void LLMessageSystem::getIPAddrFast(const char *block, const char *var,
- U32 &u, S32 blocknum)
-{
- mMessageReader->getIPAddr(block, var, u, blocknum);
-}
-
-void LLMessageSystem::getIPAddr(const char *block, const char *var, U32 &u,
- S32 blocknum)
-{
- getIPAddrFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), u, blocknum);
-}
-
-void LLMessageSystem::getIPPortFast(const char *block, const char *var,
- U16 &u, S32 blocknum)
-{
- mMessageReader->getIPPort(block, var, u, blocknum);
-}
-
-void LLMessageSystem::getIPPort(const char *block, const char *var, U16 &u,
- S32 blocknum)
-{
- getIPPortFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), u,
- blocknum);
-}
-
-
-void LLMessageSystem::getStringFast(const char *block, const char *var,
- S32 buffer_size, char *s, S32 blocknum)
-{
- if(buffer_size <= 0)
- {
- LL_WARNS("Messaging") << "buffer_size <= 0" << LL_ENDL;
- }
- mMessageReader->getString(block, var, buffer_size, s, blocknum);
-}
-
-void LLMessageSystem::getString(const char *block, const char *var,
- S32 buffer_size, char *s, S32 blocknum )
-{
- getStringFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), buffer_size, s,
- blocknum);
-}
-
-void LLMessageSystem::getStringFast(const char *block, const char *var,
- std::string& outstr, S32 blocknum)
-{
- mMessageReader->getString(block, var, outstr, blocknum);
-}
-
-void LLMessageSystem::getString(const char *block, const char *var,
- std::string& outstr, S32 blocknum )
-{
- getStringFast(LLMessageStringTable::getInstance()->getString(block),
- LLMessageStringTable::getInstance()->getString(var), outstr,
- blocknum);
-}
-
-bool LLMessageSystem::has(const char *blockname) const
-{
- return getNumberOfBlocks(blockname) > 0;
-}
-
-S32 LLMessageSystem::getNumberOfBlocksFast(const char *blockname) const
-{
- return mMessageReader->getNumberOfBlocks(blockname);
-}
-
-S32 LLMessageSystem::getNumberOfBlocks(const char *blockname) const
-{
- return getNumberOfBlocksFast(LLMessageStringTable::getInstance()->getString(blockname));
-}
-
-S32 LLMessageSystem::getSizeFast(const char *blockname, const char *varname) const
-{
- return mMessageReader->getSize(blockname, varname);
-}
-
-S32 LLMessageSystem::getSize(const char *blockname, const char *varname) const
-{
- return getSizeFast(LLMessageStringTable::getInstance()->getString(blockname),
- LLMessageStringTable::getInstance()->getString(varname));
-}
-
-// size in bytes of variable length data
-S32 LLMessageSystem::getSizeFast(const char *blockname, S32 blocknum,
- const char *varname) const
-{
- return mMessageReader->getSize(blockname, blocknum, varname);
-}
-
-S32 LLMessageSystem::getSize(const char *blockname, S32 blocknum,
- const char *varname) const
-{
- return getSizeFast(LLMessageStringTable::getInstance()->getString(blockname), blocknum,
- LLMessageStringTable::getInstance()->getString(varname));
-}
-
-S32 LLMessageSystem::getReceiveSize() const
-{
- return mMessageReader->getMessageSize();
-}
-
-//static
-void LLMessageSystem::setTimeDecodes( bool b )
-{
- LLMessageReader::setTimeDecodes(b);
-}
-
-//static
-void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds )
-{
- LLMessageReader::setTimeDecodesSpamThreshold(seconds);
-}
-
-LockMessageChecker::LockMessageChecker(LLMessageSystem* msgsystem):
- // for the lifespan of this LockMessageChecker instance, use
- // LLTemplateMessageReader as msgsystem's mMessageReader
- LockMessageReader(msgsystem->mMessageReader, msgsystem->mTemplateMessageReader),
- mMessageSystem(msgsystem)
-{}
-
-// HACK! babbage: return true if message rxed via either UDP or HTTP
-// TODO: babbage: move gServicePump in to LLMessageSystem?
-bool LLMessageSystem::checkAllMessages(LockMessageChecker& lmc, S64 frame_count, LLPumpIO* http_pump)
-{
- if(lmc.checkMessages(frame_count))
- {
- return true;
- }
- U32 packetsIn = mPacketsIn;
- http_pump->pump();
- http_pump->callback();
- return (mPacketsIn - packetsIn) > 0;
-}
-
-void LLMessageSystem::banUdpMessage(const std::string& name)
-{
- message_template_name_map_t::iterator itt = mMessageTemplates.find(
- LLMessageStringTable::getInstance()->getString(name.c_str())
- );
- if(itt != mMessageTemplates.end())
- {
- itt->second->banUdp();
- }
- else
- {
- LL_WARNS() << "Attempted to ban an unknown message: " << name << "." << LL_ENDL;
- }
-}
-const LLHost& LLMessageSystem::getSender() const
-{
- return mLastSender;
-}
-
-void LLMessageSystem::sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback)
-{
- LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
- LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("untrustedSimulatorMessage", httpPolicy));
- LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
- LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
-
-
- if (url.empty())
- {
- LL_WARNS() << "sendUntrustedSimulatorMessageCoro called with empty capability!" << LL_ENDL;
- return;
- }
-
- LL_INFOS() << "sendUntrustedSimulatorMessageCoro: message " << message << " to cap " << url << LL_ENDL;
- LLSD postData;
- postData["message"] = message;
- postData["body"] = body;
-
- LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData, httpOpts);
-
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
- if ((callback) && (!callback.empty()))
- callback((status) ? LL_ERR_NOERR : LL_ERR_TCP_TIMEOUT);
-}
-
-
-LLHTTPRegistration<LLHTTPNodeAdapter<LLTrustedMessageService> >
- gHTTPRegistrationTrustedMessageWildcard("/trusted-message/<message-name>");
-
+/** + * @file message.cpp + * @brief LLMessageSystem class implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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 <iomanip> +#include <iterator> +#include <sstream> + +#include "llapr.h" +#include "apr_portable.h" +#include "apr_network_io.h" +#include "apr_poll.h" + +// linden library headers +#include "llapp.h" +#include "indra_constants.h" +#include "lldir.h" +#include "llerror.h" +#include "llfasttimer.h" +#include "llhttpnodeadapter.h" +#include "llmd5.h" +#include "llmessagebuilder.h" +#include "llmessageconfig.h" +#include "lltemplatemessagedispatcher.h" +#include "llpumpio.h" +#include "lltemplatemessagebuilder.h" +#include "lltemplatemessagereader.h" +#include "lltrustedmessageservice.h" +#include "llmessagetemplate.h" +#include "llmessagetemplateparser.h" +#include "llsd.h" +#include "llsdmessagebuilder.h" +#include "llsdmessagereader.h" +#include "llsdserialize.h" +#include "llstring.h" +#include "lltransfermanager.h" +#include "lluuid.h" +#include "llxfermanager.h" +#include "llquaternion.h" +#include "u64.h" +#include "v3dmath.h" +#include "v3math.h" +#include "v4math.h" +#include "lltransfertargetvfile.h" +#include "llcorehttputil.h" +#include "llpounceable.h" + +// Constants +//const char* MESSAGE_LOG_FILENAME = "message.log"; +static const F32Seconds CIRCUIT_DUMP_TIMEOUT(30.f); +static const S32 TRUST_TIME_WINDOW = 3; + +// *NOTE: 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; +}; + +class LLMessageHandlerBridge : public LLHTTPNode +{ + virtual bool validate(const std::string& name, LLSD& context) const + { return true; } + + virtual void post(LLHTTPNode::ResponsePtr response, const LLSD& context, + const LLSD& input) const; +}; + +//virtual +void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response, + const LLSD& context, const LLSD& input) const +{ + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["message-name"]; + char* namePtr = LLMessageStringTable::getInstance()->getString(name.c_str()); + + LL_DEBUGS() << "Setting mLastSender " << input["sender"].asString() << LL_ENDL; + gMessageSystem->mLastSender = LLHost(input["sender"].asString()); + gMessageSystem->mPacketsIn += 1; + gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]); + LockMessageReader rdr(gMessageSystem->mMessageReader, gMessageSystem->mLLSDMessageReader); + + if(gMessageSystem->callHandler(namePtr, false, gMessageSystem)) + { + response->result(LLSD()); + } + else + { + response->notFound(); + } +} + +LLHTTPRegistration<LLMessageHandlerBridge> + gHTTPRegistrationMessageWildcard("/message/<message-name>"); + +//virtual +LLUseCircuitCodeResponder::~LLUseCircuitCodeResponder() +{ + // even abstract base classes need a concrete destructor +} + +static const char* nullToEmpty(const char* s) +{ + static char emptyString[] = ""; + return s? s : emptyString; +} + +void LLMessageSystem::init() +{ + // initialize member variables + mVerboseLog = false; + + mbError = false; + mErrorCode = 0; + mSendReliable = false; + + mUnackedListDepth = 0; + mUnackedListSize = 0; + mDSMaxListDepth = 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; + + mIncomingCompressedSize = 0; + mCurrentRecvPacketID = 0; + + mMessageFileVersionNumber = 0.f; + + mTimingCallback = NULL; + mTimingCallbackData = NULL; + + mMessageBuilder = NULL; + LockMessageReader(mMessageReader, NULL); +} + +// Read file and build message templates +LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port, + S32 version_major, + S32 version_minor, + S32 version_patch, + bool failure_is_fatal, + const F32 circuit_heartbeat_interval, const F32 circuit_timeout) : + mCircuitInfo(F32Seconds(circuit_heartbeat_interval), F32Seconds(circuit_timeout)), + mLastMessageFromTrustedMessageService(false) +{ + init(); + + mSendSize = 0; + + mSystemVersionMajor = version_major; + mSystemVersionMinor = version_minor; + mSystemVersionPatch = version_patch; + mSystemVersionServer = 0; + mVersionFlags = 0x0; + + // default to not accepting packets from not alive circuits + mbProtected = true; + + // default to blocking trusted connections on a public interface if one is specified + mBlockUntrustedInterface = true; + + mSendPacketFailureCount = 0; + + mCircuitPrintFreq = F32Seconds(60.f); + + loadTemplateFile(filename, failure_is_fatal); + + mTemplateMessageBuilder = new LLTemplateMessageBuilder(mMessageTemplates); + mLLSDMessageBuilder = new LLSDMessageBuilder(); + mMessageBuilder = NULL; + + mTemplateMessageReader = new LLTemplateMessageReader(mMessageNumbers); + mLLSDMessageReader = new LLSDMessageReader(); + + // initialize various bits of net info + mSocket = 0; + mPort = port; + + S32 error = start_net(mSocket, mPort); + if (error != 0) + { + mbError = true; + mErrorCode = error; + } +// LL_DEBUGS("Messaging") << << "*** port: " << mPort << LL_ENDL; + + // + // Create the data structure that we can poll on + // + if (!gAPRPoolp) + { + LL_ERRS("Messaging") << "No APR pool before message system initialization!" << LL_ENDL; + 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; + + F64Seconds mt_sec = getMessageTimeSeconds(); + mResendDumpTime = mt_sec; + mMessageCountTime = mt_sec; + mCircuitPrintTime = mt_sec; + mCurrentMessageTime = F64Seconds(mt_sec); + + // Constants for dumping output based on message processing time/count + mNumMessageCounts = 0; + mMaxMessageCounts = 200; // >= 0 means dump warnings + mMaxMessageTime = F32Seconds(1.f); + + mTrueReceiveSize = 0; + + mReceiveTime = F32Seconds(0.f); +} + + + +// Read file and build message templates +void LLMessageSystem::loadTemplateFile(const std::string& filename, bool failure_is_fatal) +{ + if(filename.empty()) + { + LL_ERRS("Messaging") << "No template filename specified" << LL_ENDL; + mbError = true; + return; + } + + std::string template_body; + if(!_read_file_into_string(template_body, filename)) + { + if (failure_is_fatal) { + LL_ERRS("Messaging") << "Failed to open template: " << filename << LL_ENDL; + } else { + LL_WARNS("Messaging") << "Failed to open template: " << filename << LL_ENDL; + } + mbError = true; + return; + } + + LLTemplateTokenizer tokens(template_body); + LLTemplateParser parsed(tokens); + mMessageFileVersionNumber = parsed.getVersion(); + S32 count = 0; + for(LLTemplateParser::message_iterator iter = parsed.getMessagesBegin(); + iter != parsed.getMessagesEnd(); + iter++) + { + addTemplate(*iter); + count++; + } + LL_INFOS("Messaging") << "Read " << count << " messages from " << filename << LL_ENDL; +} + + +LLMessageSystem::~LLMessageSystem() +{ + mMessageTemplates.clear(); // don't delete templates. + for_each(mMessageNumbers.begin(), mMessageNumbers.end(), DeletePairedPointer()); + mMessageNumbers.clear(); + + if (!mbError) + { + end_net(mSocket); + } + mSocket = 0; + + delete mTemplateMessageReader; + mTemplateMessageReader = NULL; + + delete mTemplateMessageBuilder; + mTemplateMessageBuilder = NULL; + mMessageBuilder = NULL; + + delete mLLSDMessageReader; + mLLSDMessageReader = NULL; + + delete mLLSDMessageBuilder; + mLLSDMessageBuilder = NULL; + + delete mPollInfop; + mPollInfop = NULL; + + mIncomingCompressedSize = 0; + mCurrentRecvPacketID = 0; +} + +void LLMessageSystem::clearReceiveState() +{ + mCurrentRecvPacketID = 0; + mIncomingCompressedSize = 0; + mLastSender.invalidate(); + mLastReceivingIF.invalidate(); + mMessageReader->clearMessage(); + mLastMessageFromTrustedMessageService = false; +} + + +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; + } +} + +bool LLMessageSystem::isTrustedSender(const LLHost& host) const +{ + LLCircuitData* cdp = mCircuitInfo.findCircuit(host); + if(NULL == cdp) + { + return false; + } + return cdp->getTrusted(); +} + +void LLMessageSystem::receivedMessageFromTrustedSender() +{ + mLastMessageFromTrustedMessageService = true; +} + +bool LLMessageSystem::isTrustedSender() const +{ + return mLastMessageFromTrustedMessageService || + isTrustedSender(getSender()); +} + +static LLMessageSystem::message_template_name_map_t::const_iterator +findTemplate(const LLMessageSystem::message_template_name_map_t& templates, + std::string name) +{ + const char* namePrehash = LLMessageStringTable::getInstance()->getString(name.c_str()); + if(NULL == namePrehash) {return templates.end();} + return templates.find(namePrehash); +} + +bool LLMessageSystem::isTrustedMessage(const std::string& name) const +{ + message_template_name_map_t::const_iterator iter = + findTemplate(mMessageTemplates, name); + if(iter == mMessageTemplates.end()) {return false;} + return iter->second->getTrust() == MT_TRUST; +} + +bool LLMessageSystem::isUntrustedMessage(const std::string& name) const +{ + message_template_name_map_t::const_iterator iter = + findTemplate(mMessageTemplates, name); + if(iter == mMessageTemplates.end()) {return false;} + return iter->second->getTrust() == MT_NOTRUST; +} + +LLCircuitData* LLMessageSystem::findCircuit(const LLHost& host, + bool resetPacketId) +{ + LLCircuitData* 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); + + if(resetPacketId) + { + // 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); + + if(resetPacketId) + { + // reset packet in ID + cdp->setPacketInID(mCurrentRecvPacketID); + } + } + } + } + return cdp; +} + +// Returns true if a valid, on-circuit message has been received. +// Requiring a non-const LockMessageChecker reference ensures that +// mMessageReader has been set to mTemplateMessageReader. +bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) +{ + // Pump + 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. + mCurrentMessageTime = totalTime(); + mMessageCountTime = getMessageTimeSeconds(); + } + + // loop until either no packets or a valid packet + // i.e., burn through packets from unregistered circuits + S32 receive_size = 0; + 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(); + + receive_size = mTrueReceiveSize; + mLastSender = mPacketRing.getLastSender(); + mLastReceivingIF = mPacketRing.getLastReceivingInterface(); + + if (receive_size < (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 (receive_size > 0) + { + LL_WARNS("Messaging") << "Invalid (too short) packet discarded " << receive_size << LL_ENDL; + 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[--receive_size]; + true_rcv_size = receive_size; + if(receive_size >= ((S32)(acks * sizeof(TPACKETID) + LL_MINIMUM_VALID_PACKET_SIZE))) + { + receive_size -= acks * sizeof(TPACKETID); + } + else + { + // mal-formed packet. ignore it and continue with + // the next one + LL_WARNS("Messaging") << "Malformed packet received. Packet size " + << receive_size << " with invalid no. of acks " << acks + << LL_ENDL; + valid_packet = false; + continue; + } + } + + // process the message as normal + mIncomingCompressedSize = zeroCodeExpand(&buffer, &receive_size); + mCurrentRecvPacketID = ntohl(*((U32*)(&buffer[1]))); + host = getSender(); + + const bool resetPacketId = true; + cdp = findCircuit(host, resetPacketId); + + // 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); + //LL_INFOS("Messaging") << "got ack: " << packet_id << LL_ENDL; + 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) + //{ + // LL_WARNS("Messaging") << "DISCARDED PACKET HOST MISMATCH! HOST: " + // << host << " CIRCUIT: " + // << mCircuitInfo.mCurrentCircuit->mHost + // << LL_ENDL; + //} + // *************************************** + //mCircuitInfo.mCurrentCircuit->mAcks.put(mCurrentRecvPacketID); + cdp->collectRAck(mCurrentRecvPacketID); + } + + LL_DEBUGS("Messaging") << "Discarding duplicate resend from " << host << LL_ENDL; + if(mVerboseLog) + { + std::ostringstream str; + str << "MSG: <- " << host; + std::string tbuf; + tbuf = llformat( "\t%6d\t%6d\t%6d ", receive_size, (mIncomingCompressedSize ? mIncomingCompressedSize : receive_size), mCurrentRecvPacketID); + str << tbuf << "(unknown)" + << (recv_reliable ? " reliable" : "") + << " resent " + << ((acks > 0) ? "acks" : "") + << " DISCARD DUPLICATE"; + LL_INFOS("Messaging") << str.str() << LL_ENDL; + } + 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 + bool trusted = cdp && cdp->getTrusted(); + valid_packet = mTemplateMessageReader->validateMessage( + buffer, + receive_size, + host, + trusted); + if (!valid_packet) + { + clearReceiveState(); + } + + // UseCircuitCode is allowed in even from an invalid circuit, so that + // we can toss circuits around. + if( + valid_packet && + !cdp && + (mTemplateMessageReader->getMessageName() != + _PREHASH_UseCircuitCode)) + { + logMsgFromInvalidCircuit( host, recv_reliable ); + clearReceiveState(); + valid_packet = false; + } + + if( + valid_packet && + cdp && + !cdp->getTrusted() && + mTemplateMessageReader->isTrusted()) + { + logTrustedMsgFromUntrustedCircuit( host ); + clearReceiveState(); + + sendDenyTrustedCircuit(host); + valid_packet = false; + } + + if( valid_packet ) + { + logValidMsg(cdp, host, recv_reliable, recv_resent, acks>0 ); + valid_packet = mTemplateMessageReader->readMessage(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) + { + 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)) + { + LL_WARNS("Messaging") << "Invalid Packet from invalid circuit " << host << LL_ENDL; + mOffCircuitPackets++; + } + else + { + mInvalidOnCircuitPackets++; + } + } + } + } while (!valid_packet && receive_size > 0); + + F64Seconds 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(LockMessageChecker&, F32 collect_time) +{ + F64Seconds 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(collect_time); + + if (!mDenyTrustedCircuitSet.empty()) + { + LL_INFOS("Messaging") << "Sending queued DenyTrustedCircuit messages." << LL_ENDL; + 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 >= F32Seconds(0.f)) + { + // This is one of the only places where we're required to get REAL message system time. + mReceiveTime = 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::copyMessageReceivedToSend() +{ + // NOTE: babbage: switch builder to match reader to avoid + // converting message format + if(mMessageReader == mTemplateMessageReader) + { + mMessageBuilder = mTemplateMessageBuilder; + } + else + { + mMessageBuilder = mLLSDMessageBuilder; + } + mSendReliable = false; + mMessageBuilder->newMessage(mMessageReader->getMessageName()); + mMessageReader->copyToBuilder(*mMessageBuilder); +} + +LLSD LLMessageSystem::getReceivedMessageLLSD() const +{ + LLSDMessageBuilder builder; + mMessageReader->copyToBuilder(builder); + return builder.getMessage(); +} + +LLSD LLMessageSystem::getBuiltMessageLLSD() const +{ + LLSD result; + if (mLLSDMessageBuilder == mMessageBuilder) + { + result = mLLSDMessageBuilder->getMessage(); + } + else + { + // TODO: implement as below? + LL_ERRS() << "Message not built as LLSD." << LL_ENDL; + } + return result; +} + +LLSD LLMessageSystem::wrapReceivedTemplateData() const +{ + if(mMessageReader == mTemplateMessageReader) + { + LLTemplateMessageBuilder builder(mMessageTemplates); + builder.newMessage(mMessageReader->getMessageName()); + mMessageReader->copyToBuilder(builder); + U8 buffer[MAX_BUFFER_SIZE]; + const U8 offset_to_data = 0; + U32 size = builder.buildMessage(buffer, MAX_BUFFER_SIZE, + offset_to_data); + std::vector<U8> binary_data(buffer, buffer+size); + LLSD wrapped_data = LLSD::emptyMap(); + wrapped_data["binary-template-data"] = binary_data; + return wrapped_data; + } + else + { + return getReceivedMessageLLSD(); + } +} + +LLSD LLMessageSystem::wrapBuiltTemplateData() const +{ + LLSD result; + if (mLLSDMessageBuilder == mMessageBuilder) + { + result = getBuiltMessageLLSD(); + } + else + { + U8 buffer[MAX_BUFFER_SIZE]; + const U8 offset_to_data = 0; + U32 size = mTemplateMessageBuilder->buildMessage( + buffer, MAX_BUFFER_SIZE, + offset_to_data); + std::vector<U8> binary_data(buffer, buffer+size); + LLSD wrapped_data = LLSD::emptyMap(); + wrapped_data["binary-template-data"] = binary_data; + result = wrapped_data; + } + return result; +} + +LLStoredMessagePtr LLMessageSystem::getReceivedMessage() const +{ + const std::string& name = mMessageReader->getMessageName(); + LLSD message = wrapReceivedTemplateData(); + + return LLStoredMessagePtr(new LLStoredMessage(name, message)); +} + +LLStoredMessagePtr LLMessageSystem::getBuiltMessage() const +{ + const std::string& name = mMessageBuilder->getMessageName(); + LLSD message = wrapBuiltTemplateData(); + + return LLStoredMessagePtr(new LLStoredMessage(name, message)); +} + +S32 LLMessageSystem::sendMessage(const LLHost &host, LLStoredMessagePtr message) +{ + return sendMessage(host, message->mName.c_str(), message->mMessage); +} + + +void LLMessageSystem::clearMessage() +{ + mSendReliable = false; + mMessageBuilder->clearMessage(); +} + +// set block to add data to within current message +void LLMessageSystem::nextBlockFast(const char *blockname) +{ + mMessageBuilder->nextBlock(blockname); +} + +void LLMessageSystem::nextBlock(const char *blockname) +{ + nextBlockFast(LLMessageStringTable::getInstance()->getString(blockname)); +} + +bool LLMessageSystem::isSendFull(const char* blockname) +{ + char* stringTableName = NULL; + if(NULL != blockname) + { + stringTableName = LLMessageStringTable::getInstance()->getString(blockname); + } + return isSendFullFast(stringTableName); +} + +bool LLMessageSystem::isSendFullFast(const char* blockname) +{ + return mMessageBuilder->isMessageFull(blockname); +} + + +// blow away the last block of a message, return false if that leaves no blocks or there wasn't a block to remove +// TODO: Babbage: Remove this horror. +bool LLMessageSystem::removeLastBlock() +{ + return mMessageBuilder->removeLastBlock(); +} + +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) +{ + F32Seconds timeout; + + LLCircuitData *cdp = mCircuitInfo.findCircuit(host); + if (cdp) + { + timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS, + F32Seconds(LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged())); + } + else + { + timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX; + } + + constexpr S32 retries = 0; + constexpr bool ping_based_timeout = false; + return sendReliable(host, retries, ping_based_timeout, timeout, callback, callback_data); +} + +// send the message via a UDP packet +S32 LLMessageSystem::sendReliable( const LLHost &host, + S32 retries, + bool ping_based_timeout, + F32Seconds 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, F32Seconds(LL_RELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged())); + } + else + { + timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, F32Seconds(LL_RELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX)); + } + } + + mSendReliable = true; + mReliablePacketParams.set(host, retries, ping_based_timeout, timeout, + callback, callback_data, + const_cast<char*>(mMessageBuilder->getMessageName())); + return sendMessage(host); +} + +void LLMessageSystem::forwardMessage(const LLHost &host) +{ + copyMessageReceivedToSend(); + sendMessage(host); +} + +void LLMessageSystem::forwardReliable(const LLHost &host) +{ + copyMessageReceivedToSend(); + sendReliable(host); +} + +void LLMessageSystem::forwardReliable(const U32 circuit_code) +{ + copyMessageReceivedToSend(); + sendReliable(findHost(circuit_code)); +} + +S32 LLMessageSystem::forwardReliable( const LLHost &host, + S32 retries, + bool ping_based_timeout, + F32Seconds timeout, + void (*callback)(void **,S32), + void ** callback_data) +{ + copyMessageReceivedToSend(); + return sendReliable(host, retries, ping_based_timeout, timeout, callback, callback_data); +} + +S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data) +{ + F32Seconds timeout; + + LLCircuitData *cdp = mCircuitInfo.findCircuit(host); + if (cdp) + { + timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS, + F32Seconds(LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged())); + } + else + { + timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX; + } + + S32 send_bytes = 0; + if (mMessageBuilder->getMessageSize()) + { + mSendReliable = true; + // No need for ping-based retry as not going to retry + mReliablePacketParams.set(host, 0, false, timeout, callback, + callback_data, + const_cast<char*>(mMessageBuilder->getMessageName())); + send_bytes = sendMessage(host); + clearMessage(); + } + else + { + delete callback_data; + } + return send_bytes; +} + +S32 LLMessageSystem::flushReliable(const LLHost &host) +{ + S32 send_bytes = 0; + if (mMessageBuilder->getMessageSize()) + { + send_bytes = sendReliable(host); + } + clearMessage(); + return send_bytes; +} + +// This can be called from signal handlers, +// so should should not use LL_INFOS(). +S32 LLMessageSystem::sendMessage(const LLHost &host) +{ + if (! mMessageBuilder->isBuilt()) + { + mSendSize = mMessageBuilder->buildMessage( + mSendBuffer, + MAX_BUFFER_SIZE, + 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) + { + LL_INFOS_ONCE("Messaging") << "MSG: -> " << host << "\tUNKNOWN CIRCUIT:\t" + << mMessageBuilder->getMessageName() << LL_ENDL; + } + LL_WARNS_ONCE("Messaging") << "sendMessage - Trying to send " + << mMessageBuilder->getMessageName() << " on unknown circuit " + << host << LL_ENDL; + 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) + { + LL_INFOS("Messaging") << "MSG: -> " << host << "\tDEAD CIRCUIT\t\t" + << mMessageBuilder->getMessageName() << LL_ENDL; + } + LL_WARNS("Messaging") << "sendMessage - Trying to send message " + << mMessageBuilder->getMessageName() << " to dead circuit " + << host << LL_ENDL; + return 0; + } + } + + // NOTE: babbage: LLSD message -> HTTP, template message -> UDP + if(mMessageBuilder == mLLSDMessageBuilder) + { + LLSD message = mLLSDMessageBuilder->getMessage(); + + UntrustedCallback_t cb = NULL; + if ((mSendReliable) && (mReliablePacketParams.mCallback)) + { + cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1); + } + + LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro", + boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this, + host.getUntrustedSimulatorCap(), + mLLSDMessageBuilder->getMessageName(), message, cb)); + + mSendReliable = false; + mReliablePacketParams.clear(); + return 1; + } + + // zero out the flags and packetid. Subtract 1 here so that we do + // not overwrite the offset if it was set set in buildMessage(). + memset(mSendBuffer, 0, LL_PACKET_ID_SIZE - 1); + + // add the send id to the front of the message + cdp->nextPacketOutID(); + + // Packet ID size is always 4 + *((S32*)&mSendBuffer[PHL_PACKET_ID]) = htonl(cdp->getPacketOutID()); + + // Compress the message, which will usually reduce its size. + U8 * buf_ptr = (U8 *)mSendBuffer; + U32 buffer_length = mSendSize; + mMessageBuilder->compressMessage(buf_ptr, buffer_length); + + if (buffer_length > 1500) + { + if((mMessageBuilder->getMessageName() != _PREHASH_ChildAgentUpdate) + && (mMessageBuilder->getMessageName() != _PREHASH_SendXferPacket)) + { + LL_WARNS("Messaging") << "sendMessage - Trying to send " + << ((buffer_length > 4000) ? "EXTRA " : "") + << "BIG message " << mMessageBuilder->getMessageName() << " - " + << buffer_length << LL_ENDL; + } + } + 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) && + (mMessageBuilder->getMessageName() != _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)); /* Flawfinder: ignore */ + // 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. + LL_ERRS("Messaging") << "Buffer packing failed due to size.." << LL_ENDL; + } + } + + // 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( (S32Bytes)buffer_length ); + } + + if(mVerboseLog) + { + std::ostringstream str; + str << "MSG: -> " << host; + std::string buffer; + buffer = llformat( "\t%6d\t%6d\t%6d ", mSendSize, buffer_length, cdp->getPacketOutID()); + str << buffer + << mMessageBuilder->getMessageName() + << (mSendReliable ? " reliable " : ""); + if(is_ack_appended) + { + str << "\tACKS:\t"; + std::ostream_iterator<TPACKETID> append(str, " "); + std::copy(acks.begin(), acks.end(), append); + } + LL_INFOS("Messaging") << str.str() << LL_ENDL; + } + + + mPacketsOut++; + mTotalBytesOut += buffer_length; + + mSendReliable = false; + mReliablePacketParams.clear(); + return buffer_length; +} + +void LLMessageSystem::logMsgFromInvalidCircuit( const LLHost& host, bool recv_reliable ) +{ + if(mVerboseLog) + { + std::ostringstream str; + str << "MSG: <- " << host; + std::string buffer; + buffer = llformat( "\t%6d\t%6d\t%6d ", mMessageReader->getMessageSize(), (mIncomingCompressedSize ? mIncomingCompressedSize: mMessageReader->getMessageSize()), mCurrentRecvPacketID); + str << buffer + << nullToEmpty(mMessageReader->getMessageName()) + << (recv_reliable ? " reliable" : "") + << " REJECTED"; + LL_INFOS("Messaging") << str.str() << LL_ENDL; + } + // 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) + { + LL_WARNS("Messaging") << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << LL_ENDL; + } + else + { + // TODO: babbage: work out if we need these + // mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber; + mMessageCountList[mNumMessageCounts].mMessageBytes = mMessageReader->getMessageSize(); + mMessageCountList[mNumMessageCounts].mInvalid = true; + mNumMessageCounts++; + } +} + +S32 LLMessageSystem::sendMessage( + const LLHost &host, + const char* name, + const LLSD& message) +{ + if (!(host.isOk())) + { + LL_WARNS("Messaging") << "trying to send message to invalid host" << LL_ENDL; + return 0; + } + + UntrustedCallback_t cb = NULL; + if ((mSendReliable) && (mReliablePacketParams.mCallback)) + { + cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1); + } + + LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro", + boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this, + host.getUntrustedSimulatorCap(), name, message, cb)); + return 1; +} + +void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host ) +{ + // RequestTrustedCircuit is how we establish trust, so don't spam + // if it's received on a trusted circuit. JC + if (strcmp(mMessageReader->getMessageName(), "RequestTrustedCircuit")) + { + LL_WARNS("Messaging") << "Received trusted message on untrusted circuit. " + << "Will reply with deny. " + << "Message: " << nullToEmpty(mMessageReader->getMessageName()) + << " Host: " << host << LL_ENDL; + } + + if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM) + { + LL_WARNS("Messaging") << "got more than " << MAX_MESSAGE_COUNT_NUM + << " packets without clearing counts" + << LL_ENDL; + } + else + { + // TODO: babbage: work out if we need these + //mMessageCountList[mNumMessageCounts].mMessageNum + // = mCurrentRMessageTemplate->mMessageNumber; + mMessageCountList[mNumMessageCounts].mMessageBytes + = mMessageReader->getMessageSize(); + 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) + { + LL_WARNS("Messaging") << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << LL_ENDL; + } + else + { + // TODO: babbage: work out if we need these + //mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber; + mMessageCountList[mNumMessageCounts].mMessageBytes = mMessageReader->getMessageSize(); + mMessageCountList[mNumMessageCounts].mInvalid = false; + mNumMessageCounts++; + } + + if (cdp) + { + // update circuit packet ID tracking (missing/out of order packets) + cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent ); + cdp->addBytesIn( (S32Bytes)mTrueReceiveSize ); + } + + if(mVerboseLog) + { + std::ostringstream str; + str << "MSG: <- " << host; + std::string buffer; + buffer = llformat( "\t%6d\t%6d\t%6d ", mMessageReader->getMessageSize(), (mIncomingCompressedSize ? mIncomingCompressedSize : mMessageReader->getMessageSize()), mCurrentRecvPacketID); + str << buffer + << nullToEmpty(mMessageReader->getMessageName()) + << (recv_reliable ? " reliable" : "") + << (recv_resent ? " resent" : "") + << (recv_acks ? " acks" : ""); + LL_INFOS("Messaging") << str.str() << LL_ENDL; + } +} + +void LLMessageSystem::sanityCheck() +{ +// TODO: babbage: reinstate + +// if (!mCurrentRMessageData) +// { +// LL_ERRS("Messaging") << "mCurrentRMessageData is NULL" << LL_ENDL; +// } + +// if (!mCurrentRMessageTemplate) +// { +// LL_ERRS("Messaging") << "mCurrentRMessageTemplate is NULL" << LL_ENDL; +// } + +// if (!mCurrentRTemplateBlock) +// { +// LL_ERRS("Messaging") << "mCurrentRTemplateBlock is NULL" << LL_ENDL; +// } + +// if (!mCurrentRDataBlock) +// { +// LL_ERRS("Messaging") << "mCurrentRDataBlock is NULL" << LL_ENDL; +// } + +// if (!mCurrentSMessageData) +// { +// LL_ERRS("Messaging") << "mCurrentSMessageData is NULL" << LL_ENDL; +// } + +// if (!mCurrentSMessageTemplate) +// { +// LL_ERRS("Messaging") << "mCurrentSMessageTemplate is NULL" << LL_ENDL; +// } + +// if (!mCurrentSTemplateBlock) +// { +// LL_ERRS("Messaging") << "mCurrentSTemplateBlock is NULL" << LL_ENDL; +// } + +// if (!mCurrentSDataBlock) +// { +// LL_ERRS("Messaging") << "mCurrentSDataBlock is NULL" << LL_ENDL; +// } +} + +void LLMessageSystem::showCircuitInfo() +{ + LL_INFOS("Messaging") << mCircuitInfo << LL_ENDL; +} + + +void LLMessageSystem::dumpCircuitInfo() +{ + LL_DEBUGS("Messaging") << mCircuitInfo << LL_ENDL; +} + +/* virtual */ +U32 LLMessageSystem::getOurCircuitCode() +{ + return mOurCircuitCode; +} + +void LLMessageSystem::getCircuitInfo(LLSD& info) const +{ + mCircuitInfo.getInfo(info); +} + +// 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) +{ + LL_INFOS("Messaging") << "LLMessageSystem::disableCircuit for " << host << LL_ENDL; + 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()) + { + LL_INFOS("Messaging") << "Circuit " << code << " removed from list" << LL_ENDL; + //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); + + LL_INFOS("Messaging") << "Host " << LLHost(old_ip, old_port) << " circuit " << code << " removed from lookup table" << LL_ENDL; + gMessageSystem->mIPPortToCircuitCode.erase(ip_port); + } + mCircuitInfo.removeCircuitData(host); + } + else + { + // Sigh, since we can open circuits which don't have circuit + // codes, it's possible for this to happen... + + LL_WARNS("Messaging") << "Couldn't find circuit code for " << host << LL_ENDL; + } + +} + + +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()) + { + LL_DEBUGS("Messaging") << "checkCircuitBlocked: Unknown circuit " << circuit << LL_ENDL; + return true; + } + + LLCircuitData *cdp = mCircuitInfo.findCircuit(host); + if (cdp) + { + return cdp->isBlocked(); + } + else + { + LL_INFOS("Messaging") << "checkCircuitBlocked(circuit): Unknown host - " << host << LL_ENDL; + return false; + } +} + +bool LLMessageSystem::checkCircuitAlive(const U32 circuit) +{ + LLHost host = findHost(circuit); + + if (!host.isOk()) + { + LL_DEBUGS("Messaging") << "checkCircuitAlive: Unknown circuit " << circuit << LL_ENDL; + return false; + } + + LLCircuitData *cdp = mCircuitInfo.findCircuit(host); + if (cdp) + { + return cdp->isAlive(); + } + else + { + LL_INFOS("Messaging") << "checkCircuitAlive(circuit): Unknown host - " << host << LL_ENDL; + return false; + } +} + +bool LLMessageSystem::checkCircuitAlive(const LLHost &host) +{ + LLCircuitData *cdp = mCircuitInfo.findCircuit(host); + if (cdp) + { + return cdp->isAlive(); + } + else + { + LL_DEBUGS("Messaging") << "checkCircuitAlive(host): Unknown host - " << host << LL_ENDL; + 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(); + } +} + +void LLMessageSystem::setMaxMessageTime(const F32 seconds) +{ + mMaxMessageTime = F32Seconds(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; +} + +// LLPounceable supports callWhenReady(), to permit clients to queue up (e.g.) +// callback registrations for when gMessageSystem is first assigned +LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem; + +// 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()) + { + LL_WARNS("Messaging") << "AssignCircuitCode, bad session id. Expecting " + << msg->getMySessionID() << " but got " << session_id + << LL_ENDL; + return; + } + U32 code; + msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code); + if (!code) + { + LL_ERRS("Messaging") << "Assigning circuit code of zero!" << LL_ENDL; + } + + msg->mOurCircuitCode = code; + LL_INFOS("Messaging") << "Circuit code " << code << " assigned." << LL_ENDL; +} +*/ + +// 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) + { + LL_WARNS("Messaging") << "addCircuitCode: zero circuit code" << LL_ENDL; + return false; + } + code_session_map_t::iterator it = mCircuitCodes.find(code); + if(it == mCircuitCodes.end()) + { + LL_INFOS("Messaging") << "New circuit code " << code << " added" << LL_ENDL; + //msg->mCircuitCodes[circuit_code] = circuit_code; + + mCircuitCodes.insert(code_session_map_t::value_type(code, session_id)); + } + else + { + LL_INFOS("Messaging") << "Duplicate circuit code " << code << " added" << LL_ENDL; + } + 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** user) +{ + 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. + LL_WARNS("Messaging") << "UseCircuitCode for " << circuit_code_in + << " received without AddCircuitCode message - aborting" + << LL_ENDL; + 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) + { + LL_WARNS("Messaging") << "UseCircuitCode unmatched session id. Got " + << session_id << " but expected " << (*it).second + << LL_ENDL; + 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 + LL_INFOS("Messaging") << "Got duplicate UseCircuitCode for circuit " << circuit_code_in << " to " << msg->getSender() << LL_ENDL; + 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); + LL_INFOS("Messaging") << "Removing derelict lookup entry for circuit " << circuit_code_old << " to " << LLHost(old_ip, old_port) << LL_ENDL; + } + + if (circuit_code_old) + { + LLHost cur_host(ip, port); + + LL_WARNS("Messaging") << "Disabling existing circuit for " << cur_host << LL_ENDL; + msg->disableCircuit(cur_host); + if (circuit_code_old == circuit_code_in) + { + LL_WARNS("Messaging") << "Asymmetrical circuit to ip/port lookup!" << LL_ENDL; + LL_WARNS("Messaging") << "Multiple circuit codes for " << cur_host << " probably!" << LL_ENDL; + LL_WARNS("Messaging") << "Permanently disabling circuit" << LL_ENDL; + return; + } + else + { + LL_WARNS("Messaging") << "Circuit code changed for " << msg->getSender() + << " from " << circuit_code_old << " to " + << circuit_code_in << LL_ENDL; + } + } + + // 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 != nullptr; + + 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; + + LL_INFOS("Messaging") << "Circuit code " << circuit_code_in << " from " + << msg->getSender() << " for agent " << id << " in session " + << session_id << LL_ENDL; + + const LLUseCircuitCodeResponder* responder = + (const LLUseCircuitCodeResponder*) user; + if(responder) + { + responder->complete(msg->getSender(), id); + } + } + else + { + LL_WARNS("Messaging") << "Got zero circuit code in use_circuit_code" << LL_ENDL; + } +} + +// static +void LLMessageSystem::processError(LLMessageSystem* msg, void**) +{ + S32 error_code = 0; + msg->getS32("Data", "Code", error_code); + std::string error_token; + msg->getString("Data", "Token", error_token); + + LLUUID error_id; + msg->getUUID("Data", "ID", error_id); + std::string error_system; + msg->getString("Data", "System", error_system); + + std::string error_message; + msg->getString("Data", "Message", error_message); + + LL_WARNS("Messaging") << "Message error from " << msg->getSender() << " - " + << error_code << " " << error_token << " " << error_id << " \"" + << error_system << "\" \"" << error_message << "\"" << LL_ENDL; +} + + +static LLHTTPNode& messageRootNode() +{ + static LLHTTPNode root_node; + static bool initialized = false; + if (!initialized) { + initialized = true; + LLHTTPRegistrar::buildAllServices(root_node); + } + + return root_node; +} + +//static +void LLMessageSystem::dispatch( + const std::string& msg_name, + const LLSD& message) +{ + LLPointer<LLSimpleResponse> responsep = LLSimpleResponse::create(); + dispatch(msg_name, message, responsep); +} + +//static +void LLMessageSystem::dispatch( + const std::string& msg_name, + const LLSD& message, + LLHTTPNode::ResponsePtr responsep) +{ + if ((gMessageSystem->mMessageTemplates.find + (LLMessageStringTable::getInstance()->getString(msg_name.c_str())) == + gMessageSystem->mMessageTemplates.end()) && + !LLMessageConfig::isValidMessage(msg_name)) + { + LL_WARNS("Messaging") << "Ignoring unknown message " << msg_name << LL_ENDL; + responsep->notFound("Invalid message name"); + return; + } + + std::string path = "/message/" + msg_name; + LLSD context; + const LLHTTPNode* handler = messageRootNode().traverse(path, context); + if (!handler) + { + LL_WARNS("Messaging") << "LLMessageService::dispatch > no handler for " + << path << LL_ENDL; + return; + } + // enable this for output of message names + LL_DEBUGS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; + LL_DEBUGS("Messaging") << "context: " << context << LL_ENDL; + LL_DEBUGS("Messaging") << "message: " << message << LL_ENDL; + + handler->post(responsep, context, message); +} + +//static +void LLMessageSystem::dispatchTemplate(const std::string& msg_name, + const LLSD& message, + LLHTTPNode::ResponsePtr responsep) +{ + LLTemplateMessageDispatcher dispatcher(*(gMessageSystem->mTemplateMessageReader)); + dispatcher.dispatch(msg_name, message, responsep); +} + +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 = LLMessageStringTable::getInstance()->getString(iter->first.c_str()); + + if (templates.find(name) == templates.end()) + { + LL_INFOS("AppInit") << " " << type + << " ban list contains unrecognized message " + << name << LL_ENDL; + } + } +} + +void LLMessageSystem::setMessageBans( + const LLSD& trusted, const LLSD& untrusted) +{ + LL_DEBUGS("AppInit") << "LLMessageSystem::setMessageBans:" << LL_ENDL; + 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) + { + LL_INFOS("AppInit") << " " << name << " banned from " + << (ban_from_trusted ? "TRUSTED " : " ") + << (ban_from_untrusted ? "UNTRUSTED " : " ") + << LL_ENDL; + any_set = true; + } + } + + if (!any_set) + { + LL_DEBUGS("AppInit") << " no messages banned" << LL_ENDL; + } + + check_for_unrecognized_messages("trusted", trusted, mMessageTemplates); + check_for_unrecognized_messages("untrusted", untrusted, mMessageTemplates); +} + +S32 LLMessageSystem::sendError( + const LLHost& host, + const LLUUID& agent_id, + S32 code, + const std::string& token, + const LLUUID& id, + const std::string& system, + const std::string& message, + const LLSD& data) +{ + newMessage("Error"); + nextBlockFast(_PREHASH_AgentData); + addUUIDFast(_PREHASH_AgentID, agent_id); + nextBlockFast(_PREHASH_Data); + addS32("Code", code); + addString("Token", token); + addUUID("ID", id); + addString("System", system); + std::string temp; + temp = message; + if(temp.size() > (size_t)MTUBYTES) temp.resize((size_t)MTUBYTES); + addString("Message", message); + LLPointer<LLSDBinaryFormatter> formatter = new LLSDBinaryFormatter; + std::ostringstream ostr; + formatter->format(data, ostr); + temp = ostr.str(); + bool pack_data = true; + static const std::string ERROR_MESSAGE_NAME("Error"); + if (LLMessageConfig::getMessageFlavor(ERROR_MESSAGE_NAME) == + LLMessageConfig::TEMPLATE_FLAVOR) + { + S32 msg_size = temp.size() + mMessageBuilder->getMessageSize(); + if(msg_size >= ETHERNET_MTU_BYTES) + { + pack_data = false; + } + } + if(pack_data) + { + addBinaryData("Data", (void*)temp.c_str(), temp.size()); + } + else + { + LL_WARNS("Messaging") << "Data and message were too large -- data removed." + << LL_ENDL; + addBinaryData("Data", NULL, 0); + } + return sendReliable(host); +} + +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); +// LL_DEBUGS("Messaging") << "ack recvd' from " << host << " for packet " << (TPACKETID)packet_id << LL_ENDL; + cdp->ackReliablePacket(packet_id); + } + if (!cdp->getUnackedPacketCount()) + { + // Remove this circuit from the list of circuits with unacked packets + gMessageSystem->mCircuitInfo.mUnackedCircuitMap.erase(host); + } + } +} + + +/* +void process_log_messages(LLMessageSystem* msg, void**) +{ + U8 log_message; + + msg->getU8Fast(_PREHASH_Options, _PREHASH_Enable, log_message); + + if (log_message) + { + LL_INFOS("Messaging") << "Starting logging via message" << LL_ENDL; + msg->startLogging(); + } + else + { + LL_INFOS("Messaging") << "Stopping logging via message" << LL_ENDL; + 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) + { + LL_WARNS("Messaging") << "Attempt to create trusted circuit without circuit data: " + << msg->getSender() << LL_ENDL; + 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; + } + + U32 untrusted_interface = msg->getUntrustedInterface().getAddress(); + U32 last_interface = msg->getReceivingInterface().getAddress(); + if ( ( untrusted_interface != INVALID_HOST_IP_ADDRESS ) && ( untrusted_interface == last_interface ) ) + { + if( msg->getBlockUntrustedInterface() ) + { + LL_WARNS("Messaging") << "Ignoring CreateTrustedCircuit on public interface from host: " + << msg->getSender() << LL_ENDL; + return; + } + else + { + LL_WARNS("Messaging") << "Processing CreateTrustedCircuit on public interface from host: " + << msg->getSender() << LL_ENDL; + } + } + + char their_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ + S32 size = msg->getSizeFast(_PREHASH_DataBlock, _PREHASH_Digest); + if(size != MD5HEX_STR_BYTES) + { + // ignore requests which pack the wrong amount of data. + return; + } + msg->getBinaryDataFast(_PREHASH_DataBlock, _PREHASH_Digest, their_digest, MD5HEX_STR_BYTES); + their_digest[MD5HEX_STR_SIZE - 1] = '\0'; + if(msg->isMatchingDigestForWindowAndUUIDs(their_digest, TRUST_TIME_WINDOW, local_id, remote_id)) + { + cdp->setTrusted(true); + LL_INFOS("Messaging") << "Trusted digest from " << msg->getSender() << LL_ENDL; + 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. + LL_WARNS("Messaging") << "Ignoring bad digest from known trusted circuit: " << their_digest + << " host: " << msg->getSender() << LL_ENDL; + return; + } + else + { + LL_WARNS("Messaging") << "Bad digest from known circuit: " << their_digest + << " host: " << msg->getSender() << LL_ENDL; + 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; + } + + U32 untrusted_interface = msg->getUntrustedInterface().getAddress(); + U32 last_interface = msg->getReceivingInterface().getAddress(); + if ( ( untrusted_interface != INVALID_HOST_IP_ADDRESS ) && ( untrusted_interface == last_interface ) ) + { + if( msg->getBlockUntrustedInterface() ) + { + LL_WARNS("Messaging") << "Ignoring DenyTrustedCircuit on public interface from host: " + << msg->getSender() << LL_ENDL; + return; + } + else + { + LL_WARNS("Messaging") << "Processing DenyTrustedCircuit on public interface from host: " + << msg->getSender() << LL_ENDL; + } + } + + + // 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. + // *TODO: probably should keep a count of number of resends + // per circuit, and stop resending after a while. + LL_INFOS("Messaging") << "Got DenyTrustedCircuit. Sending CreateTrustedCircuit to " + << msg->getSender() << LL_ENDL; + msg->sendCreateTrustedCircuit(msg->getSender(), local_id, remote_id); +} + + +void dump_prehash_files() +{ + U32 i; + std::string filename("../../indra/llmessage/message_prehash.h"); + LLFILE* fp = LLFile::fopen(filename, "w"); /* Flawfinder: ignore */ + if (fp) + { + fprintf( + fp, + "/**\n" + " * @file message_prehash.h\n" + " * @brief header file of externs of prehashed variables plus defines.\n" + " *\n" + " * $LicenseInfo:firstyear=2003&license=viewerlgpl$" + " * $/LicenseInfo$" + " */\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 const gPrehashVersionNumber;\n\n"); + for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++) + { + if (!LLMessageStringTable::getInstance()->mEmpty[i] && LLMessageStringTable::getInstance()->mString[i][0] != '.') + { + fprintf(fp, "extern char const* const _PREHASH_%s;\n", LLMessageStringTable::getInstance()->mString[i]); + } + } + fprintf(fp, "\n\n#endif\n"); + fclose(fp); + } + filename = std::string("../../indra/llmessage/message_prehash.cpp"); + fp = LLFile::fopen(filename, "w"); /* Flawfinder: ignore */ + if (fp) + { + fprintf( + fp, + "/**\n" + " * @file message_prehash.cpp\n" + " * @brief file of prehashed variables\n" + " *\n" + " * $LicenseInfo:firstyear=2003&license=viewerlgpl$" + " * $/LicenseInfo$" + " */\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 const gPrehashVersionNumber = %.3ff;\n\n", gMessageSystem->mMessageFileVersionNumber); + for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++) + { + if (!LLMessageStringTable::getInstance()->mEmpty[i] && LLMessageStringTable::getInstance()->mString[i][0] != '.') + { + fprintf(fp, "char const* const _PREHASH_%s = LLMessageStringTable::getInstance()->getString(\"%s\");\n", LLMessageStringTable::getInstance()->mString[i], LLMessageStringTable::getInstance()->mString[i]); + } + } + 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, + const LLUseCircuitCodeResponder* responder, + bool failure_is_fatal, + const F32 circuit_heartbeat_interval, + const F32 circuit_timeout) +{ + gMessageSystem = new LLMessageSystem( + template_name, + port, + version_major, + version_minor, + version_patch, + failure_is_fatal, + circuit_heartbeat_interval, + circuit_timeout); + g_shared_secret.assign(secret); + + if (!gMessageSystem) + { + LL_ERRS("AppInit") << "Messaging system initialization failed." << LL_ENDL; + return false; + } + + // bail if system encountered an error. + if(!gMessageSystem->isOK()) + { + return false; + } + + if (b_dump_prehash_file) + { + dump_prehash_files(); + exit(0); + } + else + { + if (gMessageSystem->mMessageFileVersionNumber != gPrehashVersionNumber) + { + LL_INFOS("AppInit") << "Message template version does not match prehash version number" << LL_ENDL; + LL_INFOS("AppInit") << "Run simulator with -prehash command line option to rebuild prehash data" << LL_ENDL; + } + else + { + LL_DEBUGS("AppInit") << "Message template version matches prehash version number" << LL_ENDL; + } + } + + 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, (void**)responder); + gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck, process_packet_ack, 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); + gMessageSystem->setHandlerFunc("Error", LLMessageSystem::processError); + + // 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"; + LL_INFOS("Messaging") << str.str() << LL_ENDL; +} + +void LLMessageSystem::stopLogging() +{ + if(mVerboseLog) + { + mVerboseLog = false; + LL_INFOS("Messaging") << "END MESSAGE LOG" << LL_ENDL; + } +} + +void LLMessageSystem::summarizeLogs(std::ostream& str) +{ + std::string buffer; + std::string tmp_str; + F32 run_time = mMessageSystemTimer.getElapsedTimeF32(); + str << "START MESSAGE LOG SUMMARY" << std::endl; + buffer = llformat( "Run time: %12.3f seconds", run_time); + + // Incoming + str << buffer << std::endl << "Incoming:" << std::endl; + tmp_str = U64_to_str(mTotalBytesIn); + buffer = llformat( "Total bytes received: %20s (%5.2f kbits per second)", tmp_str.c_str(), ((F32)mTotalBytesIn * 0.008f) / run_time); + str << buffer << std::endl; + tmp_str = U64_to_str(mPacketsIn); + buffer = llformat( "Total packets received: %20s (%5.2f packets per second)", tmp_str.c_str(), ((F32) mPacketsIn / run_time)); + str << buffer << std::endl; + buffer = llformat( "Average packet size: %20.0f bytes", (F32)mTotalBytesIn / (F32)mPacketsIn); + str << buffer << std::endl; + tmp_str = U64_to_str(mReliablePacketsIn); + buffer = llformat( "Total reliable packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mReliablePacketsIn)/((F32) mPacketsIn + 1)); + str << buffer << std::endl; + tmp_str = U64_to_str(mCompressedPacketsIn); + buffer = llformat( "Total compressed packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mCompressedPacketsIn)/((F32) mPacketsIn + 1)); + str << buffer << std::endl; + S64 savings = mUncompressedBytesIn - mCompressedBytesIn; + tmp_str = U64_to_str(savings); + buffer = llformat( "Total compression savings: %20s bytes", tmp_str.c_str()); + str << buffer << std::endl; + tmp_str = U64_to_str(savings/(mCompressedPacketsIn +1)); + buffer = llformat( "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mUncompressedBytesIn)/((F32) mCompressedBytesIn+1)); + str << buffer << std::endl; + tmp_str = U64_to_str(savings/(mPacketsIn+1)); + buffer = llformat( "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mTotalBytesIn + (F32) savings)/((F32) mTotalBytesIn + 1.f)); + + // Outgoing + str << buffer << std::endl << std::endl << "Outgoing:" << std::endl; + tmp_str = U64_to_str(mTotalBytesOut); + buffer = llformat( "Total bytes sent: %20s (%5.2f kbits per second)", tmp_str.c_str(), ((F32)mTotalBytesOut * 0.008f) / run_time ); + str << buffer << std::endl; + tmp_str = U64_to_str(mPacketsOut); + buffer = llformat( "Total packets sent: %20s (%5.2f packets per second)", tmp_str.c_str(), ((F32)mPacketsOut / run_time)); + str << buffer << std::endl; + buffer = llformat( "Average packet size: %20.0f bytes", (F32)mTotalBytesOut / (F32)mPacketsOut); + str << buffer << std::endl; + tmp_str = U64_to_str(mReliablePacketsOut); + buffer = llformat( "Total reliable packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mReliablePacketsOut)/((F32) mPacketsOut + 1)); + str << buffer << std::endl; + tmp_str = U64_to_str(mCompressedPacketsOut); + buffer = llformat( "Total compressed packets: %20s (%5.2f%%)", tmp_str.c_str(), 100.f * ((F32) mCompressedPacketsOut)/((F32) mPacketsOut + 1)); + str << buffer << std::endl; + savings = mUncompressedBytesOut - mCompressedBytesOut; + tmp_str = U64_to_str(savings); + buffer = llformat( "Total compression savings: %20s bytes", tmp_str.c_str()); + str << buffer << std::endl; + tmp_str = U64_to_str(savings/(mCompressedPacketsOut +1)); + buffer = llformat( "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mUncompressedBytesOut)/((F32) mCompressedBytesOut+1)); + str << buffer << std::endl; + tmp_str = U64_to_str(savings/(mPacketsOut+1)); + buffer = llformat( "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str.c_str(), ((F32) mTotalBytesOut + (F32) savings)/((F32) mTotalBytesOut + 1.f)); + str << buffer << std::endl << std::endl; + buffer = llformat( "SendPacket failures: %20d", mSendPacketFailureCount); + str << buffer << std::endl; + buffer = llformat( "Dropped packets: %20d", mDroppedPackets); + str << buffer << std::endl; + buffer = llformat( "Resent packets: %20d", mResentPackets); + str << buffer << std::endl; + buffer = llformat( "Failed reliable resends: %20d", mFailedResendPackets); + str << buffer << std::endl; + buffer = llformat( "Off-circuit rejected packets: %17d", mOffCircuitPackets); + str << buffer << std::endl; + buffer = llformat( "On-circuit invalid packets: %17d", mInvalidOnCircuitPackets); + str << buffer << std::endl << std::endl; + + str << "Decoding: " << std::endl; + buffer = llformat( "%35s%10s%10s%10s%10s", "Message", "Count", "Time", "Max", "Avg"); + str << buffer << std:: endl; + F32 avg; + for (message_template_name_map_t::const_iterator iter = mMessageTemplates.begin(), + end = mMessageTemplates.end(); + iter != end; iter++) + { + const LLMessageTemplate* mt = iter->second; + if(mt->mTotalDecoded > 0) + { + avg = mt->mTotalDecodeTime / (F32)mt->mTotalDecoded; + buffer = llformat( "%35s%10u%10f%10f%10f", mt->mName, mt->mTotalDecoded, mt->mTotalDecodeTime, mt->mMaxDecodeTimePerMsg, avg); + str << buffer << std::endl; + } + } + str << "END MESSAGE LOG SUMMARY" << std::endl; +} + +void end_messaging_system(bool print_summary) +{ + gTransferManager.cleanup(); + LLTransferTargetVFile::updateQueue(true); // shutdown LLTransferTargetVFile + if (gMessageSystem) + { + gMessageSystem->stopLogging(); + + if (print_summary) + { + std::ostringstream str; + gMessageSystem->summarizeLogs(str); + LL_INFOS("Messaging") << str.str().c_str() << LL_ENDL; + } + + delete static_cast<LLMessageSystem*>(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) + { + LL_DEBUGS("Messaging") << "Dump: " << mNumMessageCounts << " messages processed in " << mReceiveTime << " seconds" << LL_ENDL; + for (message_template_name_map_t::const_iterator iter = mMessageTemplates.begin(), + end = mMessageTemplates.end(); + iter != end; iter++) + { + const LLMessageTemplate* mt = iter->second; + if (mt->mReceiveCount > 0) + { + LL_INFOS("Messaging") << "Num: " << std::setw(3) << mt->mReceiveCount << " Bytes: " << std::setw(6) << mt->mReceiveBytes + << " Invalid: " << std::setw(3) << mt->mReceiveInvalid << " " << mt->mName << " " << ll_round(100 * mt->mDecodeTimeThisFrame / mReceiveTime.value()) << "%" << LL_ENDL; + } + } + } +} + + + +bool LLMessageSystem::isClear() const +{ + return mMessageBuilder->isClear(); +} + + +S32 LLMessageSystem::flush(const LLHost &host) +{ + if (mMessageBuilder->getMessageSize()) + { + S32 sentbytes = sendMessage(host); + clearMessage(); + return sentbytes; + } + else + { + return 0; + } +} + +U32 LLMessageSystem::getListenPort( void ) const +{ + return mPort; +} + +// TODO: babbage: remove this horror! +S32 LLMessageSystem::zeroCodeAdjustCurrentSendTotal() +{ + if(mMessageBuilder == mLLSDMessageBuilder) + { + // babbage: don't compress LLSD messages, so delta is 0 + return 0; + } + + if (! mMessageBuilder->isBuilt()) + { + mSendSize = mMessageBuilder->buildMessage( + mSendBuffer, + MAX_BUFFER_SIZE, + 0); + } + // TODO: babbage: remove this horror + mMessageBuilder->setBuilt(false); + + S32 count = mSendSize; + + S32 net_gain = 0; + U8 num_zeroes = 0; + + U8 *inptr = (U8 *)mSendBuffer; + +// skip the packet id field + + for (U32 ii = 0; ii < LL_PACKET_ID_SIZE; ++ii) + { + 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_MINIMUM_VALID_PACKET_SIZE) + { + LL_WARNS("Messaging") << "zeroCodeExpand() called with data_size of " << *data_size + << LL_ENDL; + } + + mTotalBytesIn += *data_size; + + // if we're not zero-coded, simply return. + if (!(*data[0] & LL_ZERO_CODE_FLAG)) + { + 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 ii = 0; ii < LL_PACKET_ID_SIZE; ++ii) + { + 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])) + { + LL_WARNS("Messaging") << "attempt to write past reasonable encoded buffer size 1" << LL_ENDL; + callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE); + outptr = mEncodedRecvBuffer; + break; + } + if (!((*outptr++ = *inptr++))) + { + while (((count--)) && (!(*inptr))) + { + *outptr++ = *inptr++; + if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-256])) + { + LL_WARNS("Messaging") << "attempt to write past reasonable encoded buffer size 2" << LL_ENDL; + 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)])) + { + LL_WARNS("Messaging") << "attempt to write past reasonable encoded buffer size 3" << LL_ENDL; + 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) + { + LL_ERRS("Messaging") << templatep->mName << " already used as a template name!" + << LL_ENDL; + } + 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 + { + LL_ERRS("Messaging") << name << " is not a known message name!" << LL_ENDL; + } +} + +bool LLMessageSystem::callHandler(const char *name, + bool trustedSource, LLMessageSystem* msg) +{ + name = LLMessageStringTable::getInstance()->getString(name); + message_template_name_map_t::const_iterator iter; + iter = mMessageTemplates.find(name); + if(iter == mMessageTemplates.end()) + { + LL_WARNS("Messaging") << "LLMessageSystem::callHandler: unknown message " + << name << LL_ENDL; + return false; + } + + const LLMessageTemplate* msg_template = iter->second; + if (msg_template->isBanned(trustedSource)) + { + LL_WARNS("Messaging") << "LLMessageSystem::callHandler: banned message " + << name + << " from " + << (trustedSource ? "trusted " : "untrusted ") + << "source" << LL_ENDL; + 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()) + { + return false; + } + + exception_t& ex = it->second; + msg_exception_callback ex_cb = ex.first; + + if (!ex_cb) + { + LL_WARNS("Messaging") << "LLMessageSystem::callExceptionFunc: bad message exception callback." << LL_ENDL; + return false; + } + + (ex_cb)(this, ex.second, exception); + + return true; +} + +void LLMessageSystem::setTimingFunc(msg_timing_callback func, void* data) +{ + mTimingCallback = func; + mTimingCallbackData = data; +} + +bool LLMessageSystem::isCircuitCodeKnown(U32 code) const +{ + if(mCircuitCodes.find(code) == mCircuitCodes.end()) + return false; + return true; +} + +bool LLMessageSystem::isMessageFast(const char *msg) +{ + return msg == mMessageReader->getMessageName(); +} + + +char* LLMessageSystem::getMessageName() +{ + return const_cast<char*>(mMessageReader->getMessageName()); +} + +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; +} + +bool LLMessageSystem::generateDigestForNumberAndUUIDs( + char* digest, + const U32 number, + const LLUUID& id1, + const LLUUID& id2) const +{ + // *NOTE: This method is needlessly inefficient. Instead of + // calling LLUUID::asString, it should just call + // LLUUID::toString(). + + const char *colon = ":"; + char tbuf[16]; /* Flawfinder: ignore */ + LLMD5 d; + std::string id1string = id1.asString(); + std::string id2string = id2.asString(); + 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)); /* Flawfinder: ignore */ + } + + 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)); /* Flawfinder: ignore */ + } + d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */ + + if( (char*) id2str != NULL) + { + d.update(id2str, (U32)strlen((char *) id2str)); /* Flawfinder: ignore */ + } + + 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()) + { + LL_ERRS("Messaging") << "Trying to generate complex digest on a machine without a shared secret!" << LL_ENDL; + } + + U32 now = (U32)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()) + { + LL_ERRS("Messaging") << "Trying to compare complex digests on a machine without a shared secret!" << LL_ENDL; + } + + char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ + U32 now = (U32)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()) + { + LL_ERRS("Messaging") << "Trying to generate simple digest on a machine without a shared secret!" << LL_ENDL; + } + + U32 now = (U32)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()) + { + LL_ERRS("Messaging") << "Trying to compare simple digests on a machine without a shared secret!" << LL_ENDL; + } + + 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()) + { + LL_WARNS("Messaging") << "Can't send CreateTrustedCircuit to " << host << " because we don't have the local end point ID" << LL_ENDL; + return; + } + if (id2.isNull()) + { + LL_WARNS("Messaging") << "Can't send CreateTrustedCircuit to " << host << " because we don't have the remote end point ID" << LL_ENDL; + 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); + LL_INFOS("Messaging") << "xmitting digest: " << digest << " Host: " << host << LL_ENDL; + sendMessage(host); +} + +void LLMessageSystem::sendDenyTrustedCircuit(const LLHost &host) +{ + mDenyTrustedCircuitSet.insert(host); +} + +void LLMessageSystem::reallySendDenyTrustedCircuit(const LLHost &host) +{ + LLCircuitData *cdp = mCircuitInfo.findCircuit(host); + if (!cdp) + { + LL_WARNS("Messaging") << "Not sending DenyTrustedCircuit to host without a circuit." << LL_ENDL; + return; + } + LL_INFOS("Messaging") << "Sending DenyTrustedCircuit to " << host << LL_ENDL; + 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 ) +{ + LockMessageChecker lmc(this); + + std::string shared_secret = get_shared_secret(); + if(shared_secret.empty()) + { + LL_ERRS("Messaging") << "Trying to establish bidirectional trust on a machine without a shared secret!" << LL_ENDL; + } + 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 (lmc.checkMessages( frame_count )) + { + if (isMessageFast(_PREHASH_CompletePingCheck) && + (getSender() == host)) + { + break; + } + } + lmc.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. + lmc.checkMessages(frame_count); + lmc.processAcks(); + ms_sleep(1); + } +} + + +void LLMessageSystem::dumpPacketToLog() +{ + LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing.getLastSender() << LL_ENDL; + LL_WARNS("Messaging") << "Packet Size:" << mTrueReceiveSize << LL_ENDL; + char line_buffer[256]; /* Flawfinder: ignore */ + S32 i; + S32 cur_line_pos = 0; + S32 cur_line = 0; + + for (i = 0; i < mTrueReceiveSize; i++) + { + S32 offset = cur_line_pos * 3; + snprintf(line_buffer + offset, sizeof(line_buffer) - offset, + "%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */ + cur_line_pos++; + if (cur_line_pos >= 16) + { + cur_line_pos = 0; + LL_WARNS("Messaging") << "PD:" << cur_line << "PD:" << line_buffer << LL_ENDL; + cur_line++; + } + } + if (cur_line_pos) + { + LL_WARNS("Messaging") << "PD:" << cur_line << "PD:" << line_buffer << LL_ENDL; + } +} + + +//static +U64Microseconds LLMessageSystem::getMessageTimeUsecs(const bool update) +{ + if (gMessageSystem) + { + if (update) + { + gMessageSystem->mCurrentMessageTime = totalTime(); + } + return gMessageSystem->mCurrentMessageTime; + } + else + { + return totalTime(); + } +} + +//static +F64Seconds LLMessageSystem::getMessageTimeSeconds(const bool update) +{ + if (gMessageSystem) + { + if (update) + { + gMessageSystem->mCurrentMessageTime = totalTime(); + } + return gMessageSystem->mCurrentMessageTime; + } + else + { + return F64Seconds(totalTime()); + } +} + +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; +} + +typedef std::map<const char*, LLMessageBuilder*> BuilderMap; + +void LLMessageSystem::newMessageFast(const char *name) +{ + //LL_DEBUGS("Messaging") << "creating new message: " << name << LL_ENDL; + LLMessageConfig::Flavor message_flavor = + LLMessageConfig::getMessageFlavor(name); + LLMessageConfig::Flavor server_flavor = + LLMessageConfig::getServerDefaultFlavor(); + + if(message_flavor == LLMessageConfig::TEMPLATE_FLAVOR) + { + mMessageBuilder = mTemplateMessageBuilder; + } + else if (message_flavor == LLMessageConfig::LLSD_FLAVOR) + { + mMessageBuilder = mLLSDMessageBuilder; + } + // NO_FLAVOR + else + { + if (server_flavor == LLMessageConfig::LLSD_FLAVOR) + { + mMessageBuilder = mLLSDMessageBuilder; + } + // TEMPLATE_FLAVOR or NO_FLAVOR + else + { + mMessageBuilder = mTemplateMessageBuilder; + } + } + mSendReliable = false; + mMessageBuilder->newMessage(name); +} + +void LLMessageSystem::newMessage(const char *name) +{ + newMessageFast(LLMessageStringTable::getInstance()->getString(name)); +} + +void LLMessageSystem::addBinaryDataFast(const char *varname, const void *data, S32 size) +{ + mMessageBuilder->addBinaryData(varname, data, size); +} + +void LLMessageSystem::addBinaryData(const char *varname, const void *data, S32 size) +{ + mMessageBuilder->addBinaryData(LLMessageStringTable::getInstance()->getString(varname),data, size); +} + +void LLMessageSystem::addS8Fast(const char *varname, S8 v) +{ + mMessageBuilder->addS8(varname, v); +} + +void LLMessageSystem::addS8(const char *varname, S8 v) +{ + mMessageBuilder->addS8(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addU8Fast(const char *varname, U8 v) +{ + mMessageBuilder->addU8(varname, v); +} + +void LLMessageSystem::addU8(const char *varname, U8 v) +{ + mMessageBuilder->addU8(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addS16Fast(const char *varname, S16 v) +{ + mMessageBuilder->addS16(varname, v); +} + +void LLMessageSystem::addS16(const char *varname, S16 v) +{ + mMessageBuilder->addS16(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addU16Fast(const char *varname, U16 v) +{ + mMessageBuilder->addU16(varname, v); +} + +void LLMessageSystem::addU16(const char *varname, U16 v) +{ + mMessageBuilder->addU16(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addF32Fast(const char *varname, F32 v) +{ + mMessageBuilder->addF32(varname, v); +} + +void LLMessageSystem::addF32(const char *varname, F32 v) +{ + mMessageBuilder->addF32(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addS32Fast(const char *varname, S32 v) +{ + mMessageBuilder->addS32(varname, v); +} + +void LLMessageSystem::addS32(const char *varname, S32 v) +{ + mMessageBuilder->addS32(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addU32Fast(const char *varname, U32 v) +{ + mMessageBuilder->addU32(varname, v); +} + +void LLMessageSystem::addU32(const char *varname, U32 v) +{ + mMessageBuilder->addU32(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addU64Fast(const char *varname, U64 v) +{ + mMessageBuilder->addU64(varname, v); +} + +void LLMessageSystem::addU64(const char *varname, U64 v) +{ + mMessageBuilder->addU64(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addF64Fast(const char *varname, F64 v) +{ + mMessageBuilder->addF64(varname, v); +} + +void LLMessageSystem::addF64(const char *varname, F64 v) +{ + mMessageBuilder->addF64(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addIPAddrFast(const char *varname, U32 v) +{ + mMessageBuilder->addIPAddr(varname, v); +} + +void LLMessageSystem::addIPAddr(const char *varname, U32 v) +{ + mMessageBuilder->addIPAddr(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addIPPortFast(const char *varname, U16 v) +{ + mMessageBuilder->addIPPort(varname, v); +} + +void LLMessageSystem::addIPPort(const char *varname, U16 v) +{ + mMessageBuilder->addIPPort(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addBOOLFast(const char* varname, bool v) +{ + mMessageBuilder->addBOOL(varname, v); +} + +void LLMessageSystem::addBOOL(const char* varname, bool v) +{ + mMessageBuilder->addBOOL(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addStringFast(const char* varname, const char* v) +{ + mMessageBuilder->addString(varname, v); +} + +void LLMessageSystem::addString(const char* varname, const char* v) +{ + mMessageBuilder->addString(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addStringFast(const char* varname, const std::string& v) +{ + mMessageBuilder->addString(varname, v); +} + +void LLMessageSystem::addString(const char* varname, const std::string& v) +{ + mMessageBuilder->addString(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addVector3Fast(const char *varname, const LLVector3& v) +{ + mMessageBuilder->addVector3(varname, v); +} + +void LLMessageSystem::addVector3(const char *varname, const LLVector3& v) +{ + mMessageBuilder->addVector3(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addVector4Fast(const char *varname, const LLVector4& v) +{ + mMessageBuilder->addVector4(varname, v); +} + +void LLMessageSystem::addVector4(const char *varname, const LLVector4& v) +{ + mMessageBuilder->addVector4(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addVector3dFast(const char *varname, const LLVector3d& v) +{ + mMessageBuilder->addVector3d(varname, v); +} + +void LLMessageSystem::addVector3d(const char *varname, const LLVector3d& v) +{ + mMessageBuilder->addVector3d(LLMessageStringTable::getInstance()->getString(varname), v); +} + +void LLMessageSystem::addQuatFast(const char *varname, const LLQuaternion& v) +{ + mMessageBuilder->addQuat(varname, v); +} + +void LLMessageSystem::addQuat(const char *varname, const LLQuaternion& v) +{ + mMessageBuilder->addQuat(LLMessageStringTable::getInstance()->getString(varname), v); +} + + +void LLMessageSystem::addUUIDFast(const char *varname, const LLUUID& v) +{ + mMessageBuilder->addUUID(varname, v); +} + +void LLMessageSystem::addUUID(const char *varname, const LLUUID& v) +{ + mMessageBuilder->addUUID(LLMessageStringTable::getInstance()->getString(varname), v); +} + +S32 LLMessageSystem::getCurrentSendTotal() const +{ + return mMessageBuilder->getMessageSize(); +} + +void LLMessageSystem::getS8Fast(const char *block, const char *var, S8 &u, + S32 blocknum) +{ + mMessageReader->getS8(block, var, u, blocknum); +} + +void LLMessageSystem::getS8(const char *block, const char *var, S8 &u, + S32 blocknum) +{ + getS8Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), u, blocknum); +} + +void LLMessageSystem::getU8Fast(const char *block, const char *var, U8 &u, + S32 blocknum) +{ + mMessageReader->getU8(block, var, u, blocknum); +} + +void LLMessageSystem::getU8(const char *block, const char *var, U8 &u, + S32 blocknum) +{ + getU8Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), u, blocknum); +} + +void LLMessageSystem::getBOOLFast(const char *block, const char *var, bool &b, + S32 blocknum) +{ + mMessageReader->getBOOL(block, var, b, blocknum); +} + +void LLMessageSystem::getBOOL(const char *block, const char *var, bool &b, + S32 blocknum) +{ + getBOOLFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), b, blocknum); +} + +void LLMessageSystem::getS16Fast(const char *block, const char *var, S16 &d, + S32 blocknum) +{ + mMessageReader->getS16(block, var, d, blocknum); +} + +void LLMessageSystem::getS16(const char *block, const char *var, S16 &d, + S32 blocknum) +{ + getS16Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + +void LLMessageSystem::getU16Fast(const char *block, const char *var, U16 &d, + S32 blocknum) +{ + mMessageReader->getU16(block, var, d, blocknum); +} + +void LLMessageSystem::getU16(const char *block, const char *var, U16 &d, + S32 blocknum) +{ + getU16Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + +void LLMessageSystem::getS32Fast(const char *block, const char *var, S32 &d, + S32 blocknum) +{ + mMessageReader->getS32(block, var, d, blocknum); +} + +void LLMessageSystem::getS32(const char *block, const char *var, S32 &d, + S32 blocknum) +{ + getS32Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + +void LLMessageSystem::getU32Fast(const char *block, const char *var, U32 &d, + S32 blocknum) +{ + mMessageReader->getU32(block, var, d, blocknum); +} + +void LLMessageSystem::getU32(const char *block, const char *var, U32 &d, + S32 blocknum) +{ + getU32Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + +void LLMessageSystem::getU64Fast(const char *block, const char *var, U64 &d, + S32 blocknum) +{ + mMessageReader->getU64(block, var, d, blocknum); +} + +void LLMessageSystem::getU64(const char *block, const char *var, U64 &d, + S32 blocknum) +{ + + getU64Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + +void LLMessageSystem::getBinaryDataFast(const char *blockname, + const char *varname, + void *datap, S32 size, + S32 blocknum, S32 max_size) +{ + mMessageReader->getBinaryData(blockname, varname, datap, size, blocknum, + max_size); +} + +void LLMessageSystem::getBinaryData(const char *blockname, + const char *varname, + void *datap, S32 size, + S32 blocknum, S32 max_size) +{ + getBinaryDataFast(LLMessageStringTable::getInstance()->getString(blockname), + LLMessageStringTable::getInstance()->getString(varname), + datap, size, blocknum, max_size); +} + +void LLMessageSystem::getF32Fast(const char *block, const char *var, F32 &d, + S32 blocknum) +{ + mMessageReader->getF32(block, var, d, blocknum); +} + +void LLMessageSystem::getF32(const char *block, const char *var, F32 &d, + S32 blocknum) +{ + getF32Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + +void LLMessageSystem::getF64Fast(const char *block, const char *var, F64 &d, + S32 blocknum) +{ + mMessageReader->getF64(block, var, d, blocknum); +} + +void LLMessageSystem::getF64(const char *block, const char *var, F64 &d, + S32 blocknum) +{ + getF64Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), d, blocknum); +} + + +void LLMessageSystem::getVector3Fast(const char *block, const char *var, + LLVector3 &v, S32 blocknum ) +{ + mMessageReader->getVector3(block, var, v, blocknum); +} + +void LLMessageSystem::getVector3(const char *block, const char *var, + LLVector3 &v, S32 blocknum ) +{ + getVector3Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), v, blocknum); +} + +void LLMessageSystem::getVector4Fast(const char *block, const char *var, + LLVector4 &v, S32 blocknum ) +{ + mMessageReader->getVector4(block, var, v, blocknum); +} + +void LLMessageSystem::getVector4(const char *block, const char *var, + LLVector4 &v, S32 blocknum ) +{ + getVector4Fast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), v, blocknum); +} + +void LLMessageSystem::getVector3dFast(const char *block, const char *var, + LLVector3d &v, S32 blocknum ) +{ + mMessageReader->getVector3d(block, var, v, blocknum); +} + +void LLMessageSystem::getVector3d(const char *block, const char *var, + LLVector3d &v, S32 blocknum ) +{ + getVector3dFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), v, blocknum); +} + +void LLMessageSystem::getQuatFast(const char *block, const char *var, + LLQuaternion &q, S32 blocknum ) +{ + mMessageReader->getQuat(block, var, q, blocknum); +} + +void LLMessageSystem::getQuat(const char *block, const char *var, + LLQuaternion &q, S32 blocknum) +{ + getQuatFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), q, blocknum); +} + +void LLMessageSystem::getUUIDFast(const char *block, const char *var, + LLUUID &u, S32 blocknum ) +{ + mMessageReader->getUUID(block, var, u, blocknum); +} + +void LLMessageSystem::getUUID(const char *block, const char *var, LLUUID &u, + S32 blocknum ) +{ + getUUIDFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), u, blocknum); +} + +void LLMessageSystem::getIPAddrFast(const char *block, const char *var, + U32 &u, S32 blocknum) +{ + mMessageReader->getIPAddr(block, var, u, blocknum); +} + +void LLMessageSystem::getIPAddr(const char *block, const char *var, U32 &u, + S32 blocknum) +{ + getIPAddrFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), u, blocknum); +} + +void LLMessageSystem::getIPPortFast(const char *block, const char *var, + U16 &u, S32 blocknum) +{ + mMessageReader->getIPPort(block, var, u, blocknum); +} + +void LLMessageSystem::getIPPort(const char *block, const char *var, U16 &u, + S32 blocknum) +{ + getIPPortFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), u, + blocknum); +} + + +void LLMessageSystem::getStringFast(const char *block, const char *var, + S32 buffer_size, char *s, S32 blocknum) +{ + if(buffer_size <= 0) + { + LL_WARNS("Messaging") << "buffer_size <= 0" << LL_ENDL; + } + mMessageReader->getString(block, var, buffer_size, s, blocknum); +} + +void LLMessageSystem::getString(const char *block, const char *var, + S32 buffer_size, char *s, S32 blocknum ) +{ + getStringFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), buffer_size, s, + blocknum); +} + +void LLMessageSystem::getStringFast(const char *block, const char *var, + std::string& outstr, S32 blocknum) +{ + mMessageReader->getString(block, var, outstr, blocknum); +} + +void LLMessageSystem::getString(const char *block, const char *var, + std::string& outstr, S32 blocknum ) +{ + getStringFast(LLMessageStringTable::getInstance()->getString(block), + LLMessageStringTable::getInstance()->getString(var), outstr, + blocknum); +} + +bool LLMessageSystem::has(const char *blockname) const +{ + return getNumberOfBlocks(blockname) > 0; +} + +S32 LLMessageSystem::getNumberOfBlocksFast(const char *blockname) const +{ + return mMessageReader->getNumberOfBlocks(blockname); +} + +S32 LLMessageSystem::getNumberOfBlocks(const char *blockname) const +{ + return getNumberOfBlocksFast(LLMessageStringTable::getInstance()->getString(blockname)); +} + +S32 LLMessageSystem::getSizeFast(const char *blockname, const char *varname) const +{ + return mMessageReader->getSize(blockname, varname); +} + +S32 LLMessageSystem::getSize(const char *blockname, const char *varname) const +{ + return getSizeFast(LLMessageStringTable::getInstance()->getString(blockname), + LLMessageStringTable::getInstance()->getString(varname)); +} + +// size in bytes of variable length data +S32 LLMessageSystem::getSizeFast(const char *blockname, S32 blocknum, + const char *varname) const +{ + return mMessageReader->getSize(blockname, blocknum, varname); +} + +S32 LLMessageSystem::getSize(const char *blockname, S32 blocknum, + const char *varname) const +{ + return getSizeFast(LLMessageStringTable::getInstance()->getString(blockname), blocknum, + LLMessageStringTable::getInstance()->getString(varname)); +} + +S32 LLMessageSystem::getReceiveSize() const +{ + return mMessageReader->getMessageSize(); +} + +//static +void LLMessageSystem::setTimeDecodes( bool b ) +{ + LLMessageReader::setTimeDecodes(b); +} + +//static +void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds ) +{ + LLMessageReader::setTimeDecodesSpamThreshold(seconds); +} + +LockMessageChecker::LockMessageChecker(LLMessageSystem* msgsystem): + // for the lifespan of this LockMessageChecker instance, use + // LLTemplateMessageReader as msgsystem's mMessageReader + LockMessageReader(msgsystem->mMessageReader, msgsystem->mTemplateMessageReader), + mMessageSystem(msgsystem) +{} + +// HACK! babbage: return true if message rxed via either UDP or HTTP +// TODO: babbage: move gServicePump in to LLMessageSystem? +bool LLMessageSystem::checkAllMessages(LockMessageChecker& lmc, S64 frame_count, LLPumpIO* http_pump) +{ + if(lmc.checkMessages(frame_count)) + { + return true; + } + U32 packetsIn = mPacketsIn; + http_pump->pump(); + http_pump->callback(); + return (mPacketsIn - packetsIn) > 0; +} + +void LLMessageSystem::banUdpMessage(const std::string& name) +{ + message_template_name_map_t::iterator itt = mMessageTemplates.find( + LLMessageStringTable::getInstance()->getString(name.c_str()) + ); + if(itt != mMessageTemplates.end()) + { + itt->second->banUdp(); + } + else + { + LL_WARNS() << "Attempted to ban an unknown message: " << name << "." << LL_ENDL; + } +} +const LLHost& LLMessageSystem::getSender() const +{ + return mLastSender; +} + +void LLMessageSystem::sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("untrustedSimulatorMessage", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + + if (url.empty()) + { + LL_WARNS() << "sendUntrustedSimulatorMessageCoro called with empty capability!" << LL_ENDL; + return; + } + + LL_INFOS() << "sendUntrustedSimulatorMessageCoro: message " << message << " to cap " << url << LL_ENDL; + LLSD postData; + postData["message"] = message; + postData["body"] = body; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if ((callback) && (!callback.empty())) + callback((status) ? LL_ERR_NOERR : LL_ERR_TCP_TIMEOUT); +} + + +LLHTTPRegistration<LLHTTPNodeAdapter<LLTrustedMessageService> > + gHTTPRegistrationTrustedMessageWildcard("/trusted-message/<message-name>"); + diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 8f15bc266d..b4b0d94021 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -1,1171 +1,1171 @@ -/**
- * @file message.h
- * @brief LLMessageSystem class header file
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_MESSAGE_H
-#define LL_MESSAGE_H
-
-#include <cstring>
-#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 "llstringtable.h"
-#include "llcircuit.h"
-#include "lltimer.h"
-#include "llpacketring.h"
-#include "llhost.h"
-#include "llhttpnode.h"
-//#include "llpacketack.h"
-#include "llsingleton.h"
-#include "message_prehash.h"
-#include "llstl.h"
-#include "llmsgvariabletype.h"
-#include "llmessagesenderinterface.h"
-
-#include "llstoredmessage.h"
-#include "boost/function.hpp"
-#include "llpounceable.h"
-#include "llcoros.h"
-#include LLCOROS_MUTEX_HEADER
-
-const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
-const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
-
-const S32 MESSAGE_MAX_PER_FRAME = 400;
-
-class LLMessageStringTable : public LLSingleton<LLMessageStringTable>
-{
- LLSINGLETON(LLMessageStringTable);
- ~LLMessageStringTable();
-
-public:
- 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 */
-};
-
-
-// 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;
-
-// 1 byte flags, 4 bytes sequence, 1 byte offset + 1 byte message name (high)
-const S32 LL_MINIMUM_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 1;
-enum EPacketHeaderLayout
-{
- PHL_FLAGS = 0,
- PHL_PACKET_ID = 1,
- PHL_OFFSET = 5,
- PHL_NAME = 6
-};
-
-
-const S32 LL_DEFAULT_RELIABLE_RETRIES = 3;
-const F32Seconds LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS(1.f);
-const F32Seconds LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS(1.f);
-const F32Seconds LL_PING_BASED_TIMEOUT_DUMMY(0.0f);
-
-const F32 LL_SEMIRELIABLE_TIMEOUT_FACTOR = 5.f; // averaged ping
-const F32 LL_RELIABLE_TIMEOUT_FACTOR = 5.f; // averaged ping
-const F32 LL_LOST_TIMEOUT_FACTOR = 16.f; // averaged ping for marking packets "Lost"
-const F32Seconds LL_MAX_LOST_TIMEOUT(5.f); // Maximum amount of time before considering something "lost"
-
-const S32 MAX_MESSAGE_COUNT_NUM = 1024;
-
-// Forward declarations
-class LLVector3;
-class LLVector4;
-class LLVector3d;
-class LLQuaternion;
-class LLSD;
-class LLUUID;
-class LLMessageSystem;
-class LLPumpIO;
-
-// 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);
-
-
-// message data pieces are used to collect the data called for by the message template
-class LLMsgData;
-class LLMsgBlkData;
-class LLMessageTemplate;
-
-class LLMessagePollInfo;
-class LLMessageBuilder;
-class LLTemplateMessageBuilder;
-class LLSDMessageBuilder;
-class LLMessageReader;
-class LLTemplateMessageReader;
-class LLSDMessageReader;
-
-
-
-class LLUseCircuitCodeResponder
-{
- LOG_CLASS(LLMessageSystem);
-
-public:
- virtual ~LLUseCircuitCodeResponder();
- virtual void complete(const LLHost& host, const LLUUID& agent) const = 0;
-};
-
-/**
- * SL-12204: We've observed crashes when consumer code sets
- * LLMessageSystem::mMessageReader, assuming that all subsequent processing of
- * the current message will use the same mMessageReader value -- only to have
- * a different coroutine sneak in and replace mMessageReader before
- * completion. This is a limitation of sharing a stateful global resource for
- * message parsing; instead code receiving a new message should instantiate a
- * (trivially constructed) local message parser and use that.
- *
- * Until then, when one coroutine sets a particular LLMessageReader subclass
- * as the current message reader, ensure that no other coroutine can replace
- * it until the first coroutine has finished with its message.
- *
- * This is achieved with two helper classes. LLMessageSystem::mMessageReader
- * is now an LLMessageReaderPointer instance, which can efficiently compare or
- * dereference its contained LLMessageReader* but which cannot be directly
- * assigned. To change the value of LLMessageReaderPointer, you must
- * instantiate LockMessageReader with the LLMessageReader* you wish to make
- * current. mMessageReader will have that value for the lifetime of the
- * LockMessageReader instance, then revert to nullptr. Moreover, as its name
- * implies, LockMessageReader locks the mutex in LLMessageReaderPointer so
- * that any other coroutine instantiating LockMessageReader will block until
- * the first coroutine has destroyed its instance.
- */
-class LLMessageReaderPointer
-{
-public:
- LLMessageReaderPointer(): mPtr(nullptr) {}
- // It is essential that comparison and dereferencing must be fast, which
- // is why we don't check for nullptr when dereferencing.
- LLMessageReader* operator->() const { return mPtr; }
- bool operator==(const LLMessageReader* other) const { return mPtr == other; }
- bool operator!=(const LLMessageReader* other) const { return ! (*this == other); }
-private:
- // Only LockMessageReader can set mPtr.
- friend class LockMessageReader;
- LLMessageReader* mPtr;
- LLCoros::Mutex mMutex;
-};
-
-/**
- * To set mMessageReader to nullptr:
- *
- * @code
- * // use an anonymous instance that is destroyed immediately
- * LockMessageReader(gMessageSystem->mMessageReader, nullptr);
- * @endcode
- *
- * Why do we still require going through LockMessageReader at all? Because it
- * would be Bad if any coroutine set mMessageReader to nullptr while another
- * coroutine was still parsing a message.
- */
-class LockMessageReader
-{
-public:
- LockMessageReader(LLMessageReaderPointer& var, LLMessageReader* instance):
- mVar(var.mPtr),
- mLock(var.mMutex)
- {
- mVar = instance;
- }
- // Some compilers reportedly fail to suppress generating implicit copy
- // operations even though we have a move-only LockType data member.
- LockMessageReader(const LockMessageReader&) = delete;
- LockMessageReader& operator=(const LockMessageReader&) = delete;
- ~LockMessageReader()
- {
- mVar = nullptr;
- }
-private:
- // capture a reference to LLMessageReaderPointer::mPtr
- decltype(LLMessageReaderPointer::mPtr)& mVar;
- // while holding a lock on LLMessageReaderPointer::mMutex
- LLCoros::LockType mLock;
-};
-
-/**
- * LockMessageReader is great as long as you only need mMessageReader locked
- * during a single LLMessageSystem function call. However, empirically the
- * sequence from checkAllMessages() through processAcks() need mMessageReader
- * locked to LLTemplateMessageReader. Enforce that by making them require an
- * instance of LockMessageChecker.
- */
-class LockMessageChecker;
-
-class LLMessageSystem : public LLMessageSenderInterface
-{
- private:
- U8 mSendBuffer[MAX_BUFFER_SIZE];
- S32 mSendSize;
-
- bool mBlockUntrustedInterface;
- LLHost mUntrustedInterface;
-
- public:
- LLPacketRing mPacketRing;
- LLReliablePacketParams mReliablePacketParams;
-
- // Set this flag to true when you want *very* verbose logs.
- bool mVerboseLog;
-
- 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;
- F64Seconds mCircuitPrintTime; // used to print circuit debug info every couple minutes
- F32Seconds mCircuitPrintFreq;
-
- 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 std::string& filename, U32 port, S32 version_major,
- S32 version_minor, S32 version_patch,
- bool failure_is_fatal,
- const F32 circuit_heartbeat_interval, const F32 circuit_timeout);
-
- ~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 std::string& filename, bool failure_is_fatal);
-
-
- // 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(LLMessageStringTable::getInstance()->getString(name), handler_func, user_data);
- }
-
- // 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);
-
- // Set a function that will be called once per packet processed with the
- // hashed message name and the time spent in the processing handler function
- // measured in seconds. JC
- typedef void (*msg_timing_callback)(const char* hashed_name, F32 time, void* data);
- void setTimingFunc(msg_timing_callback func, void* data = NULL);
- msg_timing_callback getTimingCallback()
- {
- return mTimingCallback;
- }
- void* getTimingCallbackData()
- {
- return mTimingCallbackData;
- }
-
- // 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(LockMessageChecker&, S64 frame_count = 0 );
- void processAcks(LockMessageChecker&, F32 collect_time = 0.f);
-
- bool isMessageFast(const char *msg);
- bool isMessage(const char *msg)
- {
- return isMessageFast(LLMessageStringTable::getInstance()->getString(msg));
- }
-
- void dumpPacketToLog();
-
- char *getMessageName();
-
- const LLHost& getSender() const;
- U32 getSenderIP() const; // getSender() is preferred
- U32 getSenderPort() const; // getSender() is preferred
-
- const LLHost& getReceivingInterface() const;
-
- // 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; }
-
- void newMessageFast(const char *name);
- void newMessage(const char *name);
-
-
-public:
- LLStoredMessagePtr getReceivedMessage() const;
- LLStoredMessagePtr getBuiltMessage() const;
- S32 sendMessage(const LLHost &host, LLStoredMessagePtr message);
-
-private:
- LLSD getReceivedMessageLLSD() const;
- LLSD getBuiltMessageLLSD() const;
-
- // NOTE: babbage: Only use to support legacy misuse of the
- // LLMessageSystem API where values are dangerously written
- // as one type and read as another. LLSD does not support
- // dangerous conversions and so converting the message to an
- // LLSD would result in the reads failing. All code which
- // misuses the message system in this way should be made safe
- // but while the unsafe code is run in old processes, this
- // method should be used to forward unsafe messages.
- LLSD wrapReceivedTemplateData() const;
- LLSD wrapBuiltTemplateData() const;
-
-public:
-
- void copyMessageReceivedToSend();
- void clearMessage();
-
- void nextBlockFast(const char *blockname);
- void nextBlock(const char *blockname);
-
-public:
- void addBinaryDataFast(const char *varname, const void *data, S32 size);
- void addBinaryData(const char *varname, const void *data, S32 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
- 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
- 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;
- TPACKETID getCurrentRecvPacketID() { return mCurrentRecvPacketID; }
-
- // 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
- 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,
- F32Seconds 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 forwardReliable(
- const LLHost &host,
- S32 retries,
- bool ping_based_timeout,
- F32Seconds timeout,
- void (*callback)(void **,S32),
- void ** callback_data);
-
- S32 sendMessage(const LLHost &host);
- S32 sendMessage(const U32 circuit);
-private:
- S32 sendMessage(const LLHost &host, const char* name,
- const LLSD& message);
-public:
- // bool decodeData(const U8 *buffer, const LLHost &host);
-
- /**
- gets binary data from the current message.
-
- @param blockname the name of the block in the message (from the message template)
-
- @param varname
-
- @param datap
-
- @param size expected size - set to zero to get any amount of data up to max_size.
- Make sure max_size is set in that case!
-
- @param blocknum
-
- @param max_size the max number of bytes to read
- */
- void getBinaryDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX);
- void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- void getStringFast( const char *block, const char *var, std::string& outstr, S32 blocknum = 0);
- void getString( const char *block, const char *var, std::string& outstr, 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();
- void getCircuitInfo(LLSD& info) const;
-
- 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);
-
- /** Return false if host is unknown or untrusted */
- // Note:DaveH/Babbage some trusted messages can be received without a circuit
- bool isTrustedSender(const LLHost& host) const;
-
- /** Return true if current message is from trusted source */
- bool isTrustedSender() const;
-
- /** Return false true if name is unknown or untrusted */
- bool isTrustedMessage(const std::string& name) const;
-
- /** Return false true if name is unknown or trusted */
- bool isUntrustedMessage(const std::string& name) const;
-
- // Mark an interface ineligible for trust
- void setUntrustedInterface( const LLHost host ) { mUntrustedInterface = host; }
- LLHost getUntrustedInterface() const { return mUntrustedInterface; }
- void setBlockUntrustedInterface( bool block ) { mBlockUntrustedInterface = block; } // Throw a switch to allow, sending warnings only
- bool getBlockUntrustedInterface() const { return mBlockUntrustedInterface; }
-
- // Change this message to be UDP black listed.
- void banUdpMessage(const std::string& name);
-
-
-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
- // Note:DaveH/Babbage some trusted messages can be received without a 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();
-
- bool has(const char *blockname) const;
- S32 getNumberOfBlocksFast(const char *blockname) const;
- S32 getNumberOfBlocks(const char *blockname) const;
- S32 getSizeFast(const char *blockname, const char *varname) const;
- S32 getSize(const char *blockname, const char *varname) const;
- S32 getSizeFast(const char *blockname, S32 blocknum,
- const char *varname) const; // size in bytes of data
- S32 getSize(const char *blockname, S32 blocknum, const char *varname) const;
-
- void resetReceiveCounts(); // resets receive counts for all message types to 0
- void dumpReceiveCounts(); // dumps receive count for each message type to LL_INFOS()
- void dumpCircuitInfo(); // Circuit information to LL_INFOS()
-
- 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;
- 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)
-
- static U64Microseconds getMessageTimeUsecs(const bool update = false); // Get the current message system time in microseconds
- static F64Seconds getMessageTimeSeconds(const bool update = false); // Get the current message system time in seconds
-
- static void setTimeDecodes(bool b);
- static void setTimeDecodesSpamThreshold(F32 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**);
- static void processError(LLMessageSystem* msg, void**);
-
- // dispatch llsd message to http node tree
- static void dispatch(const std::string& msg_name,
- const LLSD& message);
- static void dispatch(const std::string& msg_name,
- const LLSD& message,
- LLHTTPNode::ResponsePtr responsep);
-
- // this is added to support specific legacy messages and is
- // ***not intended for general use*** Si, Gabriel, 2009
- static void dispatchTemplate(const std::string& msg_name,
- const LLSD& message,
- LLHTTPNode::ResponsePtr responsep);
-
- void setMessageBans(const LLSD& trusted, const LLSD& untrusted);
-
- /**
- * @brief send an error message to the host. This is a helper method.
- *
- * @param host Destination host.
- * @param agent_id Destination agent id (may be null)
- * @param code An HTTP status compatible error code.
- * @param token A specific short string based message
- * @param id The transactionid/uniqueid/sessionid whatever.
- * @param system The hierarchical path to the system (255 bytes)
- * @param message Human readable message (1200 bytes)
- * @param data Extra info.
- * @return Returns value returned from sendReliable().
- */
- S32 sendError(
- const LLHost& host,
- const LLUUID& agent_id,
- S32 code,
- const std::string& token,
- const LLUUID& id,
- const std::string& system,
- const std::string& message,
- const LLSD& data);
-
- // Check UDP messages and pump http_pump to receive HTTP messages.
- bool checkAllMessages(LockMessageChecker&, S64 frame_count, LLPumpIO* http_pump);
-
- // Moved to allow access from LLTemplateMessageDispatcher
- void clearReceiveState();
-
- // This will cause all trust queries to return true until the next message
- // is read: use with caution!
- void receivedMessageFromTrustedSender();
-
-private:
- typedef boost::function<void(S32)> UntrustedCallback_t;
- void sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback);
-
-
- bool mLastMessageFromTrustedMessageService;
-
- // 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;
-
- void addTemplate(LLMessageTemplate *templatep);
- 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 );
-
- 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
-
- bool mbError;
- S32 mErrorCode;
-
- F64Seconds mResendDumpTime; // The last time we dumped resends
-
- LLMessageCountInfo mMessageCountList[MAX_MESSAGE_COUNT_NUM];
- S32 mNumMessageCounts;
- F32Seconds mReceiveTime;
- F32Seconds mMaxMessageTime; // Max number of seconds for processing messages
- S32 mMaxMessageCounts; // Max number of messages to process before dumping.
- F64Seconds mMessageCountTime;
-
- F64Seconds mCurrentMessageTime; // 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;
-
- msg_timing_callback mTimingCallback;
- void* mTimingCallbackData;
-
- void init(); // ctor shared initialisation.
-
- LLHost mLastSender;
- LLHost mLastReceivingIF;
- S32 mIncomingCompressedSize; // original size of compressed msg (0 if uncomp.)
- TPACKETID mCurrentRecvPacketID; // packet ID of current receive packet (for reporting)
-
- LLMessageBuilder* mMessageBuilder;
- LLTemplateMessageBuilder* mTemplateMessageBuilder;
- LLSDMessageBuilder* mLLSDMessageBuilder;
- LLMessageReaderPointer mMessageReader;
- LLTemplateMessageReader* mTemplateMessageReader;
- LLSDMessageReader* mLLSDMessageReader;
-
- friend class LLMessageHandlerBridge;
- friend class LockMessageChecker;
-
- bool callHandler(const char *name, bool trustedSource,
- LLMessageSystem* msg);
-
-
- /** Find, create or revive circuit for host as needed */
- LLCircuitData* findCircuit(const LLHost& host, bool resetPacketId);
-};
-
-
-// external hook into messaging system
-extern LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
-
-// Implementation of LockMessageChecker depends on definition of
-// LLMessageSystem, hence must follow it.
-class LockMessageChecker: public LockMessageReader
-{
-public:
- LockMessageChecker(LLMessageSystem* msgsystem);
-
- // For convenience, provide forwarding wrappers so you can call (e.g.)
- // checkAllMessages() on your LockMessageChecker instance instead of
- // passing the instance to LLMessageSystem::checkAllMessages(). Use
- // perfect forwarding to avoid having to maintain these wrappers in sync
- // with the target methods.
- template <typename... ARGS>
- bool checkAllMessages(ARGS&&... args)
- {
- return mMessageSystem->checkAllMessages(*this, std::forward<ARGS>(args)...);
- }
-
- template <typename... ARGS>
- bool checkMessages(ARGS&&... args)
- {
- return mMessageSystem->checkMessages(*this, std::forward<ARGS>(args)...);
- }
-
- template <typename... ARGS>
- void processAcks(ARGS&&... args)
- {
- return mMessageSystem->processAcks(*this, std::forward<ARGS>(args)...);
- }
-
-private:
- LLMessageSystem* mMessageSystem;
-};
-
-// 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,
- const LLUseCircuitCodeResponder* responder,
- bool failure_is_fatal,
- const F32 circuit_heartbeat_interval,
- const F32 circuit_timeout);
-
-void end_messaging_system(bool print_summary = true);
-
-void null_message_callback(LLMessageSystem *msg, void **data);
-
-//
-// Inlines
-//
-
-#if !defined( LL_BIG_ENDIAN ) && !defined( LL_LITTLE_ENDIAN )
-#error Unknown endianness for htolememcpy. Did you miss a common include?
-#endif
-
-static inline void *htolememcpy(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)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#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)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#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)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#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)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#ifdef LL_BIG_ENDIAN
- htolememcpy(s + 8, ct + 8, MVT_F32, 4);
- htolememcpy(s + 4, ct + 4, MVT_F32, 4);
- return(htolememcpy(s, ct, MVT_F32, 4));
-#else
- return(memcpy(s,ct,n)); /* Flawfinder: ignore */
-#endif
-
- case MVT_LLVector3d:
- if (n != 24)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#ifdef LL_BIG_ENDIAN
- htolememcpy(s + 16, ct + 16, MVT_F64, 8);
- htolememcpy(s + 8, ct + 8, MVT_F64, 8);
- return(htolememcpy(s, ct, MVT_F64, 8));
-#else
- return(memcpy(s,ct,n)); /* Flawfinder: ignore */
-#endif
-
- case MVT_LLVector4:
- if (n != 16)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#ifdef LL_BIG_ENDIAN
- htolememcpy(s + 12, ct + 12, MVT_F32, 4);
- htolememcpy(s + 8, ct + 8, MVT_F32, 4);
- htolememcpy(s + 4, ct + 4, MVT_F32, 4);
- return(htolememcpy(s, ct, MVT_F32, 4));
-#else
- return(memcpy(s,ct,n)); /* Flawfinder: ignore */
-#endif
-
- case MVT_U16Vec3:
- if (n != 6)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#ifdef LL_BIG_ENDIAN
- htolememcpy(s + 4, ct + 4, MVT_U16, 2);
- htolememcpy(s + 2, ct + 2, MVT_U16, 2);
- return(htolememcpy(s, ct, MVT_U16, 2));
-#else
- return(memcpy(s,ct,n)); /* Flawfinder: ignore */
-#endif
-
- case MVT_U16Quat:
- if (n != 8)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#ifdef LL_BIG_ENDIAN
- htolememcpy(s + 6, ct + 6, MVT_U16, 2);
- htolememcpy(s + 4, ct + 4, MVT_U16, 2);
- htolememcpy(s + 2, ct + 2, MVT_U16, 2);
- return(htolememcpy(s, ct, MVT_U16, 2));
-#else
- return(memcpy(s,ct,n)); /* Flawfinder: ignore */
-#endif
-
- case MVT_S16Array:
- if (n % 2)
- {
- LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL;
- }
-#ifdef LL_BIG_ENDIAN
- length = n % 2;
- for (i = 1; i < length; i++)
- {
- htolememcpy(s + i*2, ct + i*2, MVT_S16, 2);
- }
- return(htolememcpy(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(htolememcpy(s,ct,type, n));
-}
-
-inline const LLHost& LLMessageSystem::getReceivingInterface() const {return mLastReceivingIF;}
-
-inline U32 LLMessageSystem::getSenderIP() const
-{
- return mLastSender.getAddress();
-}
-
-inline U32 LLMessageSystem::getSenderPort() const
-{
- return mLastSender.getPort();
-}
-
-
-//-----------------------------------------------------------------------------
-// Transmission aliases
-//-----------------------------------------------------------------------------
-
-inline S32 LLMessageSystem::sendMessage(const U32 circuit)
-{
- return sendMessage(findHost(circuit));
-}
-
-#endif
+/** + * @file message.h + * @brief LLMessageSystem class header file + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_MESSAGE_H +#define LL_MESSAGE_H + +#include <cstring> +#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 "llstringtable.h" +#include "llcircuit.h" +#include "lltimer.h" +#include "llpacketring.h" +#include "llhost.h" +#include "llhttpnode.h" +//#include "llpacketack.h" +#include "llsingleton.h" +#include "message_prehash.h" +#include "llstl.h" +#include "llmsgvariabletype.h" +#include "llmessagesenderinterface.h" + +#include "llstoredmessage.h" +#include "boost/function.hpp" +#include "llpounceable.h" +#include "llcoros.h" +#include LLCOROS_MUTEX_HEADER + +const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; +const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; + +const S32 MESSAGE_MAX_PER_FRAME = 400; + +class LLMessageStringTable : public LLSingleton<LLMessageStringTable> +{ + LLSINGLETON(LLMessageStringTable); + ~LLMessageStringTable(); + +public: + 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 */ +}; + + +// 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; + +// 1 byte flags, 4 bytes sequence, 1 byte offset + 1 byte message name (high) +const S32 LL_MINIMUM_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 1; +enum EPacketHeaderLayout +{ + PHL_FLAGS = 0, + PHL_PACKET_ID = 1, + PHL_OFFSET = 5, + PHL_NAME = 6 +}; + + +const S32 LL_DEFAULT_RELIABLE_RETRIES = 3; +const F32Seconds LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS(1.f); +const F32Seconds LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS(1.f); +const F32Seconds LL_PING_BASED_TIMEOUT_DUMMY(0.0f); + +const F32 LL_SEMIRELIABLE_TIMEOUT_FACTOR = 5.f; // averaged ping +const F32 LL_RELIABLE_TIMEOUT_FACTOR = 5.f; // averaged ping +const F32 LL_LOST_TIMEOUT_FACTOR = 16.f; // averaged ping for marking packets "Lost" +const F32Seconds LL_MAX_LOST_TIMEOUT(5.f); // Maximum amount of time before considering something "lost" + +const S32 MAX_MESSAGE_COUNT_NUM = 1024; + +// Forward declarations +class LLVector3; +class LLVector4; +class LLVector3d; +class LLQuaternion; +class LLSD; +class LLUUID; +class LLMessageSystem; +class LLPumpIO; + +// 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); + + +// message data pieces are used to collect the data called for by the message template +class LLMsgData; +class LLMsgBlkData; +class LLMessageTemplate; + +class LLMessagePollInfo; +class LLMessageBuilder; +class LLTemplateMessageBuilder; +class LLSDMessageBuilder; +class LLMessageReader; +class LLTemplateMessageReader; +class LLSDMessageReader; + + + +class LLUseCircuitCodeResponder +{ + LOG_CLASS(LLMessageSystem); + +public: + virtual ~LLUseCircuitCodeResponder(); + virtual void complete(const LLHost& host, const LLUUID& agent) const = 0; +}; + +/** + * SL-12204: We've observed crashes when consumer code sets + * LLMessageSystem::mMessageReader, assuming that all subsequent processing of + * the current message will use the same mMessageReader value -- only to have + * a different coroutine sneak in and replace mMessageReader before + * completion. This is a limitation of sharing a stateful global resource for + * message parsing; instead code receiving a new message should instantiate a + * (trivially constructed) local message parser and use that. + * + * Until then, when one coroutine sets a particular LLMessageReader subclass + * as the current message reader, ensure that no other coroutine can replace + * it until the first coroutine has finished with its message. + * + * This is achieved with two helper classes. LLMessageSystem::mMessageReader + * is now an LLMessageReaderPointer instance, which can efficiently compare or + * dereference its contained LLMessageReader* but which cannot be directly + * assigned. To change the value of LLMessageReaderPointer, you must + * instantiate LockMessageReader with the LLMessageReader* you wish to make + * current. mMessageReader will have that value for the lifetime of the + * LockMessageReader instance, then revert to nullptr. Moreover, as its name + * implies, LockMessageReader locks the mutex in LLMessageReaderPointer so + * that any other coroutine instantiating LockMessageReader will block until + * the first coroutine has destroyed its instance. + */ +class LLMessageReaderPointer +{ +public: + LLMessageReaderPointer(): mPtr(nullptr) {} + // It is essential that comparison and dereferencing must be fast, which + // is why we don't check for nullptr when dereferencing. + LLMessageReader* operator->() const { return mPtr; } + bool operator==(const LLMessageReader* other) const { return mPtr == other; } + bool operator!=(const LLMessageReader* other) const { return ! (*this == other); } +private: + // Only LockMessageReader can set mPtr. + friend class LockMessageReader; + LLMessageReader* mPtr; + LLCoros::Mutex mMutex; +}; + +/** + * To set mMessageReader to nullptr: + * + * @code + * // use an anonymous instance that is destroyed immediately + * LockMessageReader(gMessageSystem->mMessageReader, nullptr); + * @endcode + * + * Why do we still require going through LockMessageReader at all? Because it + * would be Bad if any coroutine set mMessageReader to nullptr while another + * coroutine was still parsing a message. + */ +class LockMessageReader +{ +public: + LockMessageReader(LLMessageReaderPointer& var, LLMessageReader* instance): + mVar(var.mPtr), + mLock(var.mMutex) + { + mVar = instance; + } + // Some compilers reportedly fail to suppress generating implicit copy + // operations even though we have a move-only LockType data member. + LockMessageReader(const LockMessageReader&) = delete; + LockMessageReader& operator=(const LockMessageReader&) = delete; + ~LockMessageReader() + { + mVar = nullptr; + } +private: + // capture a reference to LLMessageReaderPointer::mPtr + decltype(LLMessageReaderPointer::mPtr)& mVar; + // while holding a lock on LLMessageReaderPointer::mMutex + LLCoros::LockType mLock; +}; + +/** + * LockMessageReader is great as long as you only need mMessageReader locked + * during a single LLMessageSystem function call. However, empirically the + * sequence from checkAllMessages() through processAcks() need mMessageReader + * locked to LLTemplateMessageReader. Enforce that by making them require an + * instance of LockMessageChecker. + */ +class LockMessageChecker; + +class LLMessageSystem : public LLMessageSenderInterface +{ + private: + U8 mSendBuffer[MAX_BUFFER_SIZE]; + S32 mSendSize; + + bool mBlockUntrustedInterface; + LLHost mUntrustedInterface; + + public: + LLPacketRing mPacketRing; + LLReliablePacketParams mReliablePacketParams; + + // Set this flag to true when you want *very* verbose logs. + bool mVerboseLog; + + 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; + F64Seconds mCircuitPrintTime; // used to print circuit debug info every couple minutes + F32Seconds mCircuitPrintFreq; + + 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 std::string& filename, U32 port, S32 version_major, + S32 version_minor, S32 version_patch, + bool failure_is_fatal, + const F32 circuit_heartbeat_interval, const F32 circuit_timeout); + + ~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 std::string& filename, bool failure_is_fatal); + + + // 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(LLMessageStringTable::getInstance()->getString(name), handler_func, user_data); + } + + // 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); + + // Set a function that will be called once per packet processed with the + // hashed message name and the time spent in the processing handler function + // measured in seconds. JC + typedef void (*msg_timing_callback)(const char* hashed_name, F32 time, void* data); + void setTimingFunc(msg_timing_callback func, void* data = NULL); + msg_timing_callback getTimingCallback() + { + return mTimingCallback; + } + void* getTimingCallbackData() + { + return mTimingCallbackData; + } + + // 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(LockMessageChecker&, S64 frame_count = 0 ); + void processAcks(LockMessageChecker&, F32 collect_time = 0.f); + + bool isMessageFast(const char *msg); + bool isMessage(const char *msg) + { + return isMessageFast(LLMessageStringTable::getInstance()->getString(msg)); + } + + void dumpPacketToLog(); + + char *getMessageName(); + + const LLHost& getSender() const; + U32 getSenderIP() const; // getSender() is preferred + U32 getSenderPort() const; // getSender() is preferred + + const LLHost& getReceivingInterface() const; + + // 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; } + + void newMessageFast(const char *name); + void newMessage(const char *name); + + +public: + LLStoredMessagePtr getReceivedMessage() const; + LLStoredMessagePtr getBuiltMessage() const; + S32 sendMessage(const LLHost &host, LLStoredMessagePtr message); + +private: + LLSD getReceivedMessageLLSD() const; + LLSD getBuiltMessageLLSD() const; + + // NOTE: babbage: Only use to support legacy misuse of the + // LLMessageSystem API where values are dangerously written + // as one type and read as another. LLSD does not support + // dangerous conversions and so converting the message to an + // LLSD would result in the reads failing. All code which + // misuses the message system in this way should be made safe + // but while the unsafe code is run in old processes, this + // method should be used to forward unsafe messages. + LLSD wrapReceivedTemplateData() const; + LLSD wrapBuiltTemplateData() const; + +public: + + void copyMessageReceivedToSend(); + void clearMessage(); + + void nextBlockFast(const char *blockname); + void nextBlock(const char *blockname); + +public: + void addBinaryDataFast(const char *varname, const void *data, S32 size); + void addBinaryData(const char *varname, const void *data, S32 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 + 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 + 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; + TPACKETID getCurrentRecvPacketID() { return mCurrentRecvPacketID; } + + // 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 + 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, + F32Seconds 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 forwardReliable( + const LLHost &host, + S32 retries, + bool ping_based_timeout, + F32Seconds timeout, + void (*callback)(void **,S32), + void ** callback_data); + + S32 sendMessage(const LLHost &host); + S32 sendMessage(const U32 circuit); +private: + S32 sendMessage(const LLHost &host, const char* name, + const LLSD& message); +public: + // bool decodeData(const U8 *buffer, const LLHost &host); + + /** + gets binary data from the current message. + + @param blockname the name of the block in the message (from the message template) + + @param varname + + @param datap + + @param size expected size - set to zero to get any amount of data up to max_size. + Make sure max_size is set in that case! + + @param blocknum + + @param max_size the max number of bytes to read + */ + void getBinaryDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX); + void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + void getStringFast( const char *block, const char *var, std::string& outstr, S32 blocknum = 0); + void getString( const char *block, const char *var, std::string& outstr, 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(); + void getCircuitInfo(LLSD& info) const; + + 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); + + /** Return false if host is unknown or untrusted */ + // Note:DaveH/Babbage some trusted messages can be received without a circuit + bool isTrustedSender(const LLHost& host) const; + + /** Return true if current message is from trusted source */ + bool isTrustedSender() const; + + /** Return false true if name is unknown or untrusted */ + bool isTrustedMessage(const std::string& name) const; + + /** Return false true if name is unknown or trusted */ + bool isUntrustedMessage(const std::string& name) const; + + // Mark an interface ineligible for trust + void setUntrustedInterface( const LLHost host ) { mUntrustedInterface = host; } + LLHost getUntrustedInterface() const { return mUntrustedInterface; } + void setBlockUntrustedInterface( bool block ) { mBlockUntrustedInterface = block; } // Throw a switch to allow, sending warnings only + bool getBlockUntrustedInterface() const { return mBlockUntrustedInterface; } + + // Change this message to be UDP black listed. + void banUdpMessage(const std::string& name); + + +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 + // Note:DaveH/Babbage some trusted messages can be received without a 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(); + + bool has(const char *blockname) const; + S32 getNumberOfBlocksFast(const char *blockname) const; + S32 getNumberOfBlocks(const char *blockname) const; + S32 getSizeFast(const char *blockname, const char *varname) const; + S32 getSize(const char *blockname, const char *varname) const; + S32 getSizeFast(const char *blockname, S32 blocknum, + const char *varname) const; // size in bytes of data + S32 getSize(const char *blockname, S32 blocknum, const char *varname) const; + + void resetReceiveCounts(); // resets receive counts for all message types to 0 + void dumpReceiveCounts(); // dumps receive count for each message type to LL_INFOS() + void dumpCircuitInfo(); // Circuit information to LL_INFOS() + + 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; + 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) + + static U64Microseconds getMessageTimeUsecs(const bool update = false); // Get the current message system time in microseconds + static F64Seconds getMessageTimeSeconds(const bool update = false); // Get the current message system time in seconds + + static void setTimeDecodes(bool b); + static void setTimeDecodesSpamThreshold(F32 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**); + static void processError(LLMessageSystem* msg, void**); + + // dispatch llsd message to http node tree + static void dispatch(const std::string& msg_name, + const LLSD& message); + static void dispatch(const std::string& msg_name, + const LLSD& message, + LLHTTPNode::ResponsePtr responsep); + + // this is added to support specific legacy messages and is + // ***not intended for general use*** Si, Gabriel, 2009 + static void dispatchTemplate(const std::string& msg_name, + const LLSD& message, + LLHTTPNode::ResponsePtr responsep); + + void setMessageBans(const LLSD& trusted, const LLSD& untrusted); + + /** + * @brief send an error message to the host. This is a helper method. + * + * @param host Destination host. + * @param agent_id Destination agent id (may be null) + * @param code An HTTP status compatible error code. + * @param token A specific short string based message + * @param id The transactionid/uniqueid/sessionid whatever. + * @param system The hierarchical path to the system (255 bytes) + * @param message Human readable message (1200 bytes) + * @param data Extra info. + * @return Returns value returned from sendReliable(). + */ + S32 sendError( + const LLHost& host, + const LLUUID& agent_id, + S32 code, + const std::string& token, + const LLUUID& id, + const std::string& system, + const std::string& message, + const LLSD& data); + + // Check UDP messages and pump http_pump to receive HTTP messages. + bool checkAllMessages(LockMessageChecker&, S64 frame_count, LLPumpIO* http_pump); + + // Moved to allow access from LLTemplateMessageDispatcher + void clearReceiveState(); + + // This will cause all trust queries to return true until the next message + // is read: use with caution! + void receivedMessageFromTrustedSender(); + +private: + typedef boost::function<void(S32)> UntrustedCallback_t; + void sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback); + + + bool mLastMessageFromTrustedMessageService; + + // 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; + + void addTemplate(LLMessageTemplate *templatep); + 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 ); + + 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 + + bool mbError; + S32 mErrorCode; + + F64Seconds mResendDumpTime; // The last time we dumped resends + + LLMessageCountInfo mMessageCountList[MAX_MESSAGE_COUNT_NUM]; + S32 mNumMessageCounts; + F32Seconds mReceiveTime; + F32Seconds mMaxMessageTime; // Max number of seconds for processing messages + S32 mMaxMessageCounts; // Max number of messages to process before dumping. + F64Seconds mMessageCountTime; + + F64Seconds mCurrentMessageTime; // 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; + + msg_timing_callback mTimingCallback; + void* mTimingCallbackData; + + void init(); // ctor shared initialisation. + + LLHost mLastSender; + LLHost mLastReceivingIF; + S32 mIncomingCompressedSize; // original size of compressed msg (0 if uncomp.) + TPACKETID mCurrentRecvPacketID; // packet ID of current receive packet (for reporting) + + LLMessageBuilder* mMessageBuilder; + LLTemplateMessageBuilder* mTemplateMessageBuilder; + LLSDMessageBuilder* mLLSDMessageBuilder; + LLMessageReaderPointer mMessageReader; + LLTemplateMessageReader* mTemplateMessageReader; + LLSDMessageReader* mLLSDMessageReader; + + friend class LLMessageHandlerBridge; + friend class LockMessageChecker; + + bool callHandler(const char *name, bool trustedSource, + LLMessageSystem* msg); + + + /** Find, create or revive circuit for host as needed */ + LLCircuitData* findCircuit(const LLHost& host, bool resetPacketId); +}; + + +// external hook into messaging system +extern LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem; + +// Implementation of LockMessageChecker depends on definition of +// LLMessageSystem, hence must follow it. +class LockMessageChecker: public LockMessageReader +{ +public: + LockMessageChecker(LLMessageSystem* msgsystem); + + // For convenience, provide forwarding wrappers so you can call (e.g.) + // checkAllMessages() on your LockMessageChecker instance instead of + // passing the instance to LLMessageSystem::checkAllMessages(). Use + // perfect forwarding to avoid having to maintain these wrappers in sync + // with the target methods. + template <typename... ARGS> + bool checkAllMessages(ARGS&&... args) + { + return mMessageSystem->checkAllMessages(*this, std::forward<ARGS>(args)...); + } + + template <typename... ARGS> + bool checkMessages(ARGS&&... args) + { + return mMessageSystem->checkMessages(*this, std::forward<ARGS>(args)...); + } + + template <typename... ARGS> + void processAcks(ARGS&&... args) + { + return mMessageSystem->processAcks(*this, std::forward<ARGS>(args)...); + } + +private: + LLMessageSystem* mMessageSystem; +}; + +// 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, + const LLUseCircuitCodeResponder* responder, + bool failure_is_fatal, + const F32 circuit_heartbeat_interval, + const F32 circuit_timeout); + +void end_messaging_system(bool print_summary = true); + +void null_message_callback(LLMessageSystem *msg, void **data); + +// +// Inlines +// + +#if !defined( LL_BIG_ENDIAN ) && !defined( LL_LITTLE_ENDIAN ) +#error Unknown endianness for htolememcpy. Did you miss a common include? +#endif + +static inline void *htolememcpy(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) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#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) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#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) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#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) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#ifdef LL_BIG_ENDIAN + htolememcpy(s + 8, ct + 8, MVT_F32, 4); + htolememcpy(s + 4, ct + 4, MVT_F32, 4); + return(htolememcpy(s, ct, MVT_F32, 4)); +#else + return(memcpy(s,ct,n)); /* Flawfinder: ignore */ +#endif + + case MVT_LLVector3d: + if (n != 24) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#ifdef LL_BIG_ENDIAN + htolememcpy(s + 16, ct + 16, MVT_F64, 8); + htolememcpy(s + 8, ct + 8, MVT_F64, 8); + return(htolememcpy(s, ct, MVT_F64, 8)); +#else + return(memcpy(s,ct,n)); /* Flawfinder: ignore */ +#endif + + case MVT_LLVector4: + if (n != 16) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#ifdef LL_BIG_ENDIAN + htolememcpy(s + 12, ct + 12, MVT_F32, 4); + htolememcpy(s + 8, ct + 8, MVT_F32, 4); + htolememcpy(s + 4, ct + 4, MVT_F32, 4); + return(htolememcpy(s, ct, MVT_F32, 4)); +#else + return(memcpy(s,ct,n)); /* Flawfinder: ignore */ +#endif + + case MVT_U16Vec3: + if (n != 6) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#ifdef LL_BIG_ENDIAN + htolememcpy(s + 4, ct + 4, MVT_U16, 2); + htolememcpy(s + 2, ct + 2, MVT_U16, 2); + return(htolememcpy(s, ct, MVT_U16, 2)); +#else + return(memcpy(s,ct,n)); /* Flawfinder: ignore */ +#endif + + case MVT_U16Quat: + if (n != 8) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#ifdef LL_BIG_ENDIAN + htolememcpy(s + 6, ct + 6, MVT_U16, 2); + htolememcpy(s + 4, ct + 4, MVT_U16, 2); + htolememcpy(s + 2, ct + 2, MVT_U16, 2); + return(htolememcpy(s, ct, MVT_U16, 2)); +#else + return(memcpy(s,ct,n)); /* Flawfinder: ignore */ +#endif + + case MVT_S16Array: + if (n % 2) + { + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; + } +#ifdef LL_BIG_ENDIAN + length = n % 2; + for (i = 1; i < length; i++) + { + htolememcpy(s + i*2, ct + i*2, MVT_S16, 2); + } + return(htolememcpy(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(htolememcpy(s,ct,type, n)); +} + +inline const LLHost& LLMessageSystem::getReceivingInterface() const {return mLastReceivingIF;} + +inline U32 LLMessageSystem::getSenderIP() const +{ + return mLastSender.getAddress(); +} + +inline U32 LLMessageSystem::getSenderPort() const +{ + return mLastSender.getPort(); +} + + +//----------------------------------------------------------------------------- +// Transmission aliases +//----------------------------------------------------------------------------- + +inline S32 LLMessageSystem::sendMessage(const U32 circuit) +{ + return sendMessage(findHost(circuit)); +} + +#endif diff --git a/indra/llmessage/message_string_table.cpp b/indra/llmessage/message_string_table.cpp index facc8f6cae..6d2157eec9 100644 --- a/indra/llmessage/message_string_table.cpp +++ b/indra/llmessage/message_string_table.cpp @@ -1,91 +1,91 @@ -/**
- * @file message_string_table.cpp
- * @brief static string table for message template
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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::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); /* Flawfinder: ignore */
- mString[hash_value][MESSAGE_MAX_STRINGS_LENGTH - 1] = 0;
- mEmpty[hash_value] = false;
- mUsed++;
- if (mUsed >= MESSAGE_NUMBER_OF_HASH_BUCKETS - 1)
- {
- U32 i;
- LL_INFOS() << "Dumping string table before crashing on HashTable full!" << LL_ENDL;
- for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
- {
- LL_INFOS() << "Entry #" << i << ": " << mString[i] << LL_ENDL;
- }
- }
- return mString[hash_value];
-}
-
+/** + * @file message_string_table.cpp + * @brief static string table for message template + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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::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); /* Flawfinder: ignore */ + mString[hash_value][MESSAGE_MAX_STRINGS_LENGTH - 1] = 0; + mEmpty[hash_value] = false; + mUsed++; + if (mUsed >= MESSAGE_NUMBER_OF_HASH_BUCKETS - 1) + { + U32 i; + LL_INFOS() << "Dumping string table before crashing on HashTable full!" << LL_ENDL; + for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++) + { + LL_INFOS() << "Entry #" << i << ": " << mString[i] << LL_ENDL; + } + } + return mString[hash_value]; +} + diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp index 833380632c..1c49f9be36 100644 --- a/indra/llmessage/net.cpp +++ b/indra/llmessage/net.cpp @@ -1,663 +1,663 @@ -/**
- * @file net.cpp
- * @brief Cross-platform routines for sending and receiving packets.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-//#include "net.h"
-
-// system library includes
-#include <stdexcept>
-
-#if LL_WINDOWS
-#include "llwin32headerslean.h"
-#else
- #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 "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
-
-static U32 gsnReceivingIFAddr = INVALID_HOST_IP_ADDRESS; // Address to which datagram was sent
-
-const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1";
-const char* BROADCAST_ADDRESS_STRING = "255.255.255.255";
-
-#if LL_DARWIN
- // macOS 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);
-}
-
-LLHost get_receiving_interface()
-{
- return LLHost(gsnReceivingIFAddr, INVALID_PORT);
-}
-
-U32 get_receiving_interface_ip(void)
-{
- return gsnReceivingIFAddr;
-}
-
-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); /*Flawfinder: ignore*/
- return ip_string;
- }
- else
- {
- return NULL;
- }
-}
-
-
-// Wrapper for inet_addr()
-U32 ip_string_to_u32(const char* ip_string)
-{
- // *NOTE: Windows doesn't support inet_aton(), so we are using
- // inet_addr(). Unfortunately, INADDR_NONE == INADDR_BROADCAST, so
- // we have to check whether the input is a broadcast address before
- // deciding that @ip_string is invalid.
- //
- // Also, our definition of INVALID_HOST_IP_ADDRESS doesn't allow us to
- // use wildcard addresses. -Ambroff
- U32 ip = inet_addr(ip_string);
- if (ip == INADDR_NONE
- && strncmp(ip_string, BROADCAST_ADDRESS_STRING, MAXADDRSTR) != 0)
- {
- LL_WARNS() << "ip_string_to_u32() failed, Error: Invalid IP string '" << ip_string << "'" << LL_ENDL;
- return INVALID_HOST_IP_ADDRESS;
- }
- return ip;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// 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();
- LL_WARNS("AppInit") << "Windows Sockets initialization failed, err " << err << LL_ENDL;
- return 1;
- }
-
- // Get a datagram socket
- hSocket = (int)socket(AF_INET, SOCK_DGRAM, 0);
- if (hSocket == INVALID_SOCKET)
- {
- S32 err = WSAGetLastError();
- WSACleanup();
- LL_WARNS("AppInit") << "socket() failed, err " << err << LL_ENDL;
- 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;
- LL_DEBUGS("AppInit") << "attempting to connect on port " << attempt_port << LL_ENDL;
- 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);
- LL_DEBUGS("AppInit") << "trying port " << attempt_port << LL_ENDL;
- nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
-
- if (!(nRet == SOCKET_ERROR &&
- WSAGetLastError() == WSAEADDRINUSE))
- {
- break;
- }
- }
-
- if (nRet == SOCKET_ERROR)
- {
- LL_WARNS("AppInit") << "startNet() : Couldn't find available network port." << LL_ENDL;
- // Fail gracefully here in release
- return 3;
- }
- }
- else
- // Some other socket error
- {
- LL_WARNS("AppInit") << llformat("bind() port: %d failed, Err: %d\n", nPort, WSAGetLastError()) << LL_ENDL;
- // Fail gracefully in release.
- return 4;
- }
- }
-
- sockaddr_in socket_address;
- S32 socket_address_size = sizeof(socket_address);
- getsockname(hSocket, (SOCKADDR*) &socket_address, &socket_address_size);
- attempt_port = ntohs(socket_address.sin_port);
-
- LL_INFOS("AppInit") << "connected on port " << attempt_port << LL_ENDL;
- 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)
- {
- LL_INFOS("AppInit") << "Can't set receive buffer size!" << LL_ENDL;
- }
-
- nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
- if (nRet)
- {
- LL_INFOS("AppInit") << "Can't set send buffer size!" << LL_ENDL;
- }
-
- getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
- getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
-
- LL_DEBUGS("AppInit") << "startNet - receive buffer size : " << rec_size << LL_ENDL;
- LL_DEBUGS("AppInit") << "startNet - send buffer size : " << snd_size << LL_ENDL;
-
- // Setup a destination address
- stDstAddr.sin_family = AF_INET;
- stDstAddr.sin_addr.s_addr = INVALID_HOST_IP_ADDRESS;
- stDstAddr.sin_port = htons(nPort);
-
- socket_out = hSocket;
- return 0;
-}
-
-void end_net(S32& socket_out)
-{
- if (socket_out >= 0)
- {
- shutdown(socket_out, SD_BOTH);
- closesocket(socket_out);
- }
- 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;
- LL_INFOS() << "receivePacket() failed, Error: " << WSAGetLastError() << LL_ENDL;
- }
-
- 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;
- }
- LL_INFOS() << "sendto() failed to " << u32_to_ip_string(recipient) << ":" << nPort
- << ", Error " << last_error << LL_ENDL;
- }
- }
- } 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)
- {
- LL_WARNS() << "socket() failed" << LL_ENDL;
- return 1;
- }
-
- if (NET_USE_OS_ASSIGNED_PORT == nPort)
- {
- // Although bind is not required it will tell us which port we were
- // assigned to.
- stLclAddr.sin_family = AF_INET;
- stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- stLclAddr.sin_port = htons(0);
- LL_INFOS() << "attempting to connect on OS assigned port" << LL_ENDL;
- nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
- if (nRet < 0)
- {
- LL_WARNS() << "Failed to bind on an OS assigned port error: "
- << nRet << LL_ENDL;
- }
- else
- {
- sockaddr_in socket_info;
- socklen_t len = sizeof(sockaddr_in);
- int err = getsockname(hSocket, (sockaddr*)&socket_info, &len);
- LL_INFOS() << "Get socket returned: " << err << " length " << len << LL_ENDL;
- nPort = ntohs(socket_info.sin_port);
- LL_INFOS() << "Assigned port: " << nPort << LL_ENDL;
-
- }
- }
- 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;
- LL_INFOS() << "attempting to connect on port " << attempt_port << LL_ENDL;
-
- 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);
- LL_INFOS() << "trying port " << attempt_port << LL_ENDL;
- nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
- if (!((nRet < 0) && (errno == EADDRINUSE)))
- {
- break;
- }
- }
- if (nRet < 0)
- {
- LL_WARNS() << "startNet() : Couldn't find available network port." << LL_ENDL;
- // Fail gracefully in release.
- return 3;
- }
- }
- // Some other socket error
- else
- {
- LL_WARNS() << llformat ("bind() port: %d failed, Err: %s\n", nPort, strerror(errno)) << LL_ENDL;
- // Fail gracefully in release.
- return 4;
- }
- }
- LL_INFOS() << "connected on port " << attempt_port << LL_ENDL;
- 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)
- {
- LL_INFOS() << "Can't set receive size!" << LL_ENDL;
- }
- nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
- if (nRet)
- {
- LL_INFOS() << "Can't set send size!" << LL_ENDL;
- }
- getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
- getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
-
- LL_INFOS() << "startNet - receive buffer size : " << rec_size << LL_ENDL;
- LL_INFOS() << "startNet - send buffer size : " << snd_size << LL_ENDL;
-
-#if LL_LINUX
- // Turn on recipient address tracking
- {
- int use_pktinfo = 1;
- if( setsockopt( hSocket, SOL_IP, IP_PKTINFO, &use_pktinfo, sizeof(use_pktinfo) ) == -1 )
- {
- LL_WARNS() << "No IP_PKTINFO available" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "IP_PKKTINFO enabled" << LL_ENDL;
- }
- }
-#endif
-
- // Setup a destination address
- char achMCAddr[MAXADDRSTR] = "127.0.0.1"; /* Flawfinder: ignore */
- stDstAddr.sin_family = AF_INET;
- stDstAddr.sin_addr.s_addr = ip_string_to_u32(achMCAddr);
- stDstAddr.sin_port = htons(nPort);
-
- socket_out = hSocket;
- return 0;
-}
-
-void end_net(S32& socket_out)
-{
- if (socket_out >= 0)
- {
- close(socket_out);
- }
-}
-
-#if LL_LINUX
-static int recvfrom_destip( int socket, void *buf, int len, struct sockaddr *from, socklen_t *fromlen, U32 *dstip )
-{
- int size;
- struct iovec iov[1];
- char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
- struct cmsghdr *cmsgptr;
- struct msghdr msg = {0};
-
- iov[0].iov_base = buf;
- iov[0].iov_len = len;
-
- memset(&msg, 0, sizeof msg);
- msg.msg_name = from;
- msg.msg_namelen = *fromlen;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = &cmsg;
- msg.msg_controllen = sizeof(cmsg);
-
- size = recvmsg(socket, &msg, 0);
-
- if (size == -1)
- {
- return -1;
- }
-
- for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR( &msg, cmsgptr))
- {
- if( cmsgptr->cmsg_level == SOL_IP && cmsgptr->cmsg_type == IP_PKTINFO )
- {
- in_pktinfo *pktinfo = (in_pktinfo *)CMSG_DATA(cmsgptr);
- if( pktinfo )
- {
- // Two choices. routed and specified. ipi_addr is routed, ipi_spec_dst is
- // routed. We should stay with specified until we go to multiple
- // interfaces
- *dstip = pktinfo->ipi_spec_dst.s_addr;
- }
- }
- }
-
- return size;
-}
-#endif
-
-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);
-
- gsnReceivingIFAddr = INVALID_HOST_IP_ADDRESS;
-
-#if LL_LINUX
- nRet = recvfrom_destip(hSocket, receiveBuffer, NET_BUFFER_SIZE, (struct sockaddr*)&stSrcAddr, &addr_size, &gsnReceivingIFAddr);
-#else
- int recv_flags = 0;
- nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, recv_flags, (struct sockaddr*)&stSrcAddr, &addr_size);
-#endif
-
- if (nRet == -1)
- {
- // To maintain consistency with the Windows implementation, return a zero for size on error.
- return 0;
- }
-
- // Uncomment for testing if/when implementing for Mac or Windows:
- // LL_INFOS() << "Received datagram to in addr " << u32_to_ip_string(get_receiving_interface_ip()) << LL_ENDL;
-
- 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
- LL_INFOS() << "sendto() reported buffer full, resending (attempt " << send_attempts << ")" << LL_ENDL;
- LL_INFOS() << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << LL_ENDL;
- resend = true;
- }
- else if (errno == ECONNREFUSED)
- {
- // response to ICMP connection refused message on earlier send
- LL_INFOS() << "sendto() reported connection refused, resending (attempt " << send_attempts << ")" << LL_ENDL;
- LL_INFOS() << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << LL_ENDL;
- resend = true;
- }
- else
- {
- // some other error
- LL_INFOS() << "sendto() failed: " << errno << ", " << strerror(errno) << LL_ENDL;
- LL_INFOS() << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << LL_ENDL;
- resend = false;
- }
- }
- }
- while (resend && send_attempts < 3);
-
- if (send_attempts >= 3)
- {
- LL_INFOS() << "sendPacket() bailed out of send!" << LL_ENDL;
- return false;
- }
-
- return success;
-}
-
-#endif
-
-//EOF
+/** + * @file net.cpp + * @brief Cross-platform routines for sending and receiving packets. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +//#include "net.h" + +// system library includes +#include <stdexcept> + +#if LL_WINDOWS +#include "llwin32headerslean.h" +#else + #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 "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 + +static U32 gsnReceivingIFAddr = INVALID_HOST_IP_ADDRESS; // Address to which datagram was sent + +const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1"; +const char* BROADCAST_ADDRESS_STRING = "255.255.255.255"; + +#if LL_DARWIN + // macOS 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); +} + +LLHost get_receiving_interface() +{ + return LLHost(gsnReceivingIFAddr, INVALID_PORT); +} + +U32 get_receiving_interface_ip(void) +{ + return gsnReceivingIFAddr; +} + +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); /*Flawfinder: ignore*/ + return ip_string; + } + else + { + return NULL; + } +} + + +// Wrapper for inet_addr() +U32 ip_string_to_u32(const char* ip_string) +{ + // *NOTE: Windows doesn't support inet_aton(), so we are using + // inet_addr(). Unfortunately, INADDR_NONE == INADDR_BROADCAST, so + // we have to check whether the input is a broadcast address before + // deciding that @ip_string is invalid. + // + // Also, our definition of INVALID_HOST_IP_ADDRESS doesn't allow us to + // use wildcard addresses. -Ambroff + U32 ip = inet_addr(ip_string); + if (ip == INADDR_NONE + && strncmp(ip_string, BROADCAST_ADDRESS_STRING, MAXADDRSTR) != 0) + { + LL_WARNS() << "ip_string_to_u32() failed, Error: Invalid IP string '" << ip_string << "'" << LL_ENDL; + return INVALID_HOST_IP_ADDRESS; + } + return ip; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// 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(); + LL_WARNS("AppInit") << "Windows Sockets initialization failed, err " << err << LL_ENDL; + return 1; + } + + // Get a datagram socket + hSocket = (int)socket(AF_INET, SOCK_DGRAM, 0); + if (hSocket == INVALID_SOCKET) + { + S32 err = WSAGetLastError(); + WSACleanup(); + LL_WARNS("AppInit") << "socket() failed, err " << err << LL_ENDL; + 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; + LL_DEBUGS("AppInit") << "attempting to connect on port " << attempt_port << LL_ENDL; + 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); + LL_DEBUGS("AppInit") << "trying port " << attempt_port << LL_ENDL; + nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); + + if (!(nRet == SOCKET_ERROR && + WSAGetLastError() == WSAEADDRINUSE)) + { + break; + } + } + + if (nRet == SOCKET_ERROR) + { + LL_WARNS("AppInit") << "startNet() : Couldn't find available network port." << LL_ENDL; + // Fail gracefully here in release + return 3; + } + } + else + // Some other socket error + { + LL_WARNS("AppInit") << llformat("bind() port: %d failed, Err: %d\n", nPort, WSAGetLastError()) << LL_ENDL; + // Fail gracefully in release. + return 4; + } + } + + sockaddr_in socket_address; + S32 socket_address_size = sizeof(socket_address); + getsockname(hSocket, (SOCKADDR*) &socket_address, &socket_address_size); + attempt_port = ntohs(socket_address.sin_port); + + LL_INFOS("AppInit") << "connected on port " << attempt_port << LL_ENDL; + 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) + { + LL_INFOS("AppInit") << "Can't set receive buffer size!" << LL_ENDL; + } + + nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size); + if (nRet) + { + LL_INFOS("AppInit") << "Can't set send buffer size!" << LL_ENDL; + } + + getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size); + getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size); + + LL_DEBUGS("AppInit") << "startNet - receive buffer size : " << rec_size << LL_ENDL; + LL_DEBUGS("AppInit") << "startNet - send buffer size : " << snd_size << LL_ENDL; + + // Setup a destination address + stDstAddr.sin_family = AF_INET; + stDstAddr.sin_addr.s_addr = INVALID_HOST_IP_ADDRESS; + stDstAddr.sin_port = htons(nPort); + + socket_out = hSocket; + return 0; +} + +void end_net(S32& socket_out) +{ + if (socket_out >= 0) + { + shutdown(socket_out, SD_BOTH); + closesocket(socket_out); + } + 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; + LL_INFOS() << "receivePacket() failed, Error: " << WSAGetLastError() << LL_ENDL; + } + + 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; + } + LL_INFOS() << "sendto() failed to " << u32_to_ip_string(recipient) << ":" << nPort + << ", Error " << last_error << LL_ENDL; + } + } + } 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) + { + LL_WARNS() << "socket() failed" << LL_ENDL; + return 1; + } + + if (NET_USE_OS_ASSIGNED_PORT == nPort) + { + // Although bind is not required it will tell us which port we were + // assigned to. + stLclAddr.sin_family = AF_INET; + stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY); + stLclAddr.sin_port = htons(0); + LL_INFOS() << "attempting to connect on OS assigned port" << LL_ENDL; + nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); + if (nRet < 0) + { + LL_WARNS() << "Failed to bind on an OS assigned port error: " + << nRet << LL_ENDL; + } + else + { + sockaddr_in socket_info; + socklen_t len = sizeof(sockaddr_in); + int err = getsockname(hSocket, (sockaddr*)&socket_info, &len); + LL_INFOS() << "Get socket returned: " << err << " length " << len << LL_ENDL; + nPort = ntohs(socket_info.sin_port); + LL_INFOS() << "Assigned port: " << nPort << LL_ENDL; + + } + } + 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; + LL_INFOS() << "attempting to connect on port " << attempt_port << LL_ENDL; + + 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); + LL_INFOS() << "trying port " << attempt_port << LL_ENDL; + nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); + if (!((nRet < 0) && (errno == EADDRINUSE))) + { + break; + } + } + if (nRet < 0) + { + LL_WARNS() << "startNet() : Couldn't find available network port." << LL_ENDL; + // Fail gracefully in release. + return 3; + } + } + // Some other socket error + else + { + LL_WARNS() << llformat ("bind() port: %d failed, Err: %s\n", nPort, strerror(errno)) << LL_ENDL; + // Fail gracefully in release. + return 4; + } + } + LL_INFOS() << "connected on port " << attempt_port << LL_ENDL; + 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) + { + LL_INFOS() << "Can't set receive size!" << LL_ENDL; + } + nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size); + if (nRet) + { + LL_INFOS() << "Can't set send size!" << LL_ENDL; + } + getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size); + getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size); + + LL_INFOS() << "startNet - receive buffer size : " << rec_size << LL_ENDL; + LL_INFOS() << "startNet - send buffer size : " << snd_size << LL_ENDL; + +#if LL_LINUX + // Turn on recipient address tracking + { + int use_pktinfo = 1; + if( setsockopt( hSocket, SOL_IP, IP_PKTINFO, &use_pktinfo, sizeof(use_pktinfo) ) == -1 ) + { + LL_WARNS() << "No IP_PKTINFO available" << LL_ENDL; + } + else + { + LL_INFOS() << "IP_PKKTINFO enabled" << LL_ENDL; + } + } +#endif + + // Setup a destination address + char achMCAddr[MAXADDRSTR] = "127.0.0.1"; /* Flawfinder: ignore */ + stDstAddr.sin_family = AF_INET; + stDstAddr.sin_addr.s_addr = ip_string_to_u32(achMCAddr); + stDstAddr.sin_port = htons(nPort); + + socket_out = hSocket; + return 0; +} + +void end_net(S32& socket_out) +{ + if (socket_out >= 0) + { + close(socket_out); + } +} + +#if LL_LINUX +static int recvfrom_destip( int socket, void *buf, int len, struct sockaddr *from, socklen_t *fromlen, U32 *dstip ) +{ + int size; + struct iovec iov[1]; + char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; + struct cmsghdr *cmsgptr; + struct msghdr msg = {0}; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + + memset(&msg, 0, sizeof msg); + msg.msg_name = from; + msg.msg_namelen = *fromlen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &cmsg; + msg.msg_controllen = sizeof(cmsg); + + size = recvmsg(socket, &msg, 0); + + if (size == -1) + { + return -1; + } + + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR( &msg, cmsgptr)) + { + if( cmsgptr->cmsg_level == SOL_IP && cmsgptr->cmsg_type == IP_PKTINFO ) + { + in_pktinfo *pktinfo = (in_pktinfo *)CMSG_DATA(cmsgptr); + if( pktinfo ) + { + // Two choices. routed and specified. ipi_addr is routed, ipi_spec_dst is + // routed. We should stay with specified until we go to multiple + // interfaces + *dstip = pktinfo->ipi_spec_dst.s_addr; + } + } + } + + return size; +} +#endif + +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); + + gsnReceivingIFAddr = INVALID_HOST_IP_ADDRESS; + +#if LL_LINUX + nRet = recvfrom_destip(hSocket, receiveBuffer, NET_BUFFER_SIZE, (struct sockaddr*)&stSrcAddr, &addr_size, &gsnReceivingIFAddr); +#else + int recv_flags = 0; + nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, recv_flags, (struct sockaddr*)&stSrcAddr, &addr_size); +#endif + + if (nRet == -1) + { + // To maintain consistency with the Windows implementation, return a zero for size on error. + return 0; + } + + // Uncomment for testing if/when implementing for Mac or Windows: + // LL_INFOS() << "Received datagram to in addr " << u32_to_ip_string(get_receiving_interface_ip()) << LL_ENDL; + + 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 + LL_INFOS() << "sendto() reported buffer full, resending (attempt " << send_attempts << ")" << LL_ENDL; + LL_INFOS() << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << LL_ENDL; + resend = true; + } + else if (errno == ECONNREFUSED) + { + // response to ICMP connection refused message on earlier send + LL_INFOS() << "sendto() reported connection refused, resending (attempt " << send_attempts << ")" << LL_ENDL; + LL_INFOS() << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << LL_ENDL; + resend = true; + } + else + { + // some other error + LL_INFOS() << "sendto() failed: " << errno << ", " << strerror(errno) << LL_ENDL; + LL_INFOS() << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << LL_ENDL; + resend = false; + } + } + } + while (resend && send_attempts < 3); + + if (send_attempts >= 3) + { + LL_INFOS() << "sendPacket() bailed out of send!" << LL_ENDL; + return false; + } + + return success; +} + +#endif + +//EOF diff --git a/indra/llmessage/net.h b/indra/llmessage/net.h index b6040b0a35..c41f3bb89f 100644 --- a/indra/llmessage/net.h +++ b/indra/llmessage/net.h @@ -1,74 +1,74 @@ -/**
- * @file net.h
- * @brief Cross platform UDP network code.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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(S32& socket_out);
-
-// 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);
-LLHost get_receiving_interface();
-U32 get_receiving_interface_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;
-extern const char* BROADCAST_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;
-
-// For automatic port discovery when running multiple viewers on one host
-const U32 PORT_DISCOVERY_RANGE_MIN = 13000;
-const U32 PORT_DISCOVERY_RANGE_MAX = PORT_DISCOVERY_RANGE_MIN + 50;
-
-#endif
+/** + * @file net.h + * @brief Cross platform UDP network code. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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(S32& socket_out); + +// 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); +LLHost get_receiving_interface(); +U32 get_receiving_interface_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; +extern const char* BROADCAST_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; + +// For automatic port discovery when running multiple viewers on one host +const U32 PORT_DISCOVERY_RANGE_MIN = 13000; +const U32 PORT_DISCOVERY_RANGE_MAX = PORT_DISCOVERY_RANGE_MIN + 50; + +#endif diff --git a/indra/llmessage/partsyspacket.cpp b/indra/llmessage/partsyspacket.cpp index f0af91e7a3..6113adbfcf 100644 --- a/indra/llmessage/partsyspacket.cpp +++ b/indra/llmessage/partsyspacket.cpp @@ -1,1297 +1,1297 @@ -/**
- * @file partsyspacket.cpp
- * @brief Object for packing particle system initialization parameters
- * before sending them over the network.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "partsyspacket.h"
-#include "indra_constants.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';
- }
-
- mNumBytes = 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); /* Flawfinder: ignore */
- mNumBytes = bytesUsed;
- return true;
- }
- else
- {
- LL_ERRS() << "NULL input data or number of bytes exceed mData size" << LL_ENDL;
- 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;
-}
-
-
+/** + * @file partsyspacket.cpp + * @brief Object for packing particle system initialization parameters + * before sending them over the network. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "partsyspacket.h" +#include "indra_constants.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'; + } + + mNumBytes = 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); /* Flawfinder: ignore */ + mNumBytes = bytesUsed; + return true; + } + else + { + LL_ERRS() << "NULL input data or number of bytes exceed mData size" << LL_ENDL; + 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 index 09d0ef76d5..23307f3409 100644 --- a/indra/llmessage/partsyspacket.h +++ b/indra/llmessage/partsyspacket.h @@ -1,261 +1,261 @@ -/**
- * @file partsyspacket.h
- * @brief Object for packing particle system initialization parameters
- * before sending them over the network
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_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
-
+/** + * @file partsyspacket.h + * @brief Object for packing particle system initialization parameters + * before sending them over the network + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_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 index 5c9dc5ad87..489b6ce6a6 100644 --- a/indra/llmessage/patch_code.cpp +++ b/indra/llmessage/patch_code.cpp @@ -1,408 +1,408 @@ -/**
- * @file patch_code.cpp
- * @brief Encode patch DCT data into bitcode.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llmath.h"
-//#include "vmath.h"
-#include "v3math.h"
-#include "patch_dct.h"
-#include "patch_code.h"
-#include "llbitpack.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))
- {
- LL_ERRS() << "Bits needed per word in code_patch_header out of legal range. Adjust compression quatization." << LL_ENDL;
- }
-
- 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))
- {
- LL_ERRS() << "Bad postquant in code_patch!" << LL_ENDL;
- }
-
- 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
-}
-
+/** + * @file patch_code.cpp + * @brief Encode patch DCT data into bitcode. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llmath.h" +//#include "vmath.h" +#include "v3math.h" +#include "patch_dct.h" +#include "patch_code.h" +#include "llbitpack.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)) + { + LL_ERRS() << "Bits needed per word in code_patch_header out of legal range. Adjust compression quatization." << LL_ENDL; + } + + 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)) + { + LL_ERRS() << "Bad postquant in code_patch!" << LL_ENDL; + } + + 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_dct.cpp b/indra/llmessage/patch_dct.cpp index 10b3e5dd77..728fe84537 100644 --- a/indra/llmessage/patch_dct.cpp +++ b/indra/llmessage/patch_dct.cpp @@ -1,769 +1,769 @@ -/**
- * @file patch_dct.cpp
- * @brief DCT patch.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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;
-}
+/** + * @file patch_dct.cpp + * @brief DCT patch. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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_idct.cpp b/indra/llmessage/patch_idct.cpp index c5320e22e3..5483cf98c0 100644 --- a/indra/llmessage/patch_idct.cpp +++ b/indra/llmessage/patch_idct.cpp @@ -1,684 +1,684 @@ -/**
- * @file patch_idct.cpp
- * @brief IDCT patch.
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "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;
- }
- }
-}
-
+/** + * @file patch_idct.cpp + * @brief IDCT patch. + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "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/tests/lltemplatemessagedispatcher_test.cpp b/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp index d0a34d720a..c5b852453f 100644 --- a/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp +++ b/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp @@ -1,160 +1,160 @@ -/**
- * @file lltrustedmessageservice_test.cpp
- * @brief LLTrustedMessageService unit tests
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "lltemplatemessagedispatcher.h"
-#include "lltut.h"
-
-#include "llhttpnode.h"
-#include "llhost.h"
-#include "message.h"
-#include "llsd.h"
-#include "llpounceable.h"
-
-#include "llhost.cpp" // Needed for copy operator
-#include "net.cpp" // Needed by LLHost.
-
-LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
-
-// sensor test doubles
-bool gClearRecvWasCalled = false;
-void LLMessageSystem::clearReceiveState(void)
-{
- gClearRecvWasCalled = true;
-}
-
-char gUdpDispatchedData[MAX_BUFFER_SIZE];
-bool gUdpDispatchWasCalled = false;
-bool LLTemplateMessageReader::readMessage(const U8* data,class LLHost const &)
-{
- gUdpDispatchWasCalled = true;
- strcpy(gUdpDispatchedData, reinterpret_cast<const char*>(data));
- return true;
-}
-
-bool gValidateMessage = false;
-bool LLTemplateMessageReader::validateMessage(const U8*, S32 buffer_size, LLHost const &sender, bool trusted)
-{
- return gValidateMessage;
-}
-
-LLHost host;
-const LLHost& LLMessageSystem::getSender() const
-{
- return host;
-}
-
-const char* gBinaryTemplateData = "BINARYTEMPLATEDATA";
-void fillVector(std::vector<U8>& vector_data, const char* data)
-{
- vector_data.resize(strlen(data) + 1);
- strcpy(reinterpret_cast<char*>(&vector_data[0]), data);
-}
-
-namespace tut
-{
- static LLTemplateMessageReader::message_template_number_map_t numberMap;
-
- struct LLTemplateMessageDispatcherData
- {
- LLTemplateMessageDispatcherData()
- {
- mMessageName = "MessageName";
- gUdpDispatchWasCalled = false;
- gClearRecvWasCalled = false;
- gValidateMessage = false;
- mMessage["body"]["binary-template-data"] = std::vector<U8>();
- }
-
- LLSD mMessage;
- LLHTTPNode::ResponsePtr mResponsePtr;
- std::string mMessageName;
- };
-
- typedef test_group<LLTemplateMessageDispatcherData> factory;
- typedef factory::object object;
-}
-
-namespace
-{
- tut::factory tf("LLTemplateMessageDispatcher");
-}
-
-namespace tut
-{
- // does an empty message stop processing?
- template<> template<>
- void object::test<1>()
- {
- LLTemplateMessageReader* pReader = NULL;
- LLTemplateMessageDispatcher t(*pReader);
- t.dispatch(mMessageName, mMessage, mResponsePtr);
- ensure(! gUdpDispatchWasCalled);
- ensure(! gClearRecvWasCalled);
- }
-
- // does the disaptch invoke the udp send method?
- template<> template<>
- void object::test<2>()
- {
- LLTemplateMessageReader* pReader = NULL;
- LLTemplateMessageDispatcher t(*pReader);
- gValidateMessage = true;
- std::vector<U8> vector_data;
- fillVector(vector_data, gBinaryTemplateData);
- mMessage["body"]["binary-template-data"] = vector_data;
- t.dispatch(mMessageName, mMessage, mResponsePtr);
- ensure("udp dispatch was called", gUdpDispatchWasCalled);
- }
-
- // what if the message wasn't valid? We would hope the message gets cleared!
- template<> template<>
- void object::test<3>()
- {
- LLTemplateMessageReader* pReader = NULL;
- LLTemplateMessageDispatcher t(*pReader);
- std::vector<U8> vector_data;
- fillVector(vector_data, gBinaryTemplateData);
- mMessage["body"]["binary-template-data"] = vector_data;
- gValidateMessage = false;
- t.dispatch(mMessageName, mMessage, mResponsePtr);
- ensure("clear received message was called", gClearRecvWasCalled);
- }
-
- // is the binary data passed through correctly?
- template<> template<>
- void object::test<4>()
- {
- LLTemplateMessageReader* pReader = NULL;
- LLTemplateMessageDispatcher t(*pReader);
- gValidateMessage = true;
- std::vector<U8> vector_data;
- fillVector(vector_data, gBinaryTemplateData);
- mMessage["body"]["binary-template-data"] = vector_data;
- t.dispatch(mMessageName, mMessage, mResponsePtr);
- ensure("data couriered correctly", strcmp(gBinaryTemplateData, gUdpDispatchedData) == 0);
- }
-}
-
+/** + * @file lltrustedmessageservice_test.cpp + * @brief LLTrustedMessageService unit tests + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "lltemplatemessagedispatcher.h" +#include "lltut.h" + +#include "llhttpnode.h" +#include "llhost.h" +#include "message.h" +#include "llsd.h" +#include "llpounceable.h" + +#include "llhost.cpp" // Needed for copy operator +#include "net.cpp" // Needed by LLHost. + +LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem; + +// sensor test doubles +bool gClearRecvWasCalled = false; +void LLMessageSystem::clearReceiveState(void) +{ + gClearRecvWasCalled = true; +} + +char gUdpDispatchedData[MAX_BUFFER_SIZE]; +bool gUdpDispatchWasCalled = false; +bool LLTemplateMessageReader::readMessage(const U8* data,class LLHost const &) +{ + gUdpDispatchWasCalled = true; + strcpy(gUdpDispatchedData, reinterpret_cast<const char*>(data)); + return true; +} + +bool gValidateMessage = false; +bool LLTemplateMessageReader::validateMessage(const U8*, S32 buffer_size, LLHost const &sender, bool trusted) +{ + return gValidateMessage; +} + +LLHost host; +const LLHost& LLMessageSystem::getSender() const +{ + return host; +} + +const char* gBinaryTemplateData = "BINARYTEMPLATEDATA"; +void fillVector(std::vector<U8>& vector_data, const char* data) +{ + vector_data.resize(strlen(data) + 1); + strcpy(reinterpret_cast<char*>(&vector_data[0]), data); +} + +namespace tut +{ + static LLTemplateMessageReader::message_template_number_map_t numberMap; + + struct LLTemplateMessageDispatcherData + { + LLTemplateMessageDispatcherData() + { + mMessageName = "MessageName"; + gUdpDispatchWasCalled = false; + gClearRecvWasCalled = false; + gValidateMessage = false; + mMessage["body"]["binary-template-data"] = std::vector<U8>(); + } + + LLSD mMessage; + LLHTTPNode::ResponsePtr mResponsePtr; + std::string mMessageName; + }; + + typedef test_group<LLTemplateMessageDispatcherData> factory; + typedef factory::object object; +} + +namespace +{ + tut::factory tf("LLTemplateMessageDispatcher"); +} + +namespace tut +{ + // does an empty message stop processing? + template<> template<> + void object::test<1>() + { + LLTemplateMessageReader* pReader = NULL; + LLTemplateMessageDispatcher t(*pReader); + t.dispatch(mMessageName, mMessage, mResponsePtr); + ensure(! gUdpDispatchWasCalled); + ensure(! gClearRecvWasCalled); + } + + // does the disaptch invoke the udp send method? + template<> template<> + void object::test<2>() + { + LLTemplateMessageReader* pReader = NULL; + LLTemplateMessageDispatcher t(*pReader); + gValidateMessage = true; + std::vector<U8> vector_data; + fillVector(vector_data, gBinaryTemplateData); + mMessage["body"]["binary-template-data"] = vector_data; + t.dispatch(mMessageName, mMessage, mResponsePtr); + ensure("udp dispatch was called", gUdpDispatchWasCalled); + } + + // what if the message wasn't valid? We would hope the message gets cleared! + template<> template<> + void object::test<3>() + { + LLTemplateMessageReader* pReader = NULL; + LLTemplateMessageDispatcher t(*pReader); + std::vector<U8> vector_data; + fillVector(vector_data, gBinaryTemplateData); + mMessage["body"]["binary-template-data"] = vector_data; + gValidateMessage = false; + t.dispatch(mMessageName, mMessage, mResponsePtr); + ensure("clear received message was called", gClearRecvWasCalled); + } + + // is the binary data passed through correctly? + template<> template<> + void object::test<4>() + { + LLTemplateMessageReader* pReader = NULL; + LLTemplateMessageDispatcher t(*pReader); + gValidateMessage = true; + std::vector<U8> vector_data; + fillVector(vector_data, gBinaryTemplateData); + mMessage["body"]["binary-template-data"] = vector_data; + t.dispatch(mMessageName, mMessage, mResponsePtr); + ensure("data couriered correctly", strcmp(gBinaryTemplateData, gUdpDispatchedData) == 0); + } +} + diff --git a/indra/llmessage/tests/llxfer_file_test.cpp b/indra/llmessage/tests/llxfer_file_test.cpp index 6a2e0566df..358659a5b9 100644 --- a/indra/llmessage/tests/llxfer_file_test.cpp +++ b/indra/llmessage/tests/llxfer_file_test.cpp @@ -1,58 +1,58 @@ -/**
- * @file llxfer_test.cpp
- * @author Moss
- * @date 2007-04-17
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "../llxfer_file.h"
-
-#include "../test/lltut.h"
-
-namespace tut
-{
- struct llxfer_data
- {
- };
- typedef test_group<llxfer_data> llxfer_test;
- typedef llxfer_test::object llxfer_object;
- tut::llxfer_test llxfer("LLXferFile");
-
- template<> template<>
- void llxfer_object::test<1>()
- {
- // test that we handle an oversized filename correctly.
- std::string oversized_filename;
- U32 i;
- for (i=0; i<LL_MAX_PATH*2; ++i) // create oversized filename
- {
- oversized_filename += 'X';
- }
-
- LLXfer_File xff(oversized_filename, false, 1);
- ensure("oversized local_filename nul-terminated",
- xff.getFileName().length() < LL_MAX_PATH);
- }
-}
+/** + * @file llxfer_test.cpp + * @author Moss + * @date 2007-04-17 + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "../llxfer_file.h" + +#include "../test/lltut.h" + +namespace tut +{ + struct llxfer_data + { + }; + typedef test_group<llxfer_data> llxfer_test; + typedef llxfer_test::object llxfer_object; + tut::llxfer_test llxfer("LLXferFile"); + + template<> template<> + void llxfer_object::test<1>() + { + // test that we handle an oversized filename correctly. + std::string oversized_filename; + U32 i; + for (i=0; i<LL_MAX_PATH*2; ++i) // create oversized filename + { + oversized_filename += 'X'; + } + + LLXfer_File xff(oversized_filename, false, 1); + ensure("oversized local_filename nul-terminated", + xff.getFileName().length() < LL_MAX_PATH); + } +} |