/** 
 * @file llpermissions.cpp
 * @author Phoenix
 * @brief Permissions for objects and inventory.
 *
 * $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 "llpermissions.h"

// library includes
#include "message.h"
#include "llsd.h"

///----------------------------------------------------------------------------
/// Class LLPermissions
///----------------------------------------------------------------------------

const LLPermissions LLPermissions::DEFAULT;

// No creator = created by system
LLPermissions::LLPermissions()
{
	init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null);
}


// Default to created by system
void LLPermissions::init(const LLUUID& creator, const LLUUID& owner, const LLUUID& last_owner, const LLUUID& group)
{
	mCreator	= creator;
	mOwner		= owner;
	mLastOwner	= last_owner;
	mGroup		= group;

	mMaskBase		= PERM_ALL;
	mMaskOwner		= PERM_ALL;
	mMaskEveryone	= PERM_ALL;
	mMaskGroup		= PERM_ALL;
	mMaskNextOwner = PERM_ALL;
	fixOwnership();
}


void LLPermissions::initMasks(PermissionMask base, PermissionMask owner,
							  PermissionMask everyone, PermissionMask group,
							  PermissionMask next)
{
	mMaskBase		= base;
	mMaskOwner		= owner;
	mMaskEveryone	= everyone;
	mMaskGroup		= group;
	mMaskNextOwner = next;
	fixFairUse();
	fix();
}

// ! BACKWARDS COMPATIBILITY ! Override masks for inventory types that
// no longer can have restricted permissions.  This takes care of previous
// version landmarks that could have had no copy/mod/transfer bits set.
void LLPermissions::initMasks(LLInventoryType::EType type)
{
	if (LLInventoryType::cannotRestrictPermissions(type))
	{
		initMasks(PERM_ALL, PERM_ALL, PERM_ALL, PERM_ALL, PERM_ALL);
	}
}

BOOL LLPermissions::getOwnership(LLUUID& owner_id, BOOL& is_group_owned) const
{
	if(mOwner.notNull())
	{
		owner_id = mOwner;
		is_group_owned = FALSE;
		return TRUE;
	}
	else if(mIsGroupOwned)
	{
		owner_id = mGroup;
		is_group_owned = TRUE;
		return TRUE;
	}
	return FALSE;
}

LLUUID LLPermissions::getSafeOwner() const
{
	if(mOwner.notNull())
	{
		return mOwner;
	}
	else if(mIsGroupOwned)
	{
		return mGroup;
	}
	else
	{
		LL_WARNS() << "LLPermissions::getSafeOwner() called with no valid owner!" << LL_ENDL;
		LLUUID unused_uuid;
		unused_uuid.generate();

		return unused_uuid;
	}
}

U32 LLPermissions::getCRC32() const
{
	U32 rv = mCreator.getCRC32();
	rv += mOwner.getCRC32();
	rv += mLastOwner.getCRC32();
	rv += mGroup.getCRC32();
	rv += mMaskBase + mMaskOwner + mMaskEveryone + mMaskGroup;
	return rv;
}

void LLPermissions::set(const LLPermissions& from)
{
	mCreator	= from.mCreator;
	mOwner		= from.mOwner;
	mLastOwner	= from.mLastOwner;
	mGroup		= from.mGroup;

	mMaskBase		= from.mMaskBase;
	mMaskOwner		= from.mMaskOwner;
	mMaskEveryone	= from.mMaskEveryone;
	mMaskGroup		= from.mMaskGroup;
	mMaskNextOwner = from.mMaskNextOwner;
	mIsGroupOwned = from.mIsGroupOwned;
}

// Fix hierarchy of permissions. 
void LLPermissions::fix()
{
	mMaskOwner &= mMaskBase;
	mMaskGroup &= mMaskOwner;
	// next owner uses base, since you may want to sell locked objects.
	mMaskNextOwner &= mMaskBase;
	mMaskEveryone &= mMaskOwner;
	mMaskEveryone &= ~PERM_MODIFY;
	if(!(mMaskBase & PERM_TRANSFER) && !mIsGroupOwned)
	{
		mMaskGroup &= ~PERM_COPY;
		mMaskEveryone &= ~PERM_COPY;
		// Do not set mask next owner to too restrictive because if we
		// rez an object, it may require an ownership transfer during
		// rez, which will note the overly restrictive perms, and then
		// fix them to allow fair use, which may be different than the
		// original intention.
	}
}

// Correct for fair use - you can never take away the right to move
// stuff you own, and you can never take away the right to transfer
// something you cannot otherwise copy.
void LLPermissions::fixFairUse()
{
	mMaskBase |= PERM_MOVE;
	if(!(mMaskBase & PERM_COPY))
	{
		mMaskBase |= PERM_TRANSFER;
	}
	// (mask next owner == PERM_NONE) iff mask base is no transfer
	if(mMaskNextOwner != PERM_NONE)
	{
		mMaskNextOwner |= PERM_MOVE;
	}
}

void LLPermissions::fixOwnership()
{
	if(mOwner.isNull() && mGroup.notNull())
	{
		mIsGroupOwned = true;
	}
	else
	{
		mIsGroupOwned = false;
	}
}

// Allow accumulation of permissions. Results in the tightest
// permissions possible. In the case of clashing UUIDs, it sets the ID
// to LLUUID::null.
void LLPermissions::accumulate(const LLPermissions& perm)
{
	if(perm.mCreator != mCreator)
	{
		mCreator = LLUUID::null;
	}
	if(perm.mOwner != mOwner)
	{
		mOwner = LLUUID::null;
	}
	if(perm.mLastOwner != mLastOwner)
	{
		mLastOwner = LLUUID::null;
	}
	if(perm.mGroup != mGroup)
	{
		mGroup = LLUUID::null;
	}

	mMaskBase &= perm.mMaskBase;
	mMaskOwner &= perm.mMaskOwner;
	mMaskGroup &= perm.mMaskGroup;
	mMaskEveryone &= perm.mMaskEveryone;
	mMaskNextOwner &= perm.mMaskNextOwner;
	fix();
}

// saves last owner, sets current owner, and sets the group.  note
// that this function has to more cleverly apply the fair use
// permissions.
BOOL LLPermissions::setOwnerAndGroup(
	const LLUUID& agent,
	const LLUUID& owner,
	const LLUUID& group,
	bool is_atomic)
{
	BOOL allowed = FALSE;

	if( agent.isNull() || mOwner.isNull()
	   || ((agent == mOwner) && ((owner == mOwner) || (mMaskOwner & PERM_TRANSFER)) ) )
	{
		// ...system can alway set owner
		// ...public objects can be claimed by anyone
		// ...otherwise, agent must own it and have transfer ability
		allowed = TRUE;
	}

	if (allowed)
	{
		if(mLastOwner.isNull() || (!mOwner.isNull() && (owner != mLastOwner)))
		{
			mLastOwner = mOwner;
		}
		if((mOwner != owner)
		   || (mOwner.isNull() && owner.isNull() && (mGroup != group)))
		{
			mMaskBase = mMaskNextOwner;
			mOwner = owner;
			// this is a selective use of fair use for atomic
			// permissions.
			if(is_atomic && !(mMaskBase & PERM_COPY))
			{
				mMaskBase |= PERM_TRANSFER;
			}
		}
		mGroup = group;
		fixOwnership();
		// if it's not atomic and we fix fair use, it blows away
		//objects as inventory items which have different permissions
		//than it's contents. :(
		// fixFairUse();
		mMaskBase |= PERM_MOVE;
		if(mMaskNextOwner != PERM_NONE) mMaskNextOwner |= PERM_MOVE;
		fix();
	}

	return allowed;
}

//Fix for DEV-33917, last owner isn't used much and has little impact on
//permissions so it's reasonably safe to do this, however, for now, 
//limiting the functionality of this routine to objects which are 
//group owned.
void LLPermissions::setLastOwner(const LLUUID& last_owner)
{
	if (isGroupOwned())
		mLastOwner = last_owner;
}

 
// only call this if you know what you're doing
// there are usually perm-bit consequences when the 
// ownerhsip changes
void LLPermissions::yesReallySetOwner(const LLUUID& owner, bool group_owned)
{
	mOwner = owner;
	mIsGroupOwned = group_owned;
}

BOOL LLPermissions::deedToGroup(const LLUUID& agent, const LLUUID& group)
{
	if(group.notNull() && (agent.isNull() || ((group == mGroup)
											  && (mMaskOwner & PERM_TRANSFER)
											  && (mMaskGroup & PERM_MOVE))))
	{
		if(mOwner.notNull())
		{
			mLastOwner = mOwner;
			mOwner.setNull();
		}
		mMaskBase = mMaskNextOwner;
		mMaskGroup = PERM_NONE;
		mGroup = group;
		mIsGroupOwned = true;
		fixFairUse();
		fix();
		return TRUE;
	}
	return FALSE;
}

BOOL LLPermissions::setBaseBits(const LLUUID& agent, BOOL set, PermissionMask bits)
{
	BOOL ownership = FALSE;
	if(agent.isNull())
	{
		// only the system is always allowed to change base bits
		ownership = TRUE;
	}

	if (ownership)
	{
		if (set)
		{
			mMaskBase |= bits;		// turn on bits
		}
		else
		{
			mMaskBase &= ~bits;		// turn off bits
		}
		fix();
	}

	return ownership;
}


// Note: If you attempt to set bits that the base bits doesn't allow,
// the function will succeed, but those bits will not be set.
BOOL LLPermissions::setOwnerBits(const LLUUID& agent, BOOL set, PermissionMask bits)
{
	BOOL ownership = FALSE;

	if(agent.isNull())
	{
		// ...system always allowed to change things
		ownership = TRUE;
	}
	else if (agent == mOwner)
	{
		// ...owner bits can only be set by owner
		ownership = TRUE;
	}

	// If we have correct ownership and 
	if (ownership)
	{
		if (set)
		{
			mMaskOwner |= bits;			// turn on bits
		}
		else
		{
			mMaskOwner &= ~bits;		// turn off bits
		}
		fix();
	}

	return (ownership);
}

BOOL LLPermissions::setGroupBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits)
{
	BOOL ownership = FALSE;
	if((agent.isNull()) || (agent == mOwner)
	   || ((group == mGroup) && (!mGroup.isNull())))
	{
		// The group bits can be set by the system, the owner, or a
		// group member.
		ownership = TRUE;
	}

	if (ownership)
	{
		if (set)
		{
			mMaskGroup |= bits;
		}
		else
		{
			mMaskGroup &= ~bits;
		}
		fix();
	}
	return ownership;
}


// Note: If you attempt to set bits that the creator or owner doesn't allow,
// the function will succeed, but those bits will not be set.
BOOL LLPermissions::setEveryoneBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits)
{
	BOOL ownership = FALSE;
	if((agent.isNull()) || (agent == mOwner)
	   || ((group == mGroup) && (!mGroup.isNull())))
	{
		// The everyone bits can be set by the system, the owner, or a
		// group member.
		ownership = TRUE;
	}
	if (ownership)
	{
		if (set)
		{
			mMaskEveryone |= bits;
		}
		else
		{
			mMaskEveryone &= ~bits;
		}

		// Fix hierarchy of permissions
		fix();
	}
	return ownership;
}

// Note: If you attempt to set bits that the creator or owner doesn't allow,
// the function will succeed, but those bits will not be set.
BOOL LLPermissions::setNextOwnerBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits)
{
	BOOL ownership = FALSE;
	if((agent.isNull()) || (agent == mOwner)
	   || ((group == mGroup) && (!mGroup.isNull())))
	{
		// The next owner bits can be set by the system, the owner, or
		// a group member.
		ownership = TRUE;
	}
	if (ownership)
	{
		if (set)
		{
			mMaskNextOwner |= bits;
		}
		else
		{
			mMaskNextOwner &= ~bits;
		}

		// Fix-up permissions
		if(!(mMaskNextOwner & PERM_COPY))
		{
			mMaskNextOwner |= PERM_TRANSFER;
		}
		fix();
	}
	return ownership;
}

bool LLPermissions::allowOperationBy(PermissionBit op, const LLUUID& requester, const LLUUID& group) const
{
	if(requester.isNull())
	{
		// ...system making request
		// ...not owned
		return TRUE;
	}
	else if (mIsGroupOwned && (mGroup == requester))
	{
		// group checking ownership permissions
		return (mMaskOwner & op);
	}
	else if (!mIsGroupOwned && (mOwner == requester))
	{
		// ...owner making request
		return (mMaskOwner & op);
	}
	else if(mGroup.notNull() && (mGroup == group))
	{
		// group member making request
		return ((mMaskGroup & op) || (mMaskEveryone & op));
	}
	return (mMaskEveryone & op);
}

//
// LLSD support for HTTP messages.
//
LLSD LLPermissions::packMessage() const
{
	LLSD result;
	result["creator-id"]	= mCreator;
	result["owner-id"]		= mOwner;
	result["group-id"]		= mGroup;

	result["base-mask"]		=	(S32)mMaskBase;
	result["owner-mask"]	=	(S32)mMaskOwner;
	result["group-mask"]	=	(S32)mMaskGroup;
	result["everyone-mask"]	=	(S32)mMaskEveryone;
	result["next-owner-mask"]=	(S32)mMaskNextOwner;
	result["group-owned"]	= (BOOL)mIsGroupOwned;
	return result;
}

//
// Messaging support
//
void LLPermissions::packMessage(LLMessageSystem* msg) const
{
	msg->addUUIDFast(_PREHASH_CreatorID, mCreator);
	msg->addUUIDFast(_PREHASH_OwnerID, mOwner);
	msg->addUUIDFast(_PREHASH_GroupID, mGroup);

	msg->addU32Fast(_PREHASH_BaseMask,		mMaskBase );
	msg->addU32Fast(_PREHASH_OwnerMask,	mMaskOwner );
	msg->addU32Fast(_PREHASH_GroupMask,	mMaskGroup );
	msg->addU32Fast(_PREHASH_EveryoneMask,	mMaskEveryone );
	msg->addU32Fast(_PREHASH_NextOwnerMask, mMaskNextOwner );
	msg->addBOOLFast(_PREHASH_GroupOwned, (BOOL)mIsGroupOwned);
}

void LLPermissions::unpackMessage(LLSD perms)
{
	mCreator	=	perms["creator-id"];
	mOwner	=	perms["owner-id"];
	mGroup	=	perms["group-id"];

	mMaskBase	=	(U32)perms["base-mask"].asInteger();
	mMaskOwner	=	(U32)perms["owner-mask"].asInteger();
	mMaskGroup	=	(U32)perms["group-mask"].asInteger();
	mMaskEveryone	=	(U32)perms["everyone-mask"].asInteger();
	mMaskNextOwner	=	(U32)perms["next-owner-mask"].asInteger();
	mIsGroupOwned	=	perms["group-owned"].asBoolean();
}

void LLPermissions::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num)
{
	msg->getUUIDFast(block, _PREHASH_CreatorID,	mCreator, block_num);
	msg->getUUIDFast(block, _PREHASH_OwnerID,	mOwner, block_num);
	msg->getUUIDFast(block, _PREHASH_GroupID,	mGroup, block_num);

	msg->getU32Fast(block, _PREHASH_BaseMask, mMaskBase, block_num );
	msg->getU32Fast(block, _PREHASH_OwnerMask, mMaskOwner, block_num );
	msg->getU32Fast(block, _PREHASH_GroupMask, mMaskGroup, block_num );
	msg->getU32Fast(block, _PREHASH_EveryoneMask, mMaskEveryone, block_num );
	msg->getU32Fast(block, _PREHASH_NextOwnerMask, mMaskNextOwner, block_num );
	BOOL tmp;
	msg->getBOOLFast(block, _PREHASH_GroupOwned, tmp, block_num);
	mIsGroupOwned = (bool)tmp;
}


BOOL LLPermissions::importLegacyStream(std::istream& input_stream)
{
	init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null);
	const S32 BUFSIZE = 16384;

	// *NOTE: Changing the buffer size will require changing the scanf
	// calls below.
	char buffer[BUFSIZE];	/* Flawfinder: ignore */
	char keyword[256];	/* Flawfinder: ignore */
	char valuestr[256];	/* Flawfinder: ignore */
	char uuid_str[256];	/* Flawfinder: ignore */
	U32 mask;

	keyword[0]  = '\0';
	valuestr[0] = '\0';

	while (input_stream.good())
	{
		input_stream.getline(buffer, BUFSIZE);
		sscanf( /* Flawfinder: ignore */
			buffer,
			" %255s %255s",
			keyword, valuestr);
		if (!strcmp("{", keyword))
		{
			continue;
		}
		if (!strcmp("}",keyword))
		{
			break;
		}
		else if (!strcmp("creator_mask", keyword))
		{
			// legacy support for "creator" masks
			sscanf(valuestr, "%x", &mask);
			mMaskBase = mask;
			fixFairUse();
		}
		else if (!strcmp("base_mask", keyword))
		{
			sscanf(valuestr, "%x", &mask);
			mMaskBase = mask;
			//fixFairUse();
		}
		else if (!strcmp("owner_mask", keyword))
		{
			sscanf(valuestr, "%x", &mask);
			mMaskOwner = mask;
		}
		else if (!strcmp("group_mask", keyword))
		{
			sscanf(valuestr, "%x", &mask);
			mMaskGroup = mask;
		}
		else if (!strcmp("everyone_mask", keyword))
		{
			sscanf(valuestr, "%x", &mask);
			mMaskEveryone = mask;
		}
		else if (!strcmp("next_owner_mask", keyword))
		{
			sscanf(valuestr, "%x", &mask);
			mMaskNextOwner = mask;
		}
		else if (!strcmp("creator_id", keyword))
		{
			sscanf(valuestr, "%255s", uuid_str);	/* Flawfinder: ignore */
			mCreator.set(uuid_str);
		}
		else if (!strcmp("owner_id", keyword))
		{
			sscanf(valuestr, "%255s", uuid_str);	/* Flawfinder: ignore */
			mOwner.set(uuid_str);
		}
		else if (!strcmp("last_owner_id", keyword))
		{
			sscanf(valuestr, "%255s", uuid_str);	/* Flawfinder: ignore */
			mLastOwner.set(uuid_str);
		}
		else if (!strcmp("group_id", keyword))
		{
			sscanf(valuestr, "%255s", uuid_str);	/* Flawfinder: ignore */
			mGroup.set(uuid_str);
		}
		else if (!strcmp("group_owned", keyword))
		{
			sscanf(valuestr, "%d", &mask);
			if(mask) mIsGroupOwned = true;
			else mIsGroupOwned = false;
		}
		else
		{
			LL_INFOS() << "unknown keyword " << keyword << " in permissions import" << LL_ENDL;
		}
	}
	fix();
	return TRUE;
}


BOOL LLPermissions::exportLegacyStream(std::ostream& output_stream) const
{
	std::string uuid_str;

	output_stream <<  "\tpermissions 0\n";
	output_stream <<  "\t{\n";

	std::string buffer;
	buffer = llformat( "\t\tbase_mask\t%08x\n",		mMaskBase);
	output_stream << buffer;
	buffer = llformat( "\t\towner_mask\t%08x\n",		mMaskOwner);
	output_stream << buffer;
	buffer = llformat( "\t\tgroup_mask\t%08x\n",		mMaskGroup);
	output_stream << buffer;
	buffer = llformat( "\t\teveryone_mask\t%08x\n",	mMaskEveryone);
	output_stream << buffer;
	buffer = llformat( "\t\tnext_owner_mask\t%08x\n",	mMaskNextOwner);
	output_stream << buffer;

	mCreator.toString(uuid_str);
	output_stream <<  "\t\tcreator_id\t" << uuid_str << "\n";

	mOwner.toString(uuid_str);
	output_stream <<  "\t\towner_id\t" << uuid_str << "\n";

	mLastOwner.toString(uuid_str);
	output_stream <<  "\t\tlast_owner_id\t" << uuid_str << "\n";

	mGroup.toString(uuid_str);
	output_stream <<  "\t\tgroup_id\t" << uuid_str << "\n";

	if(mIsGroupOwned)
	{
		output_stream <<  "\t\tgroup_owned\t1\n";
	}
	output_stream << "\t}\n";
	return TRUE;
}

// Deleted LLPermissions::exportFileXML() and LLPermissions::importXML()
// because I can't find any non-test code references to it. 2009-05-04 JC

bool LLPermissions::operator==(const LLPermissions &rhs) const
{   
	return 
		(mCreator == rhs.mCreator) &&
		(mOwner == rhs.mOwner) &&
		(mLastOwner == rhs.mLastOwner ) &&
		(mGroup == rhs.mGroup ) &&
		(mMaskBase == rhs.mMaskBase ) &&
		(mMaskOwner == rhs.mMaskOwner ) &&
		(mMaskGroup == rhs.mMaskGroup ) &&
		(mMaskEveryone == rhs.mMaskEveryone ) &&
		(mMaskNextOwner == rhs.mMaskNextOwner ) &&
		(mIsGroupOwned == rhs.mIsGroupOwned);
}


bool LLPermissions::operator!=(const LLPermissions &rhs) const
{   
	return 
		(mCreator != rhs.mCreator) ||
		(mOwner != rhs.mOwner) ||
		(mLastOwner != rhs.mLastOwner ) ||
		(mGroup != rhs.mGroup ) ||
		(mMaskBase != rhs.mMaskBase ) ||
		(mMaskOwner != rhs.mMaskOwner ) ||
		(mMaskGroup != rhs.mMaskGroup ) ||
		(mMaskEveryone != rhs.mMaskEveryone ) ||
		(mMaskNextOwner != rhs.mMaskNextOwner) ||
		(mIsGroupOwned != rhs.mIsGroupOwned);
}

std::ostream& operator<<(std::ostream &s, const LLPermissions &perm)
{
	s << "{Creator=" 		<< perm.getCreator();
	s << ", Owner=" 		<< perm.getOwner();
	s << ", Group=" 		<< perm.getGroup();
	s << std::hex << ", BaseMask=0x" << perm.getMaskBase();
	s << ", OwnerMask=0x" << perm.getMaskOwner();
	s << ", EveryoneMask=0x" << perm.getMaskEveryone();
	s << ", GroupMask=0x" << perm.getMaskGroup();
	s << ", NextOwnerMask=0x" << perm.getMaskNextOwner() << std::dec;
	s << "}";
	return s;
}

///----------------------------------------------------------------------------
/// Class LLAggregatePermissions
///----------------------------------------------------------------------------

const LLAggregatePermissions LLAggregatePermissions::empty;


LLAggregatePermissions::LLAggregatePermissions()
{
	for(S32 i = 0; i < PI_COUNT; ++i)
	{
		mBits[i] = AP_EMPTY;
	}
}

LLAggregatePermissions::EValue LLAggregatePermissions::getValue(PermissionBit bit) const
{
	EPermIndex idx = perm2PermIndex(bit);
	EValue rv = AP_EMPTY;
	if(idx != PI_END)
	{
		rv = (LLAggregatePermissions::EValue)(mBits[idx]);
	}
	return rv;
}

// returns the bits compressed into a single byte: 00TTMMCC
// where TT = transfer, MM = modify, and CC = copy
// LSB is to the right
U8 LLAggregatePermissions::getU8() const
{
	U8 byte = mBits[PI_TRANSFER];
	byte <<= 2;
	byte |= mBits[PI_MODIFY];
	byte <<= 2;
	byte |= mBits[PI_COPY];
	return byte;
}

BOOL LLAggregatePermissions::isEmpty() const
{
	for(S32 i = 0; i < PI_END; ++i)
	{
		if(mBits[i] != AP_EMPTY)
		{
			return FALSE;
		}
	}
	return TRUE;
}

void LLAggregatePermissions::aggregate(PermissionMask mask)
{
	BOOL is_allowed = mask & PERM_COPY;
	aggregateBit(PI_COPY, is_allowed);
	is_allowed = mask & PERM_MODIFY;
	aggregateBit(PI_MODIFY, is_allowed);
	is_allowed = mask & PERM_TRANSFER;
	aggregateBit(PI_TRANSFER, is_allowed);
}

void LLAggregatePermissions::aggregate(const LLAggregatePermissions& ag)
{
	for(S32 idx = PI_COPY; idx != PI_END; ++idx)
	{
		aggregateIndex((EPermIndex)idx, ag.mBits[idx]);
	}
}

void LLAggregatePermissions::aggregateBit(EPermIndex idx, BOOL allowed)
{
	//if(AP_SOME == mBits[idx]) return; // P4 branch prediction optimization
	switch(mBits[idx])
	{
	case AP_EMPTY:
		mBits[idx] = allowed ? AP_ALL : AP_NONE;
		break;
	case AP_NONE:
		mBits[idx] = allowed ? AP_SOME: AP_NONE;
		break;
	case AP_SOME:
		// no-op
		break;
	case AP_ALL:
		mBits[idx] = allowed ? AP_ALL : AP_SOME;
		break;
	default:
		LL_WARNS() << "Bad aggregateBit " << (S32)idx << " "
				<< (allowed ? "true" : "false") << LL_ENDL;
		break;
	}
}

void LLAggregatePermissions::aggregateIndex(EPermIndex idx, U8 bits)
{
	switch(mBits[idx])
	{
	case AP_EMPTY:
		mBits[idx] = bits;
		break;
	case AP_NONE:
		switch(bits)
		{
		case AP_SOME:
		case AP_ALL:
			mBits[idx] = AP_SOME;
			break;
		case AP_EMPTY:
		case AP_NONE:
		default:
			// no-op
			break;
		}
		break;
	case AP_SOME:
		// no-op
		break;
	case AP_ALL:
		switch(bits)
		{
		case AP_NONE:
		case AP_SOME:
			mBits[idx] = AP_SOME;
			break;
		case AP_EMPTY:
		case AP_ALL:
		default:
			// no-op
			break;
		}
		break;
	default:
		LL_WARNS() << "Bad aggregate index " << (S32)idx << " "
				<<  (S32)bits << LL_ENDL;
		break;
	}
}

// static
LLAggregatePermissions::EPermIndex LLAggregatePermissions::perm2PermIndex(PermissionBit bit)
{
	EPermIndex idx = PI_END; // past any good value.
	switch(bit)
	{
	case PERM_COPY:
		idx = PI_COPY;
		break;
	case PERM_MODIFY:
		idx = PI_MODIFY;
		break;
	case PERM_TRANSFER:
		idx = PI_TRANSFER;
		break;
	default:
		break;
	}
	return idx;
}


void LLAggregatePermissions::packMessage(LLMessageSystem* msg, const char* field) const
{
	msg->addU8Fast(field, getU8());
}

void LLAggregatePermissions::unpackMessage(LLMessageSystem* msg, const char* block, const char* field, S32 block_num)
{
	const U8 TWO_BITS = 0x3; // binary 00000011
	U8 bits = 0;
	msg->getU8Fast(block, field, bits, block_num);
	mBits[PI_COPY] = bits & TWO_BITS;
	bits >>= 2;
	mBits[PI_MODIFY] = bits & TWO_BITS;
	bits >>= 2;
	mBits[PI_TRANSFER] = bits & TWO_BITS;
}

const std::string AGGREGATE_VALUES[4] =
	{
		std::string( "Empty" ),
		std::string( "None" ),
		std::string( "Some" ),
		std::string( "All" )
	};

std::ostream& operator<<(std::ostream &s, const LLAggregatePermissions &perm)
{
	s << "{PI_COPY=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_COPY]];
	s << ", PI_MODIFY=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_MODIFY]];
	s << ", PI_TRANSFER=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_TRANSFER]];
	s << "}";
	return s;
}

// This converts a permissions mask into a string for debugging use.
void mask_to_string(U32 mask, char* str)
{
	if (mask & PERM_MOVE)
	{
		*str = 'V';
	}
	else
	{
		*str = ' ';
	}	
	str++;

	if (mask & PERM_MODIFY)
	{
		*str = 'M';
	}
	else
	{
		*str = ' ';
	}	
	str++;
	
	if (mask & PERM_COPY)
	{
		*str = 'C';
	}
	else
	{
		*str = ' ';
	}	
	str++;
	
	if (mask & PERM_TRANSFER)
	{
		*str = 'T';
	}
	else
	{
		*str = ' ';
	}
	str++;
	
// <FS:CR> OpenSim export permission
	if (mask & PERM_EXPORT)
	{
		*str = 'X';
	}
	else
	{
		*str = ' ';
	}
	str++;
// </FS:CR>
	*str = '\0';
}

std::string mask_to_string(U32 mask)
{
	char str[16];
	mask_to_string(mask, str);
	return std::string(str);
}

///----------------------------------------------------------------------------
/// exported functions
///----------------------------------------------------------------------------
static const std::string PERM_CREATOR_ID_LABEL("creator_id");
static const std::string PERM_OWNER_ID_LABEL("owner_id");
static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id");
static const std::string PERM_GROUP_ID_LABEL("group_id");
static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group");
static const std::string PERM_BASE_MASK_LABEL("base_mask");
static const std::string PERM_OWNER_MASK_LABEL("owner_mask");
static const std::string PERM_GROUP_MASK_LABEL("group_mask");
static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask");
static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask");

LLSD ll_create_sd_from_permissions(const LLPermissions& perm)
{
	LLSD rv;
	rv[PERM_CREATOR_ID_LABEL] = perm.getCreator();
	rv[PERM_OWNER_ID_LABEL] = perm.getOwner();
	rv[PERM_LAST_OWNER_ID_LABEL] = perm.getLastOwner();
	rv[PERM_GROUP_ID_LABEL] = perm.getGroup();
	rv[PERM_IS_OWNER_GROUP_LABEL] = perm.isGroupOwned();
	rv[PERM_BASE_MASK_LABEL] = (S32)perm.getMaskBase();
	rv[PERM_OWNER_MASK_LABEL] = (S32)perm.getMaskOwner();
	rv[PERM_GROUP_MASK_LABEL] = (S32)perm.getMaskGroup();
	rv[PERM_EVERYONE_MASK_LABEL] = (S32)perm.getMaskEveryone();
	rv[PERM_NEXT_OWNER_MASK_LABEL] = (S32)perm.getMaskNextOwner();
	return rv;
}

LLPermissions ll_permissions_from_sd(const LLSD& sd_perm)
{
	LLPermissions rv;
	rv.init(
		sd_perm[PERM_CREATOR_ID_LABEL].asUUID(),
		sd_perm[PERM_OWNER_ID_LABEL].asUUID(),
		sd_perm[PERM_LAST_OWNER_ID_LABEL].asUUID(),
		sd_perm[PERM_GROUP_ID_LABEL].asUUID());

	// We do a cast to U32 here since LLSD does not attempt to
	// represent unsigned ints.
	PermissionMask mask;
	mask = (U32)(sd_perm[PERM_BASE_MASK_LABEL].asInteger());
	rv.setMaskBase(mask);
	mask = (U32)(sd_perm[PERM_OWNER_MASK_LABEL].asInteger());
	rv.setMaskOwner(mask);
	mask = (U32)(sd_perm[PERM_EVERYONE_MASK_LABEL].asInteger());
	rv.setMaskEveryone(mask);
	mask = (U32)(sd_perm[PERM_GROUP_MASK_LABEL].asInteger());
	rv.setMaskGroup(mask);
	mask = (U32)(sd_perm[PERM_NEXT_OWNER_MASK_LABEL].asInteger());
	rv.setMaskNext(mask);
	rv.fix();
	return rv;
}
