/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 *
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
	File:		RTSPSession.cpp

	Contains:	Implemenation of RTSPSession objects
	
	
*/
#define __RTSP_HTTP_DEBUG__ 0
#define __RTSP_HTTP_VERBOSE__ 0
#define __RTSP_AUTHENTICATION_DEBUG__ 0

#include "RTSPSession.h"
#include "RTSPRequest.h"
#include "QTSServerInterface.h"

#include "MyAssert.h"
#include "OSMemory.h"

#include "QTSS.h"
#include "QTSSModuleUtils.h"
#include "UserAgentParser.h"
#include "base64.h"
#include "OSArrayObjectDeleter.h"
#include "md5digest.h"

#if __FreeBSD__ 
	#include <unistd.h>
#endif

#include <errno.h>

#if __solaris__ || __linux__	
	#include <crypt.h>
#endif

#if __RTSP_HTTP_DEBUG__
	#define HTTP_TRACE(s) printf(s);
	#define HTTP_TRACE_SPL(s) PrintfStrPtrLen(s);
	#define HTTP_TRACE_ONE(s, one ) printf(s, one);
	#define HTTP_TRACE_TWO(s, one, two ) printf(s, one, two);
#else
	#define HTTP_TRACE(s);
	#define HTTP_TRACE_SPL(s);
	#define HTTP_TRACE_ONE(s, one );
	#define HTTP_TRACE_TWO(s, one, two );
#endif

#if __RTSP_HTTP_VERBOSE__
	#define HTTP_VTRACE(s) printf(s);
	#define HTTP_VTRACE_SPL(s) PrintfStrPtrLen(s);
	#define HTTP_VTRACE_ONE(s, one ) printf(s, one);
	#define HTTP_VTRACE_TWO(s, one, two ) printf(s, one, two);
#else
	#define HTTP_VTRACE(s);
	#define HTTP_VTRACE_SPL(s);
	#define HTTP_VTRACE_ONE(s, one );
	#define HTTP_VTRACE_TWO(s, one, two );
#endif



#if  __RTSP_HTTP_DEBUG__ || __RTSP_HTTP_VERBOSE__

static void PrintfStrPtrLen( StrPtrLen *splRequest )
{

	char	buff[1024];
	
	memcpy( buff, splRequest->Ptr, splRequest->Len );
	
	buff[ splRequest->Len] = 0;
	
	HTTP_TRACE_ONE( "%s\n", buff )
	//printf( "%s\n", buff );

}
#endif

//hack stuff
static char*					sBroadcasterSessionName="QTSSReflectorModuleBroadcasterSession";
static QTSS_AttributeID			sClientBroadcastSessionAttr	=	qtssIllegalAttrID;


static StrPtrLen	sVideoStr("video");
static StrPtrLen	sAudioStr("audio");
static StrPtrLen	sRtpMapStr("rtpmap");
static StrPtrLen	sControlStr("control");
static StrPtrLen	sBufferDelayStr("x-bufferdelay");

static StrPtrLen	sAuthAlgorithm("md5");
static StrPtrLen	sAuthQop("auth");
static StrPtrLen	sEmptyStr("");

// static class member  initialized in RTSPSession ctor
OSRefTable* RTSPSession::sHTTPProxyTunnelMap = NULL;
char		RTSPSession::sHTTPResponseHeaderBuf[kMaxHTTPResponseLen];
StrPtrLen	RTSPSession::sHTTPResponseHeaderPtr(sHTTPResponseHeaderBuf, kMaxHTTPResponseLen);

// stock reponse with place holder for server header and optional "x-server-ip-address" header ( %s%s%s for  "x-server-ip-address" + ip address + \r\n )
// the optional version must be generated at runtime to include a valid IP address for the actual interface
char*		RTSPSession::sHTTPResponseFormatStr =  "HTTP/1.0 200 OK\r\n%s%s%s%s\r\nConnection: close\r\nDate: Thu, 19 Aug 1982 18:30:00 GMT\r\nCache-Control: no-store\r\nPragma: no-cache\r\nContent-Type: application/x-rtsp-tunnelled\r\n\r\n";

void RTSPSession::Initialize()
{
	sHTTPProxyTunnelMap = new OSRefTable(OSRefTable::kDefaultTableSize);

	// Construct premade HTTP response for HTTP proxy tunnel
	::sprintf(sHTTPResponseHeaderBuf, sHTTPResponseFormatStr, "","","", QTSServerInterface::GetServerHeader().Ptr);
	
	sHTTPResponseHeaderPtr.Len = ::strlen(sHTTPResponseHeaderBuf);
	Assert(sHTTPResponseHeaderPtr.Len < kMaxHTTPResponseLen);
}


RTSPSession::RTSPSession( Bool16 doReportHTTPConnectionAddress )
: RTSPSessionInterface(),
  fRequest(NULL),
  fRTPSession(NULL),
  fReadMutex(),
  fHTTPMethod( kHTTPMethodInit ),
  fWasHTTPRequest( false ),
  fFoundValidAccept( false),
  fDoReportHTTPConnectionAddress(doReportHTTPConnectionAddress),
  fCurrentModule(0),
  fState(kReadingFirstRequest)
{
	// must guarantee this map is present
	Assert(sHTTPProxyTunnelMap != NULL);
	
	QTSServerInterface::GetServer()->AlterCurrentRTSPSessionCount(1);

	// Setup the QTSS param block, as none of these fields will change through the course of this session.
	fRoleParams.rtspRequestParams.inRTSPSession = this;
	fRoleParams.rtspRequestParams.inRTSPRequest = NULL;
	fRoleParams.rtspRequestParams.inClientSession = NULL;
	
	fModuleState.curModule = NULL;
	fModuleState.curTask = this;
	fModuleState.curRole = 0;
	fModuleState.globalLockRequested = false;
		
	fProxySessionID[0] = 0;
	fProxySessionIDPtr.Set( fProxySessionID, 0 );

	fLastRTPSessionID[0] = 0;
	fLastRTPSessionIDPtr.Set( fLastRTPSessionID, 0 );
	Assert(fLastRTPSessionIDPtr.Ptr == &fLastRTPSessionID[0]);
					
	(void)QTSS_IDForAttr(qtssClientSessionObjectType, sBroadcasterSessionName, &sClientBroadcastSessionAttr);

}

RTSPSession::~RTSPSession()
{
	// Invoke the session closing modules
	QTSS_RoleParams theParams;
	theParams.rtspSessionClosingParams.inRTSPSession = this;
	
	// Invoke modules
	for (UInt32 x = 0; x < QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPSessionClosingRole); x++)
		(void)QTSServerInterface::GetModule(QTSSModule::kRTSPSessionClosingRole, x)->CallDispatch(QTSS_RTSPSessionClosing_Role, &theParams);

	this->CleanupRequest();// Make sure that all our objects are deleted
	if (fSessionType == qtssRTSPSession)
		QTSServerInterface::GetServer()->AlterCurrentRTSPSessionCount(-1);
	else
		QTSServerInterface::GetServer()->AlterCurrentRTSPHTTPSessionCount(-1);
	
	if ( *fProxySessionID != '\0')
	{
#if DEBUG
		char * str = "???";
		
		if ( fSessionType == qtssRTSPHTTPInputSession )
			str = "input session";
		else if ( fSessionType == qtssRTSPHTTPSession )
			str = "input session";
		
		HTTP_VTRACE_TWO( "~RTSPSession, was a fProxySessionID (%s), %s\n", fProxySessionID, str )
#endif		
		sHTTPProxyTunnelMap->UnRegister( &fProxyRef );	
	}
}

SInt64 RTSPSession::Run()
{
	EventFlags events = this->GetEvents();
	QTSS_Error err = QTSS_NoErr;
	QTSSModule* theModule = NULL;
	UInt32 numModules = 0;
	Assert(fLastRTPSessionIDPtr.Ptr == &fLastRTPSessionID[0]);
	// Some callbacks look for this struct in the thread object
	OSThreadDataSetter theSetter(&fModuleState, NULL);
	
	//check for a timeout or a kill. If so, just consider the session dead
	if ((events & Task::kTimeoutEvent) || (events & Task::kKillEvent))
		fLiveSession = false;
	
	while (this->IsLiveSession())
	{
		// RTSP Session state machine. There are several well defined points in an RTSP request
		// where this session may have to return from its run function and wait for a new event.
		// Because of this, we need to track our current state and return to it.
		
		switch (fState)
		{
			case kReadingFirstRequest:
			{
				if ((err = fInputStream.ReadRequest()) == QTSS_NoErr)
				{
					// If the RequestStream returns QTSS_NoErr, it means
					// that we've read all outstanding data off the socket,
					// and still don't have a full request. Wait for more data.
					
					//+rt use the socket that reads the data, may be different now.
					fInputSocketP->RequestEvent(EV_RE);
					return 0;
				}
				
				if ((err != QTSS_RequestArrived) && (err != E2BIG))
				{
					// Any other error implies that the client has gone away. At this point,
					// we can't have 2 sockets, so we don't need to do the "half closed" check
					// we do below
					Assert(err > 0); 
					Assert(!this->IsLiveSession());
					break;
				}

				if (err == QTSS_RequestArrived)
					fState = kHTTPFilteringRequest;
				// If we get an E2BIG, it means our buffer was overfilled.
				// In that case, we can just jump into the following state, and
				// the code their does a check for this error and returns an error.
				if (err == E2BIG)
					fState = kHaveNonTunnelMessage;
			}
			continue;
			
			case kHTTPFilteringRequest:
			{	
			
				HTTP_TRACE( "RTSPSession::Run kHTTPFilteringRequest\n" )
			
				fState = kHaveNonTunnelMessage;	// assume it's not a tunnel setup message
												// prefilter will set correct tunnel state if it is.

				QTSS_Error	preFilterErr = this->PreFilterForHTTPProxyTunnel();
								
				if ( preFilterErr == QTSS_NoErr )
				{	
					HTTP_TRACE( "RTSPSession::Run kHTTPFilteringRequest\n" )
					continue;
				}
				else
				{	
					// pre filter error indicates a tunnelling message that could 
					// not join to a session.
					HTTP_TRACE( "RTSPSession::Run kHTTPFilteringRequest Tunnel protocol ERROR.\n" )
					return -1;
					
				}
			}
			
			case kWaitingToBindHTTPTunnel:
				//flush the GET response, if it's there
				err = fOutputStream.Flush();
				if (err == EAGAIN)
				{
					// If we get this error, we are currently flow-controlled and should
					// wait for the socket to become writeable again
					fSocket.RequestEvent(EV_WR);
				}
				return 0;
				//continue;
			
			case kSocketHasBeenBoundIntoHTTPTunnel:
			
				// DMS - Can this execute either? I don't think so... this one
				// we may not need...
				
				// I've been joined, it's time to kill this session.
				Assert(!this->IsLiveSession());	// at least the socket should not report connected any longer
				HTTP_TRACE( "RTSPSession has died of snarfage.\n" )
				break;
				
			
			case kReadingRequest:
			{
				// We should lock down the session while reading in data,
				// because we can't snarf up a POST while reading.
				OSMutexLocker readMutexLocker(&fReadMutex);

				// we should be only reading an RTSP request here, no HTTP tunnel messages
				
				if ((err = fInputStream.ReadRequest()) == QTSS_NoErr)
				{
					// If the RequestStream returns QTSS_NoErr, it means
					// that we've read all outstanding data off the socket,
					// and still don't have a full request. Wait for more data.
					
					//+rt use the socket that reads the data, may be different now.
					fInputSocketP->RequestEvent(EV_RE);
					return 0;
				}
				
				if ((err != QTSS_RequestArrived) && (err != E2BIG) && (err != QTSS_BadArgument))
				{
					//Any other error implies that the input connection has gone away.
					// We should only kill the whole session if we aren't doing HTTP.
					// (If we are doing HTTP, the POST connection can go away)
					Assert(err > 0);
					if (fOutputSocketP->IsConnected())
					{
						// If we've gotten here, this must be an HTTP session with
						// a dead input connection. If that's the case, we should
						// clean up immediately so as to not have an open socket
						// needlessly lingering around, taking up space.
						Assert(fOutputSocketP != fInputSocketP);
						Assert(!fInputSocketP->IsConnected());
						fInputSocketP->Cleanup();
						return 0;
					}
					else
					{
						Assert(!this->IsLiveSession());
						break;
					}
				}
				fState = kHaveNonTunnelMessage;
				// fall thru to kHaveNonTunnelMessage
			}
			
			case kHaveNonTunnelMessage:
			{	
				// should only get here when fInputStream has a full message built.
				
				Assert( fInputStream.GetRequestBuffer() );
				
				Assert(fRequest == NULL);
				fRequest = NEW RTSPRequest(this);
				fRoleParams.rtspRequestParams.inRTSPRequest = fRequest;
				fRoleParams.rtspRequestParams.inRTSPHeaders = fRequest->GetHeaderDictionary();

				// We have an RTSP request and are about to begin processing. We need to
				// make sure that anyone sending interleaved data on this session won't
				// be allowed to do so until we are done sending our response
				// We also make sure that a POST session can't snarf in while we're
				// processing the request.
				fReadMutex.Lock();
				fSessionMutex.Lock();
				
				// The fOutputStream's fBytesWritten counter is used to
				// count the # of bytes for this RTSP response. So, at
				// this point, reset it to 0 (we can then just let it increment
				// until the next request comes in)
				fOutputStream.ResetBytesWritten();
				
				// Check for an overfilled buffer, and return an error.
				if (err == E2BIG)
				{
					(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest,
																	qtssMsgRequestTooLong);
					fState = kPostProcessingRequest;
					break;
				}
				// Check for a corrupt base64 error, return an error
				if (err == QTSS_BadArgument)
				{
					(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest,
																	qtssMsgBadBase64);
					fState = kPostProcessingRequest;
					break;
				}

				Assert(err == QTSS_RequestArrived);
				fState = kFilteringRequest;
				
				// Note that there is no break here. We'd like to continue onto the next
				// state at this point. This goes for every case in this case statement
			}
			
			case kFilteringRequest:
			{
				// We received something so auto refresh
				// The need to auto refresh is because the api doesn't allow a module to refresh at this point
				// 
				fTimeoutTask.RefreshTimeout();

				//
				// Before we even do this, check to see if this is a *data* packet,
				// in which case this isn't an RTSP request, so we don't need to go
				// through any of the remaining steps
				
				if (fInputStream.IsDataPacket()) // can this interfere with MP3?
				{
					this->HandleIncomingDataPacket();
					fState = kCleaningUp;
					break;
				}
				
				
				//
				// In case a module wants to replace the request
				char* theReplacedRequest = NULL;
				char* oldReplacedRequest = NULL;
				
				// Setup the filter param block
				QTSS_RoleParams theFilterParams;
				theFilterParams.rtspFilterParams.inRTSPSession = this;
				theFilterParams.rtspFilterParams.inRTSPRequest = fRequest;
				theFilterParams.rtspFilterParams.outNewRequest = &theReplacedRequest;
				
				// Invoke filter modules
				numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPFilterRole);
				for (; (fCurrentModule < numModules) && ((!fRequest->HasResponseBeenSent()) || fModuleState.eventRequested); fCurrentModule++)
				{
					fModuleState.eventRequested = false;
					fModuleState.idleTime = 0;
					if (fModuleState.globalLockRequested )
					{	fModuleState.globalLockRequested = false;
						fModuleState.isGlobalLocked = true;
					}
					
					theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPFilterRole, fCurrentModule);
					(void)theModule->CallDispatch(QTSS_RTSPFilter_Role, &theFilterParams);
					fModuleState.isGlobalLocked = false;
					
					// If this module has requested an event, return and wait for the event to transpire
					if (fModuleState.globalLockRequested == true) // call this request back locked
						return this->CallLocked();
							
					if (fModuleState.eventRequested == true)
					{	
						this->ForceSameThread(); 	// We are holding mutexes, so we need to force
													// the same thread to be used for next Run()
						return fModuleState.idleTime; // If the module has requested idle time...
					}
			
					//
					// Check to see if this module has replaced the request. If so, check
					// to see if there is an old replacement that we should delete
					if (theReplacedRequest != NULL)
					{
						if (oldReplacedRequest != NULL)
							delete [] oldReplacedRequest;
						
						fRequest->SetVal(qtssRTSPReqFullRequest, theReplacedRequest, ::strlen(theReplacedRequest));
						oldReplacedRequest = theReplacedRequest;
						theReplacedRequest = NULL;
					}
					
				}
				
				fCurrentModule = 0;
				if (fRequest->HasResponseBeenSent())
				{
					fState = kPostProcessingRequest;
					break;
				}

				if (fSentOptionsRequest && this->ParseOptionsResponse())
				{
					fRoundTripTime = OS::Milliseconds() - fOptionsRequestSendTime;
					//printf("RTSPSession::Run RTT time = %ld msec\n", fRoundTripTime);
					fState = kSendingResponse;
					break;
				}
				else	
				// Otherwise, this is a normal request, so parse it and get the RTPSession.
					this->SetupRequest();
				
				// This might happen if there is some syntax or other error,
				// or if it is an OPTIONS request
				if (fRequest->HasResponseBeenSent())
				{
					fState = kPostProcessingRequest;
					break;
				}
				fState = kRoutingRequest;
			}
			case kRoutingRequest:
			{
				// Invoke router modules
				numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPRouteRole);
				{
					// Manipulation of the RTPSession from the point of view of
					// a module is guaranteed to be atomic by the API.
					Assert(fRTPSession != NULL);
					OSMutexLocker	locker(fRTPSession->GetSessionMutex());
					
					for (; (fCurrentModule < numModules) && ((!fRequest->HasResponseBeenSent()) || fModuleState.eventRequested); fCurrentModule++)
					{  
						fModuleState.eventRequested = false;
						fModuleState.idleTime = 0;
						if (fModuleState.globalLockRequested )
						{	fModuleState.globalLockRequested = false;
							fModuleState.isGlobalLocked = true;
						} 
						
						theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPRouteRole, fCurrentModule);
						(void)theModule->CallDispatch(QTSS_RTSPRoute_Role, &fRoleParams);
						fModuleState.isGlobalLocked = false;

						if (fModuleState.globalLockRequested == true) // call this request back locked
							return this->CallLocked();

						// If this module has requested an event, return and wait for the event to transpire
						if (fModuleState.eventRequested == true)
						{
							this->ForceSameThread(); 	// We are holding mutexes, so we need to force
														// the same thread to be used for next Run()
							return fModuleState.idleTime; // If the module has requested idle time...
						}
					}
				}
				fCurrentModule = 0;
				
				// SetupAuthLocalPath must happen after kRoutingRequest and before kAuthenticatingRequest
				// placed here so that if the state is shifted to kPostProcessingRequest from a response being sent
				// then the AuthLocalPath will still be set.
				fRequest->SetupAuthLocalPath(); 
				
				if (fRequest->HasResponseBeenSent())
				{
					fState = kPostProcessingRequest;
					break;
				}
				
				if(fRequest->SkipAuthorization())
				{
					// Skip the authentication and authorization states
					
					// The foll. normally gets executed at the end of the authorization state 
					// Prepare for kPreprocessingRequest state.
					fState = kPreprocessingRequest;

					if (fRequest->GetMethod() == qtssSetupMethod)
					// Make sure to erase the session ID stored in the request at this point.
					// If we fail to do so, this same session would be used if another
					// SETUP was issued on this same TCP connection.
						fLastRTPSessionIDPtr.Len = 0;
						
					break;
				}
				else
					fState = kAuthenticatingRequest;
			}
			
			case kAuthenticatingRequest:
			{
				
				QTSS_RTSPMethod method = fRequest->GetMethod();
				if (method != qtssIllegalMethod) do  
				{   //Set the request action before calling the authentication module
				
					if((method == qtssAnnounceMethod) || ((method == qtssSetupMethod) && fRequest->IsPushRequest()))
					{	fRequest->SetAction(qtssActionFlagsWrite);
						break;
					}
					
					void* theSession = NULL;
					UInt32 theLen = sizeof(theSession);
					if (QTSS_NoErr == fRTPSession->GetValue(sClientBroadcastSessionAttr, 0,  &theSession, &theLen) )
					{	fRequest->SetAction(qtssActionFlagsWrite); // an incoming broadcast session
						break;
					}

					fRequest->SetAction(qtssActionFlagsRead);
				} while (false);
				else
				{	Assert(0);
				}
				
				if(fRequest->GetAuthScheme() == qtssAuthNone)
				{
					QTSS_AuthScheme scheme = QTSServerInterface::GetServer()->GetPrefs()->GetAuthScheme();
					if( scheme == qtssAuthBasic)
							fRequest->SetAuthScheme(qtssAuthBasic);
					else if( scheme == qtssAuthDigest)
							fRequest->SetAuthScheme(qtssAuthDigest);
				}
				
				// Setup the authentication param block
				QTSS_RoleParams theAuthenticationParams;
				theAuthenticationParams.rtspAthnParams.inRTSPRequest = fRequest;
			
				fModuleState.eventRequested = false;
				fModuleState.idleTime = 0;
				if (QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPAthnRole) > 0)
				{
					if (fModuleState.globalLockRequested )
					{	
						fModuleState.globalLockRequested = false;
						fModuleState.isGlobalLocked = true;
					} 

					theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPAthnRole, 0);
					(void)theModule->CallDispatch(QTSS_RTSPAuthenticate_Role, &theAuthenticationParams);
					fModuleState.isGlobalLocked = false;
						
					if (fModuleState.globalLockRequested == true) // call this request back locked
						return this->CallLocked();

					// If this module has requested an event, return and wait for the event to transpire
					if (fModuleState.eventRequested == true)
					{
						this->ForceSameThread(); 	// We are holding mutexes, so we need to force
													// the same thread to be used for next Run()
						return fModuleState.idleTime; // If the module has requested idle time...
					}
				}
				
				this->CheckAuthentication();
												
				fCurrentModule = 0;
				if (fRequest->HasResponseBeenSent())
				{
					fState = kPostProcessingRequest;
					break;
				}
				fState = kAuthorizingRequest;
			}
			case kAuthorizingRequest:
			{
				// Invoke authorization modules
				numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPAuthRole);

				Bool16 		allowed = true;	
				QTSS_Error 	theErr = QTSS_NoErr;
								
				// Invoke authorization modules
				
				// Manipulation of the RTPSession from the point of view of
				// a module is guaranteed to be atomic by the API.
				Assert(fRTPSession != NULL);
				OSMutexLocker	locker(fRTPSession->GetSessionMutex());

				fRequest->SetAllowed(allowed);					
			
				for (; (fCurrentModule < numModules) && ((!fRequest->HasResponseBeenSent()) || fModuleState.eventRequested); fCurrentModule++)
				{
					fModuleState.eventRequested = false;
					fModuleState.idleTime = 0;
					if (fModuleState.globalLockRequested )
					{	fModuleState.globalLockRequested = false;
						fModuleState.isGlobalLocked = true;
					} 
					
					theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPAuthRole, fCurrentModule);
					(void)theModule->CallDispatch(QTSS_RTSPAuthorize_Role, &fRoleParams);
					fModuleState.isGlobalLocked = false;

					if (fModuleState.globalLockRequested == true) // call this request back locked
						return this->CallLocked();
						
					// If this module has requested an event, return and wait for the event to transpire
					if (fModuleState.eventRequested == true)
					{
						this->ForceSameThread(); 	// We are holding mutexes, so we need to force
													// the same thread to be used for next Run()
						return fModuleState.idleTime; // If the module has requested idle time...
					}
						
					// If module has failed the request send a response and exit loop	
					allowed = fRequest->GetAllowed();
					
					#if __RTSP_AUTHENTICATION_DEBUG__
					{
						UInt32 len = sizeof(Bool16);
						theErr = fRequest->GetValue(qtssRTSPReqUserAllowed,0,  &allowed, &len);
						printf("Module result for qtssRTSPReqUserAllowed = %d err = %ld \n",allowed,theErr);
					}
					#endif
					
					if (!allowed) 
						break;

				}
				
				this->SaveRequestAuthorizationParams(fRequest);

				if (!allowed) 
				{
					if (false == fRequest->HasResponseBeenSent())
					{
						QTSS_AuthScheme challengeScheme = fRequest->GetAuthScheme();
                                                				
						if(challengeScheme == qtssAuthBasic) {
							fRTPSession->SetAuthScheme(qtssAuthBasic);
							theErr = fRequest->SendBasicChallenge();
						}
						else if(challengeScheme == qtssAuthDigest) {
							fRTPSession->UpdateDigestAuthChallengeParams(false, false, RTSPSessionInterface::kNoQop);
							theErr = fRequest->SendDigestChallenge(fRTPSession->GetAuthQop(), fRTPSession->GetAuthNonce(), fRTPSession->GetAuthOpaque());
						}
						else {
							// No authentication scheme is given and the request was not allowed,
							// so send a 403: Forbidden message
							theErr = fRequest->SendForbiddenResponse();
						}
						if (QTSS_NoErr != theErr) // We had an error so bail on the request quit the session and post process the request.
						{	
							fRequest->SetResponseKeepAlive(false);
							fCurrentModule = 0;
							fState = kPostProcessingRequest;
							//if (fRTPSession) fRTPSession->Teardown(); // make sure the RTP Session is ended and logged
							break;
							
						}					
					}
					//if (fRTPSession) fRTPSession->Teardown(); // close RTP Session and log
				}
					
				fCurrentModule = 0;
				if (fRequest->HasResponseBeenSent())
				{
					fState = kPostProcessingRequest;
					break;
				}

				// Prepare for kPreprocessingRequest state.
				fState = kPreprocessingRequest;

				if (fRequest->GetMethod() == qtssSetupMethod)
					// Make sure to erase the session ID stored in the request at this point.
					// If we fail to do so, this same session would be used if another
					// SETUP was issued on this same TCP connection.
					fLastRTPSessionIDPtr.Len = 0;
			}
			
			case kPreprocessingRequest:
			{
				// Invoke preprocessor modules
				numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPPreProcessorRole);
				{
					// Manipulation of the RTPSession from the point of view of
					// a module is guarenteed to be atomic by the API.
					Assert(fRTPSession != NULL);
					OSMutexLocker	locker(fRTPSession->GetSessionMutex());
						
					for (; (fCurrentModule < numModules) && ((!fRequest->HasResponseBeenSent()) || fModuleState.eventRequested); fCurrentModule++)
					{
						fModuleState.eventRequested = false;
						fModuleState.idleTime = 0;
						if (fModuleState.globalLockRequested )
						{	fModuleState.globalLockRequested = false;
							fModuleState.isGlobalLocked = true;
						} 
						
						theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPPreProcessorRole, fCurrentModule);
						(void)theModule->CallDispatch(QTSS_RTSPPreProcessor_Role, &fRoleParams);
						fModuleState.isGlobalLocked = false;

						// The way the API is set up currently, the first module that adds a stream
						// to the session is responsible for sending RTP packets for the session.
						if (fRTPSession->HasAnRTPStream() && (fRTPSession->GetPacketSendingModule() == NULL))
							fRTPSession->SetPacketSendingModule(theModule);
												
						if (fModuleState.globalLockRequested == true) // call this request back locked
							return this->CallLocked();

						// If this module has requested an event, return and wait for the event to transpire
						if (fModuleState.eventRequested == true)
						{
							this->ForceSameThread(); 	// We are holding mutexes, so we need to force
														// the same thread to be used for next Run()
							return fModuleState.idleTime; // If the module has requested idle time...
						}
					}
				}
				fCurrentModule = 0;
				if (fRequest->HasResponseBeenSent())
				{
					fState = kPostProcessingRequest;
					break;
				}
				fState = kProcessingRequest;
			}

			case kProcessingRequest:
			{
				// If no preprocessor sends a response, move onto the request processing module. It
				// is ALWAYS supposed to send a response, but if it doesn't, we have a canned error
				// to send back.
				fModuleState.eventRequested = false;
				fModuleState.idleTime = 0;
				if (QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPRequestRole) > 0)
				{
					// Manipulation of the RTPSession from the point of view of
					// a module is guarenteed to be atomic by the API.
					Assert(fRTPSession != NULL);
					OSMutexLocker	locker(fRTPSession->GetSessionMutex());
						
					if (fModuleState.globalLockRequested )
					{	fModuleState.globalLockRequested = false;
						fModuleState.isGlobalLocked = true;
					} 

					theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPRequestRole, 0);
					(void)theModule->CallDispatch(QTSS_RTSPRequest_Role, &fRoleParams);
					fModuleState.isGlobalLocked = false;

					// Do the same check as above for the preprocessor
					if (fRTPSession->HasAnRTPStream() && fRTPSession->GetPacketSendingModule() == NULL)
						fRTPSession->SetPacketSendingModule(theModule);
						
					if (fModuleState.globalLockRequested == true) // call this request back locked
						return this->CallLocked();

					// If this module has requested an event, return and wait for the event to transpire
					if (fModuleState.eventRequested == true)
					{
						this->ForceSameThread(); 	// We are holding mutexes, so we need to force
													// the same thread to be used for next Run()
						return fModuleState.idleTime; // If the module has requested idle time...
					}
				}
				
				if (!fRequest->HasResponseBeenSent())
					QTSSModuleUtils::SendErrorResponse(fRequest, qtssServerInternal,
														qtssMsgNoModuleForRequest);

				fState = kPostProcessingRequest;
			}

			case kPostProcessingRequest:
			{
				// Post process the request *before* sending the response. Therefore, we
				// will post process regardless of whether the client actually gets our response
				// or not.
				
				//if this is not a keepalive request, we should kill the session NOW
				fLiveSession = fRequest->GetResponseKeepAlive();
				
				if (fRTPSession != NULL)
				{
					// Invoke postprocessor modules only if there is an RTP session. We do NOT want
					// postprocessors running when filters or syntax errors have occurred in the request!
					numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPPostProcessorRole);
					{
						// Manipulation of the RTPSession from the point of view of
						// a module is guarenteed to be atomic by the API.
						OSMutexLocker	locker(fRTPSession->GetSessionMutex());
	
						// Make sure the RTPSession contains a copy of the realStatusCode in this request
						UInt32 realStatusCode = RTSPProtocol::GetStatusCode(fRequest->GetStatus());
						(void) fRTPSession->SetValue(qtssCliRTSPReqRealStatusCode,(UInt32) 0,(void *) &realStatusCode, sizeof(realStatusCode), QTSSDictionary::kDontObeyReadOnly);

						// Make sure the RTPSession contains a copy of the qtssRTSPReqRespMsg in this request
						StrPtrLen* theRespMsg = fRequest->GetValue(qtssRTSPReqRespMsg);
						if (theRespMsg->Len > 0)
							(void)fRTPSession->SetValue(qtssCliRTSPReqRespMsg, 0, theRespMsg->Ptr, theRespMsg->Len, QTSSDictionary::kDontObeyReadOnly);
				
						// Set the current RTSP session for this RTP session.
						// We do this here because we need to make sure the SessionMutex
						// is grabbed while we do this. Only do this if the RTSP session
						// is still alive, of course.
						if (this->IsLiveSession())
							fRTPSession->UpdateRTSPSession(this);
					
						for (; (fCurrentModule < numModules) ||  (fModuleState.eventRequested == true) ; fCurrentModule++)
						{
							fModuleState.eventRequested = false;
							fModuleState.idleTime = 0;
							if (fModuleState.globalLockRequested )
							{	fModuleState.globalLockRequested = false;
								fModuleState.isGlobalLocked = true;
							} 
							
							theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPPostProcessorRole, fCurrentModule);
							(void)theModule->CallDispatch(QTSS_RTSPPostProcessor_Role, &fRoleParams);
							fModuleState.isGlobalLocked = false;
							
							if (fModuleState.globalLockRequested == true) // call this request back locked
								return this->CallLocked();
								
							// If this module has requested an event, return and wait for the event to transpire
							if (fModuleState.eventRequested == true)
							{
								this->ForceSameThread(); 	// We are holding mutexes, so we need to force
															// the same thread to be used for next Run()
								return fModuleState.idleTime; // If the module has requested idle time...
							}
						}
					}
				}
				fCurrentModule = 0;
				fState = kSendingResponse;
			}

			case kSendingResponse:
			{
				// Sending the RTSP response consists of making sure the
				// RTSP request output buffer is completely flushed to the socket.
				Assert(fRequest != NULL);
					
				// If x-dynamic-rate header is sent with a value of 1, send OPTIONS request
				if ((fRequest->GetMethod() == qtssSetupMethod) && (fRequest->GetStatus() == qtssSuccessOK)
				    && (fRequest->GetDynamicRateState() == 1) && fRoundTripTimeCalculation)
				{
					this->SaveOutputStream();
					this->ResetOutputStream();
					this->SendOptionsRequest();
				}
			
				if (fSentOptionsRequest && (fRequest->GetMethod() == qtssIllegalMethod))
				{
					this->ResetOutputStream();
					this->RevertOutputStream();
					fSentOptionsRequest = false;
				}
				
				err = fOutputStream.Flush();
				
				if (err == EAGAIN)
				{
					// If we get this error, we are currently flow-controlled and should
					// wait for the socket to become writeable again
					fSocket.RequestEvent(EV_WR);
					this->ForceSameThread(); 	// We are holding mutexes, so we need to force
												// the same thread to be used for next Run()
					return 0;
				}
				else if (err != QTSS_NoErr)
				{
					// Any other error means that the client has disconnected, right?
					Assert(!this->IsLiveSession());
					break;
				}
			
				fState = kCleaningUp;
			}
			
			case kCleaningUp:
			{
				// Cleaning up consists of making sure we've read all the incoming Request Body
				// data off of the socket
				if (this->GetRemainingReqBodyLen() > 0)
				{
					err = this->DumpRequestData();
					
					if (err == EAGAIN)
					{
						fInputSocketP->RequestEvent(EV_RE);
						this->ForceSameThread(); 	// We are holding mutexes, so we need to force
													// the same thread to be used for next Run()
						return 0;
					}
				}
					
				// If we've gotten here, we've flushed all the data. Cleanup,
				// and wait for our next request!
				this->CleanupRequest();
				fState = kReadingRequest;
			}
		}
	}
	
	// Make absolutely sure there are no resources being occupied by the session
	// at this point.
	this->CleanupRequest();

	// Only delete if it is ok to delete!
	if (fObjectHolders == 0)
		return -1;

	// If we are here because of a timeout, but we can't delete because someone
	// is holding onto a reference to this session, just reschedule the timeout.
	//
	// At this point, however, the session is DEAD.
	return 0;
}



Bool16 RTSPSession::ParseProxyTunnelHTTP()
{
	/*
		if it's an HTTP request
		parse the interesing parts from the request
		
		- check for GET or POST, set fHTTPMethod
		- checck for HTTP protocol, set fWasHTTPRequest
		- check for SessionID header, set fProxySessionID char array
		- check for accept "application/x-rtsp-tunnelled.
		
	*/
	
	Bool16			isHTTPRequest = false;
	StrPtrLen		*splRequest;
	
	HTTP_VTRACE( "ParseProxyTunnelHTTP\n" )	
	splRequest = fInputStream.GetRequestBuffer();
	
	
	fFoundValidAccept = true;
	
	Assert( splRequest );
	
	if ( splRequest )
	{
		
		fHTTPMethod = kHTTPMethodUnknown;
	
	#if __RTSP_HTTP_DEBUG__	
		{
			char	buff[1024];
			
			memcpy( buff, splRequest->Ptr, splRequest->Len );
			
			buff[ splRequest->Len] = 0;
			
			HTTP_VTRACE( buff )
		}
	#endif

		StrPtrLen 		theParsedData;
		StringParser 	parser(splRequest);
		
		parser.ConsumeWord(&theParsedData);
		
		HTTP_VTRACE( "request method: \n" )
		HTTP_VTRACE_SPL( &theParsedData )
		
		// does first line begin with POST or GET?
		if (theParsedData.EqualIgnoreCase("post", 4 ))
		{	
			fHTTPMethod = kHTTPMethodPost;
		}
		else if (theParsedData.EqualIgnoreCase("get", 3 ))
		{
	
			fHTTPMethod = kHTTPMethodGet;
		}
		
		if ( fHTTPMethod != kHTTPMethodUnknown )
		{
			HTTP_VTRACE( "IsAHTTPProxyTunnelPostRequest found POST or GET\n" )
			parser.ConsumeWhitespace();	// skip over ws past method

			parser.ConsumeUntilWhitespace( &theParsedData ); // theParsedData now contains the URL and CGI params ( we don't need yet );
			
			parser.ConsumeWhitespace();	// skip over ws past url
			
			parser.ConsumeWord(&theParsedData);	// now should contain "HTTP"
			
			HTTP_VTRACE( "should be HTTP/1.* next: \n" )
			HTTP_VTRACE_SPL( &theParsedData )
			
			// DMS - why use NumEqualIgnoreCase? Wouldn't EqualIgnoreCase do the trick here?
			if (theParsedData.NumEqualIgnoreCase("http", 4 ))
			{	HTTP_TRACE( "ParseProxyTunnelHTTP found HTTP\n" )
				fWasHTTPRequest = true;
			}
		
		}
		
		
		if ( fWasHTTPRequest )
		{
			// it's HTTP and one of the methods we like....
			// now, find the Session ID and Accept headers
			const char* kSessionHeaderName = "X-SessionCookie:";
			const int	kSessionHeaderNameLen = ::strlen(kSessionHeaderName);
			const char* kAcceptHeaderName = "Accept:";
			const int 	kAcceptHeaderNameLen = ::strlen(kAcceptHeaderName);
			//const char* kAcceptData = "application/x-rtsp-tunnelled";
			//const int	kAcceptDataLen = ::strlen(kAcceptData);
			
			while ( parser.GetDataRemaining() > 0 )
			{
				parser.GetThruEOL( &theParsedData ); // we don't need this, but there is not a ComsumeThru...
			
				parser.ConsumeUntilWhitespace( &theParsedData ); // theParsedData now contains the URL and CGI params ( we don't need yet );
			
				if ( theParsedData.EqualIgnoreCase( kSessionHeaderName, kSessionHeaderNameLen ) )
				{
					// we got a weener!
					if ( parser.GetDataRemaining() > 0 )
						parser.ConsumeWhitespace();
					
					if ( parser.GetDataRemaining() > 0 )
					{	
						StrPtrLen	sessionID;
						
						parser.ConsumeUntil( &sessionID, StringParser::sEOLMask );
					
						// cache the ID so we can use it to remove ourselves from the map
						if ( sessionID.Len < QTSS_MAX_SESSION_ID_LENGTH )
						{	
							::memcpy( fProxySessionID, sessionID.Ptr,  sessionID.Len );
							fProxySessionID[sessionID.Len] = 0;
							fProxySessionIDPtr.Set( fProxySessionID, ::strlen(fProxySessionID) );
							HTTP_VTRACE_ONE( "found session id: %s\n", fProxySessionID )
						}
					}
				}
				else if ( theParsedData.EqualIgnoreCase( kAcceptHeaderName, kAcceptHeaderNameLen ) )
				{
					StrPtrLen	hTTPAcceptHeader;
					
					// we got another weener!
					if ( parser.GetDataRemaining() > 0 )
						parser.ConsumeWhitespace();
					
					if ( parser.GetDataRemaining() > 0 )
					{	
						parser.ConsumeUntil( &hTTPAcceptHeader, StringParser::sEOLMask );			
						
						#if __RTSP_HTTP_DEBUG__	
						{
							char	buff[1024];
							
							memcpy( buff, hTTPAcceptHeader.Ptr, hTTPAcceptHeader.Len );
							
							buff[ hTTPAcceptHeader.Len] = 0;
							
							HTTP_VTRACE_ONE( "client will accept: %s\n", buff )
						}
						#endif
							
						// we really don't need to check thisif ( theParsedData.EqualIgnoreCase( kAcceptData, kAcceptDataLen ) ) 
						{	fFoundValidAccept = true;
							
							HTTP_VTRACE( "found valid accept\n" )
						}
					
					}
						
				}
			}			
		
		}
		
	}
	
	// we found all that we were looking for
	if ( fFoundValidAccept && *fProxySessionID  && fWasHTTPRequest )
		isHTTPRequest = true;
		
	return isHTTPRequest;
	
}

/*

	"pre" filter the request looking for the HHTP Proxy
	tunnel HTTP requests, merge the 2 sessions
	into one, let the donor Session die.
	

*/

QTSS_Error RTSPSession::PreFilterForHTTPProxyTunnel()
{
	// returns true if it's an HTTP request that can tunnel
	if (!this->ParseProxyTunnelHTTP())
		return QTSS_NoErr;
	
	// This is an RTSP / HTTP session, so decrement the total RTSP sessions
	// and increment the total HTTP sessions
	Assert(fSessionType == qtssRTSPSession);
	QTSServerInterface::GetServer()->SwapFromRTSPToHTTP();
	
	// Setup our ProxyTunnel OSRefTable Ref
	Assert( fProxySessionIDPtr.Len > 0 );
	fProxyRef.Set(fProxySessionIDPtr, this);

	// We have to set this here, because IF we are able to put ourselves in the map,
	// the GET may arrive immediately after, and the GET checks this state.
	fState = kWaitingToBindHTTPTunnel;
	QTSS_RTSPSessionType theOtherSessionType = qtssRTSPSession;

	if ( fHTTPMethod == kHTTPMethodPost )
	{
		HTTP_TRACE( "RTSPSession is a POST request.\n" )
		fSessionType 			= qtssRTSPHTTPInputSession;
		theOtherSessionType 	= qtssRTSPHTTPSession;
	}
	else if ( fHTTPMethod == kHTTPMethodGet )
	{
		HTTP_TRACE( "RTSPSession is a GET request.\n" )
		// we're session O (outptut)  the POST half is session 1 ( input )
		fSessionType 			= qtssRTSPHTTPSession;	
		theOtherSessionType 	= qtssRTSPHTTPInputSession;

		if ( fDoReportHTTPConnectionAddress )
		{	// contruct a 200 OK header with an "x-server-ip-address" header
		
			char		responseHeaderBuf[kMaxHTTPResponseLen];
			char 		localIPAddrBuf[20] = { 0 };
			StrPtrLen 	localIPAddr(localIPAddrBuf, 19);
			
			// get a copy of the local IP address from the dictionary
			this->GetValue(qtssRTSPSesLocalAddrStr, 0, localIPAddr.Ptr, &localIPAddr.Len);
			Assert( localIPAddr.Len < sizeof( localIPAddrBuf ) );
			localIPAddrBuf[localIPAddr.Len] = 0;
			
			::sprintf( responseHeaderBuf, sHTTPResponseFormatStr, "X-server-ip-address: ", localIPAddrBuf, "\r\n", QTSServerInterface::GetServerHeader().Ptr );
		
			Assert(::strlen(responseHeaderBuf) < kMaxHTTPResponseLen);
			fOutputStream.Put(responseHeaderBuf); 
			

		}
		else // use the premade stopck version
		{	fOutputStream.Put(sHTTPResponseHeaderPtr); 	// 200 OK just means we connected...

		}
	}	
	else
		Assert(0);
		
	// This function attempts to register our Ref into the map. If there is another
	// session with a matching magic number, it resolves it and returns that Ref.
	// If it returns NULL, something bad has happened, and we should just kill the session.
	OSRef* rtspSessionRef = this->RegisterRTSPSessionIntoHTTPProxyTunnelMap(theOtherSessionType);

	// Something went wrong (usually we get here because there is a session with this magic
	// number, and that session is currently doing something
	if (rtspSessionRef == NULL)
	{
		HTTP_TRACE("RegisterRTSPSessionIntoHTTPProxyTunnelMap returned NULL. Abort.\n");
		return QTSS_RequestFailed;
	}

	// We registered ourselves into the map (we are the first half), so wait for our other half
	if (rtspSessionRef == &fProxyRef)
	{
		HTTP_TRACE("Registered this session into map. Waiting to bind\n");
		return QTSS_NoErr;
	}
	
	OSRefReleaser theRefReleaser(sHTTPProxyTunnelMap, rtspSessionRef); // auto release this ref
	RTSPSession* theOtherSession = (RTSPSession*)theRefReleaser.GetRef()->GetObject();

	// We must lock down this session, for we (may) be manipulating its socket & input
	// stream, and therefore it cannot be in the process of reading data or processing a request.
	// If it is, well, safest thing to do is probably just deny this attempt to bind.
	if (!theOtherSession->fReadMutex.TryLock())
	{
		HTTP_TRACE("Found another session in map, but couldn't grab fReadMutex. Abort.\n");
		return QTSS_RequestFailed;
	}
	
	if (fHTTPMethod == kHTTPMethodPost)
	{
		// take the input session's socket. This also grabs the other session's input stream
		theOtherSession->SnarfInputSocket(this);

		// Attempt to bind to this GET connection
		// this will reset our state on success.
		HTTP_TRACE_ONE( "RTSPSession POST snarfed a donor session successfuly (%s).\n", fProxySessionID )
		fState = kSocketHasBeenBoundIntoHTTPTunnel;
		theOtherSession->fState = kReadingRequest;
		theOtherSession->Signal(Task::kReadEvent);
	}
	else if (fHTTPMethod == kHTTPMethodGet)
	{
		Assert( theOtherSession->fState == kWaitingToBindHTTPTunnel );
		HTTP_TRACE_ONE( "RTSPSession GET snarfed a donor session successfuly (%s).\n", fProxySessionID )

		// take the input session's socket. This also grabs the other session's input stream
		this->SnarfInputSocket(theOtherSession);

		// we assume the donor's place in the map.
		sHTTPProxyTunnelMap->Swap( &fProxyRef );

		// the 1/2 connections are bound
		// the output Session state goes back to reading a request, this time an RTSP request
		// the socket donor Session(rtspSessionInput) state goes to kSocketHasBeenBoundIntoHTTPTunnel to die
		fState = kReadingRequest;
		theOtherSession->fState = kSocketHasBeenBoundIntoHTTPTunnel;
		theOtherSession->Signal(Task::kKillEvent);
	}
	
	theOtherSession->fReadMutex.Unlock();
	return QTSS_NoErr;
}

OSRef* RTSPSession::RegisterRTSPSessionIntoHTTPProxyTunnelMap(QTSS_RTSPSessionType inSessionType)
{
	// This function attempts to register the current session's fProxyRef into the map, and
	// 1) returns the current session's fProxyRef if registration was successful
	// 2) returns another session's fProxyRef if it has the same magic number and is the right sessionType
	// 3) returns NULL if there is a session with the same magic # but it couldn't be resolved.
	
	OSMutexLocker locker(sHTTPProxyTunnelMap->GetMutex());
	OSRef* theRef = sHTTPProxyTunnelMap->RegisterOrResolve(&fProxyRef);
	if (theRef == NULL)
		return &fProxyRef;
		
	RTSPSession* rtspSession = (RTSPSession*)theRef->GetObject();
	
	// we can be the only user of the object right now
	Assert(theRef->GetRefCount() > 0);
	if (theRef->GetRefCount() > 1 || rtspSession->fSessionType != inSessionType)
	{
		sHTTPProxyTunnelMap->Release(theRef);
		theRef = NULL;
	}
	return theRef;
}

void RTSPSession::CheckAuthentication() {
	
	QTSSUserProfile* profile = fRequest->GetUserProfile();
	StrPtrLen* userPassword = profile->GetValue(qtssUserPassword);
	QTSS_AuthScheme scheme = fRequest->GetAuthScheme();
	Bool16 authenticated = true;
	
	// Check if authorization information returned by the client is for the scheme that the server sent the challenge
	if(scheme != (fRTPSession->GetAuthScheme())) {
		authenticated = false;
	}
	else if(scheme == qtssAuthBasic) {	
		// For basic authentication, the authentication module returns the crypt of the password, 
		// so compare crypt of qtssRTSPReqUserPassword and the text in qtssUserPassword
		StrPtrLen* reqPassword = fRequest->GetValue(qtssRTSPReqUserPassword);
		char* userPasswdStr = userPassword->GetAsCString();	// memory allocated
		char* reqPasswdStr = reqPassword->GetAsCString();	// memory allocated
		
		if(userPassword->Len == 0)
		{
		      authenticated = false;
		}
                else
		{
#ifdef __Win32__
		  // The password is md5 encoded for win32
		  char md5EncodeResult[120];
		  // no memory is allocated in this function call
		  MD5Encode(reqPasswdStr, userPasswdStr, md5EncodeResult, sizeof(md5EncodeResult));
		  if(::strcmp(userPasswdStr, md5EncodeResult) != 0)
		    authenticated = false;
#else
		  if(::strcmp(userPasswdStr, (char*)crypt(reqPasswdStr, userPasswdStr)) != 0)
		    authenticated = false;
#endif
		}

		delete [] userPasswdStr;	// deleting allocated memory
		userPasswdStr = NULL;
		delete [] reqPasswdStr;
		reqPasswdStr = NULL;		// deleting allocated memory
	}
	else if(scheme == qtssAuthDigest) { // For digest authentication, md5 digest comparison
		// The text returned by the authentication module in qtssUserPassword is MD5 hash of (username:realm:password)
		
		UInt32 qop = fRequest->GetAuthQop();
		StrPtrLen* opaque = fRequest->GetAuthOpaque();
		StrPtrLen* sessionOpaque = fRTPSession->GetAuthOpaque();
		UInt32 sessionQop = fRTPSession->GetAuthQop();
		
		do {
			// The Opaque string should be the same as that sent by the server
			// The QoP should be the same as that sent by the server
			if((sessionOpaque->Len != 0) && !(sessionOpaque->Equal(*opaque))) {
				authenticated = false;
				break;
			}
			
			if(sessionQop != qop) {
				authenticated = false;
				break;
			}
			
			// All these are just pointers to existing memory... no new memory is allocated
			//StrPtrLen* userName = profile->GetValue(qtssUserName);
			//StrPtrLen* realm = fRequest->GetAuthRealm();
			StrPtrLen* nonce = fRequest->GetAuthNonce();
			StrPtrLen method = RTSPProtocol::GetMethodString(fRequest->GetMethod());
			StrPtrLen* digestUri = fRequest->GetAuthUri();
			StrPtrLen* responseDigest = fRequest->GetAuthResponse();
			//StrPtrLen hA1;
			StrPtrLen requestDigest;
			StrPtrLen emptyStr;
			
			StrPtrLen* cNonce = fRequest->GetAuthCNonce();
			// Since qtssUserPassword = md5(username:realm:password)
			// Just convert the 16 bit hash to a 32 bit char array to get HA1
			//HashToString((unsigned char *)userPassword->Ptr, &hA1);
			//CalcHA1(&sAuthAlgorithm, userName, realm, userPassword, nonce, cNonce, &hA1);
			
			
			// For qop="auth"
			if(qop ==  RTSPSessionInterface::kAuthQop) {
				StrPtrLen* nonceCount = fRequest->GetAuthNonceCount();
				// Convert nounce count (which is a string of 8 hex digits) into a UInt32
				UInt32 ncValue, i, pos = 1;
				ncValue = (nonceCount->Ptr)[nonceCount->Len - 1];
				for(i = (nonceCount->Len - 1); i > 0; i--) {
					pos *= 16;
					ncValue += (nonceCount->Ptr)[i - 1] * pos;
				}
				// nonce count must not be repeated by the client
				if(ncValue < (fRTPSession->GetAuthNonceCount())) { 
					authenticated = false;
					break;
				}
				
				// allocates memory for requestDigest.Ptr
				CalcRequestDigest(userPassword, nonce, nonceCount, cNonce, &sAuthQop, &method, digestUri, &emptyStr, &requestDigest);
				// If they are equal, check if nonce used by client is same as the one sent by the server
				
			}	// For No qop
			else if(qop == RTSPSessionInterface::kNoQop) 
			{
				// allocates memory for requestDigest->Ptr
				CalcRequestDigest(userPassword, nonce, &emptyStr, &emptyStr, &emptyStr, &method, digestUri, &emptyStr, &requestDigest);				
			}
			
			// hA1 is allocated memory 
			//delete [] hA1.Ptr;
			
			if(responseDigest->Equal(requestDigest)) {
				if(!(nonce->Equal(*(fRTPSession->GetAuthNonce()))))
					fRequest->SetStale(true);
				authenticated = true;
			}
			else { 
				authenticated = false;
			}
			
			// delete the memory allocated in CalcRequestDigest above 
			delete [] requestDigest.Ptr;
			requestDigest.Len = 0;
			
		} while(false); 			
	}
	
	// If authenticaton failed, set qtssUserName in the qtssRTSPReqUserProfile attribute
	// to NULL.
	if((!authenticated) || (authenticated && (fRequest->GetStale()))) {
		(void)profile->SetValue(qtssUserName, 0,  sEmptyStr.Ptr, sEmptyStr.Len, QTSSDictionary::kDontObeyReadOnly);
	}
}

Bool16 RTSPSession::ParseOptionsResponse()
{
	StringParser parser(fRequest->GetValue(qtssRTSPReqFullRequest));
	Assert(fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr != NULL);
	static StrPtrLen sRTSPStr("RTSP", 4);
	StrPtrLen theProtocol;
	parser.ConsumeLength(&theProtocol, 4);
	
	return (theProtocol.Equal(sRTSPStr));
}

void RTSPSession::SetupRequest()
{
	//
	// First parse the request
	QTSS_Error theErr = fRequest->Parse();
	 
	if (theErr != QTSS_NoErr)
		return;
	
	//
	// If this is an OPTIONS request, don't even bother letting modules see it. Just
	// send a standard OPTIONS response, and bedone.
	if (fRequest->GetMethod() == qtssOptionsMethod)
	{
		StrPtrLen* cSeqPtr = fRequest->GetHeaderDictionary()->GetValue(qtssCSeqHeader);
		if (cSeqPtr == NULL || cSeqPtr->Len == 0)
		{	
			(void) QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest, qtssMsgBadFormat);
			return;
		}
			
		// If this is an options request, just return a very simple response
		fRequest->AppendHeader(qtssPublicHeader, QTSServerInterface::GetPublicHeader());
		fRequest->SendHeader();

		// let's also refresh RTP session timeout so that it's kept alive in sync with the RTSP session.
		//
		// Attempt to find the RTP session for this request.
		OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
		theErr = this->FindRTPSession(theMap);
		if (theErr != QTSS_NoErr)
			return;	// we don't care if one does not exist.
		
		if (fRTPSession != NULL)
			fRTPSession->RefreshTimeout();
			
		return;
	}

	//
	// If this is a SET_PARAMETER request, don't let modules see it.
	if (fRequest->GetMethod() == qtssSetParameterMethod)
	{
		// Check that it has the CSeq header
		StrPtrLen* cSeqPtr = fRequest->GetHeaderDictionary()->GetValue(qtssCSeqHeader);
		if (cSeqPtr == NULL || cSeqPtr->Len == 0)
		{	
			(void) QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest, qtssMsgBadFormat);
			return;
		}
	
		// Attempt to find the RTP session for this request.
		OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
		QTSS_Error theErr = this->FindRTPSession(theMap);
		if (theErr != QTSS_NoErr)
			return;
		
		// If the RTPSession doesn't exist, return error
		if (fRTPSession == NULL)
		{
			(void) QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientSessionNotFound, qtssMsgNoSessionID);
			return;
		}
		
		// refresh RTP session timeout so that it's kept alive in sync with the RTSP session.
		fRTPSession->RefreshTimeout();
		if (fRequest->GetLateToleranceInSec() != -1)
			fRTPSession->SetStreamThinningParams(fRequest->GetLateToleranceInSec());
		fRequest->SendHeader();

		return;
	}

	//
	// If this is a DESCRIBE request, make sure there is no SessionID. This is not allowed,
	// and may screw up modules if we let them see this request.
	if (fRequest->GetMethod() == qtssDescribeMethod)
	{
		if (fRequest->GetHeaderDictionary()->GetValue(qtssSessionHeader)->Len > 0)
		{
			(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientHeaderFieldNotValid, qtssMsgNoSesIDOnDescribe);
			return;
		}
	}
	
	//
	// Attempt to find the RTP session for this request.
	OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
	theErr = this->FindRTPSession(theMap);
	if (theErr != QTSS_NoErr)
		return;
	
	//
	// If we don't have an RTP session yet, create one...
	if (fRTPSession == NULL)
	{
		theErr = this->CreateNewRTPSession(theMap);
		if (theErr != QTSS_NoErr)
			return;
	}

	// If it's a play request and the 
	if ((fRequest->GetMethod() == qtssPlayMethod) && (fRequest->GetLateToleranceInSec() != -1))
		fRTPSession->SetStreamThinningParams(fRequest->GetLateToleranceInSec());
	
	//
	// Check to see if this is a "ping" PLAY request (a PLAY request while already
	// playing with no Range header). If so, just send back a 200 OK response and do nothing.
	// No need to go to modules to do this, because this is an RFC documented behavior	
	if ((fRequest->GetMethod() == qtssPlayMethod) && (fRTPSession->GetSessionState() == qtssPlayingState)
		&& (fRequest->GetStartTime() == -1) && (fRequest->GetStopTime() == -1))
	{
		fRequest->SendHeader();
		fRTPSession->RefreshTimeout();
		return;
	}

		
	Assert(fRTPSession != NULL); // At this point, we must have one!
	fRoleParams.rtspRequestParams.inClientSession = fRTPSession;
	
	// Setup Authorization params;
	fRequest->ParseAuthHeader();	
	
		
}

void RTSPSession::CleanupRequest()
{
	if (fRTPSession != NULL)
	{
		// Release the ref.
		OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
		theMap->Release(fRTPSession->GetRef());
		
		// NULL out any references to this RTP session
		fRTPSession = NULL;
		fRoleParams.rtspRequestParams.inClientSession = NULL;
	}
	
	if (fRequest != NULL)
	{
		// Check to see if a filter module has replaced the request. If so, delete
		// their request now.
		if (fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr != fInputStream.GetRequestBuffer()->Ptr)
			delete [] fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr;
			
		// NULL out any references to the current request
		delete fRequest;
		fRequest = NULL;
		fRoleParams.rtspRequestParams.inRTSPRequest = NULL;
		fRoleParams.rtspRequestParams.inRTSPHeaders = NULL;
	}
	
	fSessionMutex.Unlock();
	fReadMutex.Unlock();
	
	// Clear out our last value for request body length before moving onto the next request
	this->SetRequestBodyLength(-1);
}

QTSS_Error	RTSPSession::FindRTPSession(OSRefTable* inRefTable)
{
	// This function attempts to locate the appropriate RTP session for this RTSP
	// Request. It uses an RTSP session ID as a key to finding the correct RTP session,
	// and it looks for this session ID in two places. First, the RTSP session ID header
	// in the RTSP request, and if there isn't one there, in the RTSP session object itself.
	
	StrPtrLen* theSessionID = fRequest->GetHeaderDictionary()->GetValue(qtssSessionHeader);	
	if (theSessionID->Len > 0)
	{
		OSRef* theRef = inRefTable->Resolve(theSessionID);
		if (theRef == NULL)
			return QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientSessionNotFound,
														qtssMsgNoSessionID);
			
		Assert(fRTPSession == NULL);
		fRTPSession = (RTPSession*)theRef->GetObject();
	}
	
	// If there wasn't a session ID in the headers, look for one in the RTSP session itself
	else if (fLastRTPSessionIDPtr.Len > 0)
	{
		OSRef* theRef = inRefTable->Resolve(&fLastRTPSessionIDPtr);
		if (theRef != NULL)
			fRTPSession = (RTPSession*)theRef->GetObject();
	}
	
	return QTSS_NoErr;
}

QTSS_Error	RTSPSession::CreateNewRTPSession(OSRefTable* inRefTable)
{
	Assert(fLastRTPSessionIDPtr.Ptr == &fLastRTPSessionID[0]);

	// This is a brand spanking new session. At this point, we need to create
	// a new RTPSession object that will represent this session until it completes.
	// Then, we need to pass the session onto one of the modules

	// First of all, ask the server if it's ok to add a new session
	QTSS_Error theErr = this->IsOkToAddNewRTPSession();
	if (theErr != QTSS_NoErr)
		return theErr;

	// Create the RTPSession object
	Assert(fRTPSession == NULL);
	fRTPSession = NEW RTPSession();
	
	{
		//
		// Lock the RTP session down so that it won't delete itself in the
		// unusual event there is a timeout while we are doing this.
        OSMutexLocker locker(fRTPSession->GetSessionMutex());

		// Because this is a new RTP session, setup some dictionary attributes
		// pertaining to RTSP that only need to be set once
		this->SetupClientSessionAttrs();	
		
		// So, generate a session ID for this session
		QTSS_Error activationError = EPERM;
		while (activationError == EPERM)
		{
			fLastRTPSessionIDPtr.Len = this->GenerateNewSessionID(fLastRTPSessionID);
			
			//ok, some module has bound this session, we can activate it.
			//At this point, we may find out that this new session ID is a duplicate.
			//If that's the case, we'll simply retry until we get a unique ID
			activationError = fRTPSession->Activate(fLastRTPSessionID);
		}
		Assert(activationError == QTSS_NoErr);
	}
	Assert(fLastRTPSessionIDPtr.Ptr == &fLastRTPSessionID[0]);

	// Activate adds this session into the RTP session map. We need to therefore
	// make sure to resolve the RTPSession object out of the map, even though
	// we don't actually need to pointer.
	OSRef* theRef = inRefTable->Resolve(&fLastRTPSessionIDPtr);
	Assert(theRef != NULL);
	
	return QTSS_NoErr;
}

void RTSPSession::SetupClientSessionAttrs()
{
	// get and pass presentation url
	StrPtrLen* theValue = fRequest->GetValue(qtssRTSPReqURI);
	Assert(theValue != NULL);
	(void)fRTPSession->SetValue(qtssCliSesPresentationURL, 0, theValue->Ptr, theValue->Len, QTSSDictionary::kDontObeyReadOnly);
	
	// get and pass full request url
	theValue = fRequest->GetValue(qtssRTSPReqAbsoluteURL);
	Assert(theValue != NULL);
	(void)fRTPSession->SetValue(qtssCliSesFullURL, 0, theValue->Ptr, theValue->Len, QTSSDictionary::kDontObeyReadOnly);
	
	// get and pass request host name
	theValue = fRequest->GetHeaderDictionary()->GetValue(qtssHostHeader);
	Assert(theValue != NULL);	
	(void)fRTPSession->SetValue(qtssCliSesHostName, 0, theValue->Ptr, theValue->Len, QTSSDictionary::kDontObeyReadOnly);

	// get and pass user agent header
	theValue = fRequest->GetHeaderDictionary()->GetValue(qtssUserAgentHeader);
	Assert(theValue != NULL);	
	(void)fRTPSession->SetValue(qtssCliSesFirstUserAgent, 0, theValue->Ptr, theValue->Len, QTSSDictionary::kDontObeyReadOnly);

	// get and pass CGI params
	if (fRequest->GetMethod() == qtssDescribeMethod)
	{
		theValue = fRequest->GetValue(qtssRTSPReqQueryString);
		Assert(theValue != NULL);	
		(void)fRTPSession->SetValue(qtssCliSesReqQueryString, 0, theValue->Ptr, theValue->Len, QTSSDictionary::kDontObeyReadOnly);
	}
	
	// store RTSP session info in the RTPSession.	
	StrPtrLen tempStr;
	tempStr.Len = 0;
	(void) this->GetValuePtr(qtssRTSPSesRemoteAddrStr, (UInt32) 0, (void **) &tempStr.Ptr, &tempStr.Len);
	Assert(tempStr.Len != 0);	
	(void) fRTPSession->SetValue(qtssCliRTSPSessRemoteAddrStr, (UInt32) 0, tempStr.Ptr, tempStr.Len, QTSSDictionary::kDontObeyReadOnly );
	
	tempStr.Len = 0;
	(void) this->GetValuePtr(qtssRTSPSesLocalDNS, (UInt32) 0, (void **) &tempStr.Ptr, &tempStr.Len);
	Assert(tempStr.Len != 0);	
	(void) fRTPSession->SetValue(qtssCliRTSPSessLocalDNS, (UInt32) 0,  (void **)tempStr.Ptr, tempStr.Len, QTSSDictionary::kDontObeyReadOnly );

	tempStr.Len = 0;
	(void) this->GetValuePtr(qtssRTSPSesLocalAddrStr, (UInt32) 0, (void **) &tempStr.Ptr, &tempStr.Len);
	Assert(tempStr.Len != 0);	
	(void) fRTPSession->SetValue(qtssCliRTSPSessLocalAddrStr, (UInt32) 0, tempStr.Ptr, tempStr.Len, QTSSDictionary::kDontObeyReadOnly );
}

UInt32 RTSPSession::GenerateNewSessionID(char* ioBuffer)
{
	//RANDOM NUMBER GENERATOR
	
	//We want to make our session IDs as random as possible, so use a bunch of
	//current server statistics to generate a random SInt64.

	//Generate the random number in two UInt32 parts. The first UInt32 uses
	//statistics out of a random RTP session.
	SInt64 theMicroseconds = OS::Microseconds();
	::srand((unsigned int)theMicroseconds);
	UInt32 theFirstRandom = ::rand();
	
	QTSServerInterface* theServer = QTSServerInterface::GetServer();
	
	{
		OSMutexLocker locker(theServer->GetRTPSessionMap()->GetMutex());
		OSRefHashTable* theHashTable = theServer->GetRTPSessionMap()->GetHashTable();
		if (theHashTable->GetNumEntries() > 0)
		{
			theFirstRandom %= theHashTable->GetNumEntries();
			theFirstRandom >>= 2;
			
			OSRefHashTableIter theIter(theHashTable);
			//Iterate through the session map, finding a random session
			for (UInt32 theCount = 0; theCount < theFirstRandom; theIter.Next(), theCount++)
				Assert(!theIter.IsDone());
			
			RTPSession* theSession = (RTPSession*)theIter.GetCurrent()->GetObject();
			theFirstRandom += theSession->GetPacketsSent();
			theFirstRandom += (UInt32)theSession->GetSessionCreateTime();
			theFirstRandom += (UInt32)theSession->GetPlayTime();
			theFirstRandom += (UInt32)theSession->GetBytesSent();
		}
	}
	//Generate the first half of the random number
	::srand((unsigned int)theFirstRandom);
	theFirstRandom = ::rand();
	
	//Now generate the second half
	UInt32 theSecondRandom = ::rand();
	theSecondRandom += theServer->GetCurBandwidthInBits();
	theSecondRandom += theServer->GetAvgBandwidthInBits();
	theSecondRandom += theServer->GetRTPPacketsPerSec();
	theSecondRandom += (UInt32)theServer->GetTotalRTPBytes();
	theSecondRandom += theServer->GetTotalRTPSessions();
	
	::srand((unsigned int)theSecondRandom);
	theSecondRandom = ::rand();
	
	SInt64 theSessionID = (SInt64)theFirstRandom;
	theSessionID <<= 32;
	theSessionID += (SInt64)theSecondRandom;
	::sprintf(ioBuffer, "%"_64BITARG_"d", theSessionID);
	Assert(::strlen(ioBuffer) < QTSS_MAX_SESSION_ID_LENGTH);
	return ::strlen(ioBuffer);
}

QTSS_Error RTSPSession::IsOkToAddNewRTPSession()
{
	QTSServerInterface* theServer = QTSServerInterface::GetServer();
	QTSS_ServerState theServerState = theServer->GetServerState();
	
	//we may want to deny this connection for a couple of different reasons
	//if the server is refusing new connections
	if ((theServerState == qtssRefusingConnectionsState) ||
		(theServerState == qtssIdleState) ||
		(theServerState == qtssFatalErrorState) ||
		(theServerState == qtssShuttingDownState))
		return QTSSModuleUtils::SendErrorResponse(fRequest, qtssServerUnavailable,
													qtssMsgRefusingConnections);

	//if the max connection limit has been hit
	SInt32 maxConns = theServer->GetPrefs()->GetMaxConnections();
	if ((maxConns > -1) && (theServer->GetNumRTPSessions() >= (UInt32)maxConns))
		return QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientNotEnoughBandwidth,
													qtssMsgTooManyClients);

	//if the max bandwidth limit has been hit
	SInt32 maxKBits = theServer->GetPrefs()->GetMaxKBitsBandwidth();
	if ( (maxKBits > -1) && (theServer->GetCurBandwidthInBits() >= ((UInt32)maxKBits*1024)) )
		return QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientNotEnoughBandwidth,
													qtssMsgTooMuchThruput);

	//if the server is too loaded down (CPU too high, whatever)
	// --INSERT WORKING CODE HERE--
	
	return QTSS_NoErr;													
}


void RTSPSession::SaveRequestAuthorizationParams(RTSPRequest *theRTSPRequest)
{
	// Set the RTSP session's copy of the user name
	StrPtrLen* tempPtr = theRTSPRequest->GetValue(qtssRTSPReqUserName);
	Assert(tempPtr != NULL);
	if (tempPtr)
	{	(void)this->SetValue(qtssRTSPSesLastUserName, 0, tempPtr->Ptr, tempPtr->Len,QTSSDictionary::kDontObeyReadOnly);
		(void)fRTPSession->SetValue(qtssCliRTSPSesUserName, (UInt32) 0, tempPtr->Ptr, tempPtr->Len, QTSSDictionary::kDontObeyReadOnly );
	}
	
	// Same thing... user password
	tempPtr = theRTSPRequest->GetValue(qtssRTSPReqUserPassword);
	Assert(tempPtr != NULL);
	if (tempPtr)
	{	(void)this->SetValue(qtssRTSPSesLastUserPassword, 0, tempPtr->Ptr, tempPtr->Len,QTSSDictionary::kDontObeyReadOnly);
		(void)fRTPSession->SetValue(qtssCliRTSPSesUserPassword, (UInt32) 0, tempPtr->Ptr, tempPtr->Len, QTSSDictionary::kDontObeyReadOnly );
	}
	
	tempPtr = theRTSPRequest->GetValue(qtssRTSPReqURLRealm);
	if (tempPtr)
	{
		if (tempPtr->Len == 0)
		{
			// If there is no realm explicitly specified in the request, then let's get the default out of the prefs
			OSCharArrayDeleter theDefaultRealm(QTSServerInterface::GetServer()->GetPrefs()->GetAuthorizationRealm());
			char *realm = theDefaultRealm.GetObject();
			UInt32 len = ::strlen(theDefaultRealm.GetObject());
			(void)this->SetValue(qtssRTSPSesLastURLRealm, 0, realm, len,QTSSDictionary::kDontObeyReadOnly);
			(void)fRTPSession->SetValue(qtssCliRTSPSesURLRealm, (UInt32) 0,realm,len, QTSSDictionary::kDontObeyReadOnly );
		}
		else
		{
			(void)this->SetValue(qtssRTSPSesLastURLRealm, 0, tempPtr->Ptr, tempPtr->Len,QTSSDictionary::kDontObeyReadOnly);
			(void)fRTPSession->SetValue(qtssCliRTSPSesURLRealm, (UInt32) 0,tempPtr->Ptr,tempPtr->Len, QTSSDictionary::kDontObeyReadOnly );
		}
	}
}

QTSS_Error RTSPSession::DumpRequestData()
{
	char theDumpBuffer[2048];
	
	QTSS_Error theErr = QTSS_NoErr;
	while (theErr == QTSS_NoErr)
		theErr = this->Read(theDumpBuffer, 2048, NULL);
		
	return theErr;
}

void RTSPSession::HandleIncomingDataPacket()
{
	
	// Attempt to find the RTP session for this request.
	UInt8	packetChannel = (UInt8)fInputStream.GetRequestBuffer()->Ptr[1];
	StrPtrLen* theSessionID = this->GetSessionIDForChannelNum(packetChannel);
	
	if (theSessionID == NULL)
	{
		Assert(0);
		theSessionID = &fLastRTPSessionIDPtr;

	}
	
	OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
	OSRef* theRef = theMap->Resolve(theSessionID);
	
	if (theRef != NULL)
		fRTPSession = (RTPSession*)theRef->GetObject();

	if (fRTPSession == NULL)
		return;

	StrPtrLen packetWithoutHeaders(fInputStream.GetRequestBuffer()->Ptr + 4, fInputStream.GetRequestBuffer()->Len - 4);
	
	OSMutexLocker locker(fRTPSession->GetMutex());
	fRTPSession->RefreshTimeout();
	RTPStream* theStream = fRTPSession->FindRTPStreamForChannelNum(packetChannel);
	theStream->ProcessIncomingInterleavedData(packetChannel, this, &packetWithoutHeaders);

	//
	// We currently don't support async notifications from within this role
	QTSS_RoleParams packetParams;
	packetParams.rtspIncomingDataParams.inRTSPSession = this;
	
	packetParams.rtspIncomingDataParams.inClientSession = fRTPSession;
	packetParams.rtspIncomingDataParams.inPacketData = fInputStream.GetRequestBuffer()->Ptr;
	packetParams.rtspIncomingDataParams.inPacketLen = fInputStream.GetRequestBuffer()->Len;
	
	UInt32 numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPIncomingDataRole);
	for (; fCurrentModule < numModules; fCurrentModule++)
	{
		QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kRTSPIncomingDataRole, fCurrentModule);
		(void)theModule->CallDispatch(QTSS_RTSPIncomingData_Role, &packetParams);
	}
	fCurrentModule = 0;
}
