/*----------------------------------------------------------------------------+
 |       ___     _       _                                                    |
 |      /   |   | |     | |                                                   |
 |     / /| | __| | ___ | |__   ___                                           |
 |    / /_| |/ _  |/ _ \|  _ \ / _ \                                          |
 |   / ___  | (_| | (_) | |_) |  __/                                          |
 |  /_/   |_|\__,_|\___/|____/ \___|                                          |
 |                                                                            |
 |                                                                            |
 |  ADOBE CONFIDENTIAL                                                        |
 |  __________________                                                        |
 |                                                                            |
 |  Copyright (c) 2003 - 2007, Adobe Systems Incorporated.                    |
 |  All rights reserved.                                                      |
 |                                                                            |
 |  NOTICE:  All information contained herein is, and remains the property    |
 |  of Adobe Systems Incorporated and its suppliers, if any. The intellectual |
 |  and technical concepts contained herein are proprietary to Adobe Systems  |
 |  Incorporated and its suppliers and may be covered by U.S. and Foreign     |
 |  Patents, patents in process, and are protected by trade secret or         |
 |  copyright law. Dissemination of this information or reproduction of this  |
 |  material is strictly forbidden unless prior written permission is         |
 |  obtained from Adobe Systems Incorporated.                                 |
 |                                                                            |
 |          Adobe Systems Incorporated       415.832.2000                     |
 |          601 Townsend Street              415.832.2020 fax                 |
 |          San Francisco, CA 94103                                           |
 |                                                                            |
 +----------------------------------------------------------------------------*/


#include "StdAfx.h"
#include "FmsAuthAdaptor.h"
#include "FmsAuthActions.h"
#include "FmsMedia.h"
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

#if defined (WIN32)
#pragma warning(disable : 4996)

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpReserved )  // reserved
{
	return TRUE;
}
#endif


// --------------------------------------------------------------------------
//IFmsAuthAdaptor

class FmsAuthAdaptor : public IFmsAuthAdaptor
{	
	public:

		FmsAuthAdaptor(IFmsAuthServerContext* pFmsAuthServerContext) 
			: m_pFmsAuthServerContext(pFmsAuthServerContext) {}
		
		virtual ~FmsAuthAdaptor() {}
		
		void authorize(IFmsAuthEvent* pAev);
		void notify(IFmsAuthEvent* pAev);
		void getEvents(I32 aevBitAuth[], I32 aevBitNotf[], unsigned int count);

	private:

		bool getStats(I64 clientStatsHandle, FmsClientStats& baseStats);
		void processStats(IFmsAuthEvent* pAev);

		IFmsAuthServerContext* m_pFmsAuthServerContext;

};

//////////////////utils////////////////////////////////////////
/////////////////////////////////////////////////
static char* getStringField(const IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop) 
{
	FmsVariant field;
	if (pEv->getField(prop, field) == IFmsAuthEvent::S_SUCCESS && field.type == field.kString)
	{
		return field.str;
	}
	return 0;
}

static bool getI8Field(const IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, I8& iValue) 
{
	FmsVariant field;
	if (pEv->getField(prop, field) == IFmsAuthEvent::S_SUCCESS && field.type == field.kI8)
	{
		iValue = field.i8;
		return true;
	}
	return false;
}

static bool getI32Field(const IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, I32& iValue) 
{
	FmsVariant field;
	if (pEv->getField(prop, field) == IFmsAuthEvent::S_SUCCESS && field.type == field.kI32)
	{
		iValue = field.i32;
		return true;
	}
	return false;
}

static bool getI64Field(const IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, I64& iValue) 
{
	FmsVariant field;
	if (pEv->getField(prop, field) == IFmsAuthEvent::S_SUCCESS && field.type == field.kI64)
	{
		iValue = field.i64;
		return true;
	}
	return false;
}

static bool getFloatField(const IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, float& fValue) 
{
	FmsVariant field;
	if (pEv->getField(prop, field) == IFmsAuthEvent::S_SUCCESS && field.type == field.kFloat)
	{
		fValue = field.f;
		return true;
	}
	return false;
}

static bool getU16Field(const IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, U16& iValue) 
{
	FmsVariant field;
	if (pEv->getField(prop, field) == IFmsAuthEvent::S_SUCCESS && field.type == field.kU16)
	{
		iValue = field.u16;
		return true;
	}
	return false;
}

static bool setStringField(IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, char* pValue) 
{
	FmsVariant field;
	field.setString(pValue);
	return pEv->setField(prop, field) == IFmsAuthEvent::S_SUCCESS; 
}

static bool setI8Field(IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, I8 iValue) 
{
	FmsVariant field;
	field.setI8(iValue);	
	return pEv->setField(prop, field) == IFmsAuthEvent::S_SUCCESS; 
}

static bool setI32Field(IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, I32 iValue) 
{
	FmsVariant field;
	field.setI32(iValue);	
	return pEv->setField(prop, field) == IFmsAuthEvent::S_SUCCESS; 
}

static bool setI64Field(IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, I64 iValue) 
{
	FmsVariant field;
	field.setI64(iValue);	
	return pEv->setField(prop, field) == IFmsAuthEvent::S_SUCCESS; 
}

static bool setFloatField(IFmsAuthEvent* pEv, IFmsAuthEvent::Field prop, float fValue) 
{
	FmsVariant field;
	field.setFloat(fValue);	
	return pEv->setField(prop, field) == IFmsAuthEvent::S_SUCCESS; 
}


static bool isADPCMSupported(int iAudioCodecs)
{	
	return (iAudioCodecs & SUPPORT_SND_ADPCM) != 0;
}

static bool isVP6Supported(int iVideoCodecs)
{
	int iAllVP6 = ( SUPPORT_VID_VP6ALPHA | SUPPORT_VID_VP6 );
	return (iVideoCodecs & iAllVP6) != 0;
}

static bool isService(int iType)
{
	return (iType & TYPE_SERVICE) != 0;
}

static bool isAMF3(unsigned char uEncod)
{
	return (uEncod == ENCODE_AMF3);
}

//////////////process all events/////////////////
class MyFmsAuthorizeEvent 
{
	public:
		
		MyFmsAuthorizeEvent(IFmsAuthEvent* pAev, IFmsAuthServerContext*	pFmsAuthServerContext)
			: m_pAev(pAev), m_pFmsAuthServerContext(pFmsAuthServerContext) {}
		
		virtual ~MyFmsAuthorizeEvent() {}
		
		void authorize();

	private:
		
		IFmsAuthEvent*			m_pAev;
		IFmsAuthServerContext*	m_pFmsAuthServerContext;

};

//here you can safely process authorization events
void MyFmsAuthorizeEvent::authorize()
{
	//approved
	bool bAuthorized = true;
	
	//proccessing	
	switch(m_pAev->getType())
	{
		case IFmsAuthEvent::E_CONNECT:
		{
			//the following fields can be changed only in connect:
			//F_CLIENT_READ_ACCESS, F_CLIENT_WRITE_ACCESS
			//F_CLIENT_READ_ACCESS_LOCK, F_CLIENT_WRITE_ACCESS_LOCK,
			//F_CLIENT_AUDIO_SAMPLE_ACCESS, F_CLIENT_VIDEO_SAMPLE_ACCESS, 
            //F_CLIENT_AUDIO_SAMPLE_ACCESS_LOCK, F_CLIENT_VIDEO_SAMPLE_ACCESS_LOCK
			I8 iValue;
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_WRITE_ACCESS, iValue))
			{
				bool bRes = setI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_WRITE_ACCESS, iValue);
			}
			//here is a redirect connection case 
			char* pUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI); 
			if (pUri && !strcmp(pUri, "rtmp://localhost/streamtest"))
			{				
				//redirect connection to new uri
				setStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REDIRECT_URI, 
					"rtmp://localhost:1935/streamtest");
				bAuthorized = false;	
			}
		}
		break;	
		case IFmsAuthEvent::E_PLAY:
		{			
			char* pStreamName = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME); 
			if (pStreamName)
			{
				setStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME, pStreamName);
			}			
			char* pStreamType = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE);
			if (pStreamType)
			{
				setStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE, pStreamType);
			}			
			char* pStreamQuery = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_QUERY);
			if (pStreamQuery)
			{
				setStringField(m_pAev, IFmsAuthEvent::F_STREAM_QUERY, pStreamQuery);
			}			
			I8 iValue;
			if (getI8Field(m_pAev, IFmsAuthEvent::F_STREAM_RESET, iValue))
			{
				//not 0 means do not add it to play list, false otherwise
				setI8Field(m_pAev, IFmsAuthEvent::F_STREAM_RESET, iValue);	
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_STREAM_IGNORE, iValue))
			{
				//not 0 means ignore timestamps, true otherwise
				setI8Field(m_pAev, IFmsAuthEvent::F_STREAM_IGNORE, iValue);	
			}
			
			char* pStreamTransition = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TRANSITION); 
			if (pStreamTransition && strlen(pStreamTransition))
			{				
				//here is how to handle MBR transition case
				if (!strcmp(pStreamTransition, "switch") ||
					!strcmp(pStreamTransition, "swap"))
				{					
					//get old stream (switch from) properties, 
					char* pOldStreamName = getStringField(m_pAev, IFmsAuthEvent::F_OLD_STREAM_NAME);
					char* pOldStreamType = getStringField(m_pAev, IFmsAuthEvent::F_OLD_STREAM_TYPE);
					char* pOldStreamQuery = getStringField(m_pAev, IFmsAuthEvent::F_OLD_STREAM_QUERY);
					//if pOldStream empty (optional for switch) current stream is in play
					//do we really want stream transition? 	
					{
						//no we do not allow transition
						//bAuthorized = false;
						//now transition will be turned off and old stream continue playing
						//break;					
					}
					//doing nothing will execute transition mode as is
					//or you could modify transition by changing transition properties					
				}				
			}
			else
			{
				//this is not a transition but regular play waiting for approval
				//or you could turn play into play2 by changing transition properties
			}			
		}
		break;
		case IFmsAuthEvent::E_PUBLISH:
		{	
			char* pStreamName = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME); 
			if (pStreamName)
			{
				setStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME, pStreamName);
			}	
			char* pStreamType = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE);
			if (pStreamType)
			{
				setStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE, pStreamType);
			}
			I32 iValue;
			if (getI32Field(m_pAev, IFmsAuthEvent::F_STREAM_PUBLISH_TYPE, iValue))
			{
				//0 record, 1 append, -1 live
				setI32Field(m_pAev, IFmsAuthEvent::F_STREAM_PUBLISH_TYPE, iValue);	
			}
			if (getI32Field(m_pAev, IFmsAuthEvent::F_STREAM_PUBLISH_BROADCAST, iValue))
			{
				//multicast 0:1 true/false not supported
				setI32Field(m_pAev, IFmsAuthEvent::F_STREAM_PUBLISH_BROADCAST, 0);	 
			}			
		} 		
		break;
		case IFmsAuthEvent::E_FILENAME_TRANSFORM:
			{
				I64 iValue;
				if (getI64Field(m_pAev, IFmsAuthEvent::F_CLIENT_ID, iValue))
				{
					I64 iClientId = iValue;	
					//should be false not allowed to set
					bool bSet = setI64Field(m_pAev, IFmsAuthEvent::F_CLIENT_ID, iValue); 
				}
				char* pStreamName = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME); 
				if (pStreamName)
				{
					//should be false not allowed
					bool bSet = setStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME, pStreamName); 
				}
				char* pStreamPath = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_PATH); 
				if (pStreamPath)
				{
					setStringField(m_pAev, IFmsAuthEvent::F_STREAM_PATH, pStreamPath);
				}				
				char* pStreamType = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE);
				if (pStreamType)
				{
					setStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE, pStreamType);		
				}						
			}
		break;
		case IFmsAuthEvent::E_PAUSE:
		{
			bAuthorized = false; //block it
						
			float fValue;
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_PAUSE_TIME, fValue))
			{
				float fPauseTime = fValue;	//in sec	
			}		
			
			I8 iValue;
			if (getI8Field(m_pAev, IFmsAuthEvent::F_STREAM_PAUSE, iValue))
			{
				//true means PAUSE otherwise false
				bool boolPause = iValue != 0;	
			}			
			if (getI8Field(m_pAev, IFmsAuthEvent::F_STREAM_PAUSE_TOGGLE, iValue))
			{
				//true means PAUSE_TOGGLE otherwise false
				bool boolPauseToggle = iValue != 0;	
			}	
		
			FmsVariant field;
			
			//action to notify SSAS by calling "method" with U16 variable = 12345
			//and I64 variable client id
			if (m_pAev->getField(IFmsAuthEvent::F_CLIENT_ID, field) == IFmsAuthEvent::S_SUCCESS)
			{
				I64 clientId = field.i64;
				IFmsNotifyAction* pAction = m_pAev->addNotifyAction("Notified by adaptor");
				pAction->setClientId(field);
				field.setString("method");
				pAction->setMethodName(field);
				field.setU16(12345);

				///script does not work with I64 and returns invalid type
				//should be preset to double or string
				//field.setI64(clientId);  //wrong!!!
				
				field.setDouble((double)clientId);
				pAction->addParam(field);
			}
		}
		break;
		case IFmsAuthEvent::E_SEEK:
		{
			bAuthorized = false;	//block it
			
			float fValue;
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_SEEK_POSITION, fValue))
			{
				//set seek offset in sec; fValue + 3 will be 3 sec later
				float fSeekTime = fValue;	
				setFloatField(m_pAev, IFmsAuthEvent::F_STREAM_SEEK_POSITION, fSeekTime);
			}			
		}
		break;
		case IFmsAuthEvent::E_LOADSEGMENT:
		{
			//read only event substituted PLAY on origin in case of recorded stream
			//bAuthorized = false;	//block it
			
			I64 iValue;	
			if (getI64Field(m_pAev, IFmsAuthEvent::F_SEGMENT_START, iValue))
			{
				I64 iStart = iValue; //segment begin position in bytes
			}			
			if (getI64Field(m_pAev, IFmsAuthEvent::F_SEGMENT_END, iValue))
			{
				I64 iEnd = iValue;	//segment end position in bytes
			}			
		}
		break;
		case IFmsAuthEvent::E_RECORD:
		{
			float fValue;	
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_RECORD_MAXSIZE, fValue))
			{
				float recMaxSize = fValue; //max stream recording size in kb
				setFloatField(m_pAev, IFmsAuthEvent::F_STREAM_RECORD_MAXSIZE, recMaxSize);
			}			
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_RECORD_MAXDURATION, fValue))
			{
				float recMaxDuration = fValue;	//max stream recording duration in sec
				setFloatField(m_pAev, IFmsAuthEvent::F_STREAM_RECORD_MAXDURATION, recMaxDuration);
			}	
		}
		break;
	}

	char buf[1024];
	const char* const action = bAuthorized ? "approved" : "rejected";
	sprintf(buf, "Received authorization type=%d id=%p %s\n", m_pAev->getType(), 
		m_pAev, action);
	//here is a log goes to server log if true also to event log
	m_pFmsAuthServerContext->log(buf, IFmsServerContext::kInformation, false); 
	m_pFmsAuthServerContext->onAuthorize(m_pAev, bAuthorized);
}

class MyFmsNotifyEvent 
{
	public:
		MyFmsNotifyEvent(IFmsAuthEvent* pAev, IFmsAuthServerContext* pFmsAuthServerContext)
			: m_pAev(pAev), m_pFmsAuthServerContext(pFmsAuthServerContext) {}
		
		virtual ~MyFmsNotifyEvent() {}
		
		void notify() const;

	private:
		
		IFmsAuthEvent*	m_pAev;
		IFmsAuthServerContext*	m_pFmsAuthServerContext;
		
};

void MyFmsNotifyEvent::notify() const
{	
	switch(m_pAev->getType())
	{
		case IFmsAuthEvent::E_PLAY:
		{
			//all pointers to buffer are valid in this scope. 
			//kString type look at field.type!
			char* pAppName = getStringField(m_pAev, IFmsAuthEvent::F_APP_NAME);
			char* pAppInst = getStringField(m_pAev, IFmsAuthEvent::F_APP_INST);
			char* pAppUri = getStringField(m_pAev, IFmsAuthEvent::F_APP_URI);
			
			char* pClIp = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_IP);
			char* pClUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI);
			char* pClNewUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REDIRECT_URI);
			char* pClVhost = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_VHOST);
			char* pClRef = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REFERRER);
			char* pClPurl = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_PAGE_URL);
			char* pClAgent = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_USER_AGENT);
			char* pClRAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_READ_ACCESS);
			char* pClWAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_WRITE_ACCESS);
			char* pClAudioAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_AUDIO_SAMPLE_ACCESS);
			char* pClVideoAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_VIDEO_SAMPLE_ACCESS);
			char* pClProto = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_PROTO);
			char* pClUstem = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI_STEM);
			
			char* pStreamName = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME);
			char* pStreamType = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE);
			char* pStreamQuery = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_QUERY);
			char* pStreamPath = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_PATH);

			I32 iValue;
			if (getI32Field(m_pAev, IFmsAuthEvent::F_CLIENT_AUDIO_CODECS, iValue))
			{
				bool bADPCM = isADPCMSupported(iValue);
			}
			if (getI32Field(m_pAev, IFmsAuthEvent::F_CLIENT_VIDEO_CODECS, iValue))
			{
				bool bVP6 = isVP6Supported(iValue);
			}			
			if (getI32Field(m_pAev, IFmsAuthEvent::F_CLIENT_TYPE, iValue))
			{	
				bool bService = isService(iValue);
			}
			if (getI32Field(m_pAev, IFmsAuthEvent::F_STREAM_ID, iValue))
			{	
				I32 iStreamId = iValue;
			}
						
			float fValue;
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_LENGTH, fValue))
			{
				float fLength = fValue; //in sec
			}
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_POSITION, fValue))
			{
				float iPosition = fValue; //in sec
			}

			I64 lValue;
			if (getI64Field(m_pAev, IFmsAuthEvent::F_CLIENT_ID, lValue))
			{
				I64 iClientId = lValue;		
			}
						
			I8 sValue;
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_SECURE, sValue))
			{
				bool bSecure = sValue != 0;
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_AMF_ENCODING, sValue))
			{
				bool bAMF3 = isAMF3(sValue);
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_READ_ACCESS_LOCK, sValue))
			{			
				bool bRead = sValue != 0;
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_WRITE_ACCESS_LOCK, sValue))
			{
				bool bWrite = sValue != 0;
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_AUDIO_SAMPLE_ACCESS_LOCK, sValue))
			{
				bool bAudioRead = sValue != 0;
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_CLIENT_VIDEO_SAMPLE_ACCESS_LOCK, sValue))
			{
				bool bVideoRead = sValue != 0;
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_STREAM_RESET, sValue))
			{
				bool bReset = sValue != 0;
			}
			if (getI8Field(m_pAev, IFmsAuthEvent::F_STREAM_IGNORE, sValue))
			{
				bool bIgnore = sValue != 0;
			}
		}			
		break;
		
		case IFmsAuthEvent::E_SEEK:
		{
			float fValue;
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_SEEK_POSITION, fValue))
			{
				float fSeekTime = fValue;
			}

			//action by disconnecting seeking client attached to notifable event 
			FmsVariant field;
			if (m_pAev->getField(IFmsAuthEvent::F_CLIENT_ID, field) == IFmsAuthEvent::S_SUCCESS)
			{
				IFmsDisconnectAction* pAction = 
					const_cast<IFmsAuthEvent*>(m_pAev)->
						addDisconnectAction("Seek is not allowed. Blocked by adaptor");
				pAction->setClientId(field);
			}
		}
		break;

		case IFmsAuthEvent::E_CODEC_CHANGE:
		{
			//all pointers to buffer are valid in this scope. 
			//kString type look at field.type!
			char* pAppName = getStringField(m_pAev, IFmsAuthEvent::F_APP_NAME);
			char* pAppInst = getStringField(m_pAev, IFmsAuthEvent::F_APP_INST);
			char* pAppUri = getStringField(m_pAev, IFmsAuthEvent::F_APP_URI);
			
			char* pClIp = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_IP);
			char* pClUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI);
			char* pClNewUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REDIRECT_URI);
			char* pClVhost = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_VHOST);
			char* pClRef = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REFERRER);
			char* pClPurl = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_PAGE_URL);
			char* pClAgent = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_USER_AGENT);
			char* pClRAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_READ_ACCESS);
			char* pClWAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_WRITE_ACCESS);
			char* pClAudioAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_AUDIO_SAMPLE_ACCESS);
			char* pClVideoAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_VIDEO_SAMPLE_ACCESS);
			char* pClProto = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_PROTO);
			char* pClUstem = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI_STEM);
			
			char* pStreamName = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME);
			char* pStreamType = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE);
			char* pStreamQuery = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_QUERY);
			char* pStreamPath = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_PATH);

			U16 fType;
			if (getU16Field(m_pAev, IFmsAuthEvent::F_STREAM_CODEC_TYPE, fType))
			{
				U16 streamCodecType = fType;
				if (streamCodecType == kVIDEO_CODEC)
				{
					U16 fValue;
					if (getU16Field(m_pAev, IFmsAuthEvent::F_STREAM_CODEC, fValue))
					{				
						U16 streamCodecValue = fValue;
						if (streamCodecValue == VIDEO_CODEC_SORENSON)
						{
							//Sorenson publishing client detected.
							//Do not allow him publishing by creating 
							//a diconnect action to disconnect this client. 
							FmsVariant field;
							if (m_pAev->getField(IFmsAuthEvent::F_CLIENT_ID, field) == IFmsAuthEvent::S_SUCCESS)
							{
								IFmsDisconnectAction* pAction = 
									const_cast<IFmsAuthEvent*>(m_pAev)->
									addDisconnectAction("Sorenson is not allowed. Blocked by adaptor");
								pAction->setClientId(field);
							}
						}
					}		
				}
			}				
		}
		break;
		case IFmsAuthEvent::E_RECORD_STOP:
		{
			//all pointers to buffer are valid in this scope. 
			//kString type look at field.type!
			char* pAppName = getStringField(m_pAev, IFmsAuthEvent::F_APP_NAME);
			char* pAppInst = getStringField(m_pAev, IFmsAuthEvent::F_APP_INST);
			char* pAppUri = getStringField(m_pAev, IFmsAuthEvent::F_APP_URI);
			
			char* pClIp = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_IP);
			char* pClUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI);
			char* pClNewUri = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REDIRECT_URI);
			char* pClVhost = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_VHOST);
			char* pClRef = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_REFERRER);
			char* pClPurl = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_PAGE_URL);
			char* pClAgent = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_USER_AGENT);
			char* pClRAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_READ_ACCESS);
			char* pClWAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_WRITE_ACCESS);
			char* pClAudioAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_AUDIO_SAMPLE_ACCESS);
			char* pClVideoAccess = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_VIDEO_SAMPLE_ACCESS);
			char* pClProto = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_PROTO);
			char* pClUstem = getStringField(m_pAev, IFmsAuthEvent::F_CLIENT_URI_STEM);
			
			char* pStreamName = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_NAME);
			char* pStreamType = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_TYPE);
			char* pStreamQuery = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_QUERY);
			char* pStreamPath = getStringField(m_pAev, IFmsAuthEvent::F_STREAM_PATH);
			
			float fValue;	
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_RECORD_MAXSIZE, fValue))
			{
				float recMaxSize = fValue; //max stream recording size in kb
			}			
			if (getFloatField(m_pAev, IFmsAuthEvent::F_STREAM_RECORD_MAXDURATION, fValue))
			{
				float recMaxDuration = fValue;	//max stream recording duration in sec
			}	
		}
		break;
	}	
	char buf[1024];
	sprintf(buf, "Received notification type=%d id=%p\n", m_pAev->getType(), m_pAev);
	//here is a log goes to server log if true also to event log
	m_pFmsAuthServerContext->log(buf, IFmsServerContext::kInformation, false); 
	m_pFmsAuthServerContext->onNotify(m_pAev);
}

/*all authorization events are coming here,
please do not modify this function, here MyFmsAppAuthEvent allocated on stack 
but can be also allocated by new and delete and passed to new thread to process,
all processing should be done in authorize function from derived class above.
*/
void FmsAuthAdaptor::authorize(IFmsAuthEvent* pAev)
{
	 MyFmsAuthorizeEvent(pAev, m_pFmsAuthServerContext).authorize();
}

/*all notification events are coming here,
please do not modify this function, here MyFmsAppAuthEvent allocated on stack 
but can be allocated by new and delete and passed to new thread to process,
all processing should be done in notify function from derived class above.
*/
void FmsAuthAdaptor::notify(IFmsAuthEvent* pAev)
{
	 processStats(pAev);
	 MyFmsNotifyEvent(pAev, m_pFmsAuthServerContext).notify();
}

/*
* Get client statistics.
*/
bool FmsAuthAdaptor::getStats(I64 clientStatsHandle, FmsClientStats& baseStats)
{
	bool bValue= m_pFmsAuthServerContext->getClientStats(clientStatsHandle, baseStats);

	return bValue;
}

/*
* processStats save client ID/application into map at E_PLAY event.
* Remove client ID/application from map at E_DISCONNECT event.
* Retrieve client stats at E_PLAY and E_STOP.
*/
void FmsAuthAdaptor::processStats(IFmsAuthEvent* pAev)
{
	 I64 statsHandle;
	 FmsClientStats baseStats;
	 if (!getI64Field(pAev, IFmsAuthEvent::F_CLIENT_STATS_HANDLE, statsHandle))
		 return;
	 char* pAppName = getStringField(pAev, IFmsAuthEvent::F_APP_NAME);
	 if (pAev->getType() == IFmsAuthEvent::E_CONNECT) 
	 {
		 getStats(statsHandle, baseStats);

		 // log data
		 char buf[1024];
		 char hashKey[9];
		 memset(hashKey, 0, 9);
		 memcpy(hashKey, &statsHandle, sizeof(statsHandle));
		 sprintf(buf, "client Stats Handle= %s, bytes_in= %f, bytes_out= %f\n", hashKey, 
			 static_cast<double>(baseStats.bytes_in), static_cast<double>(baseStats.bytes_out));
		 m_pFmsAuthServerContext->log(buf, IFmsServerContext::kInformation, false); 
	 }
	 else if (pAev->getType() == IFmsAuthEvent::E_STOP)
	 {
		 getStats(statsHandle, baseStats);
	 }
}

/*by default bit is 0 allow to receive notification / authorization
to stop receiving event set bit to 1 
Note: events: E_APPSTART, E_APPSTOP, E_DISCONNECT, E_STOP, E_UNPUBLISH E_CODEC_CHANGE are excluded
by default and non authorizable.
*/
void FmsAuthAdaptor::getEvents(I32 aevBitAuth[], I32 aevBitNotf[], unsigned int count)
{	
	/////////////exclude certain auth events/////////////////////////
	IFmsAuthEvent::EventType authExcludeEvent[] = { IFmsAuthEvent::E_SEEK };
	//making E_SEEK non authorizable
	m_pFmsAuthServerContext->excludeEvents(aevBitAuth, count, authExcludeEvent, 1);
	/////////////exclude certain notify events. ////////////////////////////////////////////////////////////
	/*Warning: if E_CODEC_CHANGE event is not excluded, all messages will be scanned to detect codec change. 
	Subscribe to this event only as needed!*/
	//removing E_PAUSE, E_CODEC_CHANGE notifications
	IFmsAuthEvent::EventType notifyExcludeEvent[] = { IFmsAuthEvent::E_PAUSE, IFmsAuthEvent::E_CODEC_CHANGE };
	m_pFmsAuthServerContext->excludeEvents(aevBitNotf, count, notifyExcludeEvent, 2);	
	////////////////////////////////////////////////////////////////	
}
// --------------------------------------------------------------------------

extern "C" void FCExport FmsCreateAuthAdaptor(IFmsAuthServerContext* pAuthServerCtx, 
	IFmsAuthAdaptor*& pFmsAuthAdaptor)
{
	pFmsAuthAdaptor = new FmsAuthAdaptor(pAuthServerCtx);
	U32 version = pAuthServerCtx->getVersion();
	U16 w2 = LWORD(version);
	U16 w1 = HWORD(version);
	char buf[1024];
	char *ptr = buf;
	int valueLen = pAuthServerCtx->getConfig("UserKey1", &ptr, sizeof(buf));
 	if (!valueLen)
 	{
 		//key foo  found
		valueLen = pAuthServerCtx->getConfig("UserKey2", &ptr, sizeof(buf));
   		if (!valueLen)
		{
			//key boo found 
			return; 
		}
		if (valueLen < 0)
 		{
 			// failed to find this key
			return;
 		}
 	}
	if (valueLen < 0)
 	{
 		// failed to find this key
 		return; 
 	}
	//value length is bigger then a buffer size
	//real adaptor should allocate valueLen + 1 bytes 
	//and call again
}

extern "C" void FCExport FmsCreateAuthAdaptor2(IFmsAuthServerContext* pAuthServerCtx, 
	IFmsAuthAdaptor*& pFmsAuthAdaptor, U32& iVersion)
{
	pFmsAuthAdaptor = new FmsAuthAdaptor(pAuthServerCtx);
	U32 version = pAuthServerCtx->getVersion();
	U16 w2 = LWORD(version);
	U16 w1 = HWORD(version);
	iVersion = MKLONG(INTERFACE_MINOR_VERSION, INTERFACE_MAJOR_VERSION );
	char buf[1024];
	char *ptr = buf;
	int valueLen = pAuthServerCtx->getConfig("UserKey1", &ptr, sizeof(buf));
 	if (!valueLen)
 	{
 		//key foo  found
		valueLen = pAuthServerCtx->getConfig("UserKey2", &ptr, sizeof(buf));
   		if (!valueLen)
		{
			//key boo found 
			return; 
		}
		if (valueLen < 0)
 		{
 			// failed to find this key
			return;
 		}
 	}
	if (valueLen < 0)
 	{
 		// failed to find this key
 		return; 
 	}
	//value length is bigger then a buffer size
	//real adaptor should allocate valueLen + 1 bytes 
	//and call again
}

extern "C" void FCExport FmsDestroyAuthAdaptor(IFmsAuthAdaptor* pAuthAdaptor )
{
	delete pAuthAdaptor;	
}

extern "C" void FCExport FmsDestroyAuthAdaptor2(IFmsAuthAdaptor* pAuthAdaptor )
{
	delete pAuthAdaptor;	
}


