807 lines
34 KiB
C++
807 lines
34 KiB
C++
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The 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: RTSPRequestInterface.cp
|
||
|
|
||
|
Contains: Implementation of class defined in RTSPRequestInterface.h
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
//INCLUDES:
|
||
|
#ifndef __Win32__
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/uio.h>
|
||
|
#endif
|
||
|
|
||
|
#include "RTSPRequestInterface.h"
|
||
|
#include "RTSPSessionInterface.h"
|
||
|
#include "RTSPRequestStream.h"
|
||
|
|
||
|
#include "StringParser.h"
|
||
|
#include "OSMemory.h"
|
||
|
#include "OSThread.h"
|
||
|
#include "DateTranslator.h"
|
||
|
#include "QTSSDataConverter.h"
|
||
|
#include "OSArrayObjectDeleter.h"
|
||
|
#include "QTSSPrefs.h"
|
||
|
#include "QTSServerInterface.h"
|
||
|
|
||
|
char RTSPRequestInterface::sPremadeHeader[kStaticHeaderSizeInBytes];
|
||
|
StrPtrLen RTSPRequestInterface::sPremadeHeaderPtr(sPremadeHeader, kStaticHeaderSizeInBytes);
|
||
|
|
||
|
char RTSPRequestInterface::sPremadeNoHeader[kStaticHeaderSizeInBytes];
|
||
|
StrPtrLen RTSPRequestInterface::sPremadeNoHeaderPtr(sPremadeNoHeader, kStaticHeaderSizeInBytes);
|
||
|
|
||
|
|
||
|
StrPtrLen RTSPRequestInterface::sColonSpace(": ", 2);
|
||
|
|
||
|
QTSSAttrInfoDict::AttrInfo RTSPRequestInterface::sAttributes[] =
|
||
|
{ /*fields: fAttrName, fFuncPtr, fAttrDataType, fAttrPermission */
|
||
|
/* 0 */ { "qtssRTSPReqFullRequest", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 1 */ { "qtssRTSPReqMethodStr", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 2 */ { "qtssRTSPReqFilePath", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite },
|
||
|
/* 3 */ { "qtssRTSPReqURI", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 4 */ { "qtssRTSPReqFilePathTrunc", GetTruncatedPath, qtssAttrDataTypeCharArray, qtssAttrModeRead },
|
||
|
/* 5 */ { "qtssRTSPReqFileName", GetFileName, qtssAttrDataTypeCharArray, qtssAttrModeRead },
|
||
|
/* 6 */ { "qtssRTSPReqFileDigit", GetFileDigit, qtssAttrDataTypeCharArray, qtssAttrModeRead },
|
||
|
/* 7 */ { "qtssRTSPReqAbsoluteURL", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 8 */ { "qtssRTSPReqTruncAbsoluteURL", GetAbsTruncatedPath, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeCacheable },
|
||
|
/* 9 */ { "qtssRTSPReqMethod", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 10 */ { "qtssRTSPReqStatusCode", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 11 */ { "qtssRTSPReqStartTime", NULL, qtssAttrDataTypeFloat64, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 12 */ { "qtssRTSPReqStopTime", NULL, qtssAttrDataTypeFloat64, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 13 */ { "qtssRTSPReqRespKeepAlive", NULL, qtssAttrDataTypeBool16, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 14 */ { "qtssRTSPReqRootDir", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite },
|
||
|
/* 15 */ { "qtssRTSPReqRealStatusCode", GetRealStatusCode, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 16 */ { "qtssRTSPReqStreamRef", NULL, qtssAttrDataTypeQTSS_StreamRef, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
|
||
|
/* 17 */ { "qtssRTSPReqUserName", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 18 */ { "qtssRTSPReqUserPassword", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 19 */ { "qtssRTSPReqUserAllowed", NULL, qtssAttrDataTypeBool16, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 20 */ { "qtssRTSPReqURLRealm", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 21 */ { "qtssRTSPReqLocalPath", GetLocalPath, qtssAttrDataTypeCharArray, qtssAttrModeRead },
|
||
|
/* 22 */ { "qtssRTSPReqIfModSinceDate", NULL, qtssAttrDataTypeTimeVal, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 23 */ { "qtssRTSPReqQueryString", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 24 */ { "qtssRTSPReqRespMsg", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 25 */ { "qtssRTSPReqContentLen", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 26 */ { "qtssRTSPReqSpeed", NULL, qtssAttrDataTypeFloat32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 27 */ { "qtssRTSPReqLateTolerance", NULL, qtssAttrDataTypeFloat32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 28 */ { "qtssRTSPReqTransportType", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 29 */ { "qtssRTSPReqTransportMode", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 30 */ { "qtssRTSPReqSetUpServerPort", NULL, qtssAttrDataTypeUInt16, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite},
|
||
|
/* 31 */ { "qtssRTSPReqAction", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 32 */ { "qtssRTSPReqUserProfile", NULL, qtssAttrDataTypeQTSS_Object, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 33 */ { "qtssRTSPReqPrebufferMaxTime", NULL, qtssAttrDataTypeFloat32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 34 */ { "qtssRTSPReqAuthScheme", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 35 */ { "qtssRTSPReqSkipAuthorization", NULL, qtssAttrDataTypeBool16, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 36 */ { "qtssRTSPReqNetworkMode", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 37 */ { "qtssRTSPReqDynamicRateValue", NULL, qtssAttrDataTypeSInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 38 */ { "qtssRTSPReq3GPPRequestObject", NULL, qtssAttrDataTypeQTSS_Object,qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 39 */ { "qtssRTSPReqBandwidthBits", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 40 */ { "qtssRTSPReqUserFound", NULL, qtssAttrDataTypeBool16, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 41 */ { "qtssRTSPReqAuthHandled", NULL, qtssAttrDataTypeBool16, qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
|
||
|
/* 42 */ { "qtssRTSPReqDigestChallenge", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe },
|
||
|
/* 43 */ { "qtssRTSPReqDigestResponse", GetAuthDigestResponse, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
void RTSPRequestInterface::Initialize(void)
|
||
|
{
|
||
|
//make a partially complete header
|
||
|
StringFormatter headerFormatter(sPremadeHeaderPtr.Ptr, kStaticHeaderSizeInBytes);
|
||
|
PutStatusLine(&headerFormatter, qtssSuccessOK, RTSPProtocol::k10Version);
|
||
|
|
||
|
headerFormatter.Put(QTSServerInterface::GetServerHeader());
|
||
|
headerFormatter.PutEOL();
|
||
|
headerFormatter.Put(RTSPProtocol::GetHeaderString(qtssCSeqHeader));
|
||
|
headerFormatter.Put(sColonSpace);
|
||
|
sPremadeHeaderPtr.Len = headerFormatter.GetCurrentOffset();
|
||
|
Assert(sPremadeHeaderPtr.Len < kStaticHeaderSizeInBytes);
|
||
|
|
||
|
|
||
|
StringFormatter noServerInfoHeaderFormatter(sPremadeNoHeaderPtr.Ptr, kStaticHeaderSizeInBytes);
|
||
|
PutStatusLine(&noServerInfoHeaderFormatter, qtssSuccessOK, RTSPProtocol::k10Version);
|
||
|
noServerInfoHeaderFormatter.Put(RTSPProtocol::GetHeaderString(qtssCSeqHeader));
|
||
|
noServerInfoHeaderFormatter.Put(sColonSpace);
|
||
|
sPremadeNoHeaderPtr.Len = noServerInfoHeaderFormatter.GetCurrentOffset();
|
||
|
Assert(sPremadeNoHeaderPtr.Len < kStaticHeaderSizeInBytes);
|
||
|
|
||
|
//Setup all the dictionary stuff
|
||
|
for (UInt32 x = 0; x < qtssRTSPReqNumParams; x++)
|
||
|
QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kRTSPRequestDictIndex)->
|
||
|
SetAttribute(x, sAttributes[x].fAttrName, sAttributes[x].fFuncPtr,
|
||
|
sAttributes[x].fAttrDataType, sAttributes[x].fAttrPermission);
|
||
|
|
||
|
QTSSDictionaryMap* theHeaderMap = QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kRTSPHeaderDictIndex);
|
||
|
for (UInt32 y = 0; y < qtssNumHeaders; y++)
|
||
|
theHeaderMap->SetAttribute(y, RTSPProtocol::GetHeaderString(y).Ptr, NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe);
|
||
|
}
|
||
|
|
||
|
//CONSTRUCTOR / DESTRUCTOR: very simple stuff
|
||
|
RTSPRequestInterface::RTSPRequestInterface(RTSPSessionInterface *session)
|
||
|
: QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kRTSPRequestDictIndex)),
|
||
|
fMethod(qtssIllegalMethod),
|
||
|
fStatus(qtssSuccessOK),
|
||
|
fRealStatusCode(0),
|
||
|
fRequestKeepAlive(true),
|
||
|
//fResponseKeepAlive(true), //parameter need not be set
|
||
|
fVersion(RTSPProtocol::k10Version),
|
||
|
fStartTime(-1),
|
||
|
fStopTime(-1),
|
||
|
fClientPortA(0),
|
||
|
fClientPortB(0),
|
||
|
fTtl(0),
|
||
|
fDestinationAddr(0),
|
||
|
fSourceAddr(0),
|
||
|
fTransportType(qtssRTPTransportTypeUDP),
|
||
|
fNetworkMode(qtssRTPNetworkModeDefault),
|
||
|
fContentLength(0),
|
||
|
fIfModSinceDate(0),
|
||
|
fSpeed(0),
|
||
|
fLateTolerance(-1),
|
||
|
fPrebufferAmt(-1),
|
||
|
fWindowSize(0),
|
||
|
fMovieFolderPtr(&fMovieFolderPath[0]),
|
||
|
fHeaderDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kRTSPHeaderDictIndex)),
|
||
|
fAllowed(true),
|
||
|
fHasUser(false),
|
||
|
fAuthHandled(false),
|
||
|
fTransportMode(qtssRTPTransportModePlay),
|
||
|
fSetUpServerPort(0),
|
||
|
fAction(qtssActionFlagsNoFlags),
|
||
|
fAuthScheme(qtssAuthNone),
|
||
|
fAuthQop(RTSPSessionInterface::kNoQop),
|
||
|
fUserProfile(),
|
||
|
fUserProfilePtr(&fUserProfile),
|
||
|
fStale(false),
|
||
|
fSkipAuthorization(false),
|
||
|
fEnableDynamicRateState(-1),// -1 undefined, 0 disabled, 1 enabled
|
||
|
// DJM PROTOTYPE
|
||
|
fRandomDataSize(0),
|
||
|
fRequest3GPP( QTSServerInterface::GetServer()->GetPrefs()->Get3GPPEnabled() ),
|
||
|
fRequest3GPPPtr(&fRequest3GPP),
|
||
|
fBandwidthBits(0),
|
||
|
|
||
|
// private storage initializes after protected and public storage above
|
||
|
fSession(session),
|
||
|
fOutputStream(session->GetOutputStream()),
|
||
|
fStandardHeadersWritten(false) // private initializes after protected and public storage above
|
||
|
|
||
|
{
|
||
|
//Setup QTSS parameters that can be setup now. These are typically the parameters that are actually
|
||
|
//pointers to binary variable values. Because these variables are just member variables of this object,
|
||
|
//we can properly initialize their pointers right off the bat.
|
||
|
|
||
|
fStreamRef = this;
|
||
|
RTSPRequestStream* input = session->GetInputStream();
|
||
|
this->SetVal(qtssRTSPReqFullRequest, input->GetRequestBuffer()->Ptr, input->GetRequestBuffer()->Len);
|
||
|
this->SetVal(qtssRTSPReqMethod, &fMethod, sizeof(fMethod));
|
||
|
this->SetVal(qtssRTSPReqStatusCode, &fStatus, sizeof(fStatus));
|
||
|
this->SetVal(qtssRTSPReqRespKeepAlive, &fResponseKeepAlive, sizeof(fResponseKeepAlive));
|
||
|
this->SetVal(qtssRTSPReqStreamRef, &fStreamRef, sizeof(fStreamRef));
|
||
|
this->SetVal(qtssRTSPReqContentLen, &fContentLength, sizeof(fContentLength));
|
||
|
this->SetVal(qtssRTSPReqSpeed, &fSpeed, sizeof(fSpeed));
|
||
|
this->SetVal(qtssRTSPReqLateTolerance, &fLateTolerance, sizeof(fLateTolerance));
|
||
|
this->SetVal(qtssRTSPReqPrebufferMaxTime, &fPrebufferAmt, sizeof(fPrebufferAmt));
|
||
|
|
||
|
// Get the default root directory from QTSSPrefs, and store that in the proper parameter
|
||
|
// Note that the GetMovieFolderPath function may allocate memory, so we check for that
|
||
|
// in this object's destructor and free that memory if necessary.
|
||
|
UInt32 pathLen = kMovieFolderBufSizeInBytes;
|
||
|
fMovieFolderPtr = QTSServerInterface::GetServer()->GetPrefs()->GetMovieFolder(fMovieFolderPtr, &pathLen);
|
||
|
//this->SetVal(qtssRTSPReqRootDir, fMovieFolderPtr, pathLen);
|
||
|
this->SetValue(qtssRTSPReqRootDir, 0, fMovieFolderPtr, pathLen, QTSSDictionary::kDontObeyReadOnly);
|
||
|
|
||
|
//There are actually other attributes that point to member variables that we COULD setup now, but they are attributes that
|
||
|
//typically aren't set for every request, so we lazy initialize those when we parse the request
|
||
|
|
||
|
this->SetVal(qtssRTSPReqUserAllowed, &fAllowed, sizeof(fAllowed));
|
||
|
this->SetVal(qtssRTSPReqUserFound, &fHasUser, sizeof(fHasUser));
|
||
|
this->SetVal(qtssRTSPReqAuthHandled, &fAuthHandled, sizeof(fAuthHandled));
|
||
|
|
||
|
this->SetVal(qtssRTSPReqTransportType, &fTransportType, sizeof(fTransportType));
|
||
|
this->SetVal(qtssRTSPReqTransportMode, &fTransportMode, sizeof(fTransportMode));
|
||
|
this->SetVal(qtssRTSPReqSetUpServerPort, &fSetUpServerPort, sizeof(fSetUpServerPort));
|
||
|
this->SetVal(qtssRTSPReqAction, &fAction, sizeof(fAction));
|
||
|
this->SetVal(qtssRTSPReqUserProfile, &fUserProfilePtr, sizeof(fUserProfilePtr));
|
||
|
this->SetVal(qtssRTSPReqAuthScheme, &fAuthScheme, sizeof(fAuthScheme));
|
||
|
this->SetVal(qtssRTSPReqSkipAuthorization, &fSkipAuthorization, sizeof(fSkipAuthorization));
|
||
|
|
||
|
this->SetVal(qtssRTSPReqDynamicRateState, &fEnableDynamicRateState, sizeof(fEnableDynamicRateState));
|
||
|
|
||
|
this->SetVal(qtssRTSPReq3GPPRequestObject, &fRequest3GPPPtr, sizeof(fRequest3GPPPtr));
|
||
|
this->SetVal(qtssRTSPReqBandwidthBits, &fBandwidthBits, sizeof(fBandwidthBits));
|
||
|
|
||
|
this->SetVal(qtssRTSPReqDigestResponse, &fAuthDigestResponse, sizeof(fAuthDigestResponse));
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::AppendHeader(QTSS_RTSPHeader inHeader, StrPtrLen* inValue)
|
||
|
{
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
fOutputStream->Put(RTSPProtocol::GetHeaderString(inHeader));
|
||
|
fOutputStream->Put(sColonSpace);
|
||
|
fOutputStream->Put(*inValue);
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::PutStatusLine(StringFormatter* putStream, QTSS_RTSPStatusCode status,
|
||
|
RTSPProtocol::RTSPVersion version)
|
||
|
{
|
||
|
putStream->Put(RTSPProtocol::GetVersionString(version));
|
||
|
putStream->PutSpace();
|
||
|
putStream->Put(RTSPProtocol::GetStatusCodeAsString(status));
|
||
|
putStream->PutSpace();
|
||
|
putStream->Put(RTSPProtocol::GetStatusCodeString(status));
|
||
|
putStream->PutEOL();
|
||
|
}
|
||
|
|
||
|
|
||
|
void RTSPRequestInterface::AppendContentLength(UInt32 contentLength)
|
||
|
{
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
char dataSize[10];
|
||
|
dataSize[sizeof(dataSize) -1] = 0;
|
||
|
qtss_snprintf(dataSize, sizeof(dataSize) -1, "%"_U32BITARG_"", contentLength);
|
||
|
StrPtrLen contentLengthStr(dataSize);
|
||
|
this->AppendHeader(qtssContentLengthHeader, &contentLengthStr);
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::AppendDateAndExpires()
|
||
|
{
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
Assert(OSThread::GetCurrent() != NULL);
|
||
|
DateBuffer* theDateBuffer = OSThread::GetCurrent()->GetDateBuffer();
|
||
|
theDateBuffer->InexactUpdate(); // Update the date buffer to the current date & time
|
||
|
StrPtrLen theDate(theDateBuffer->GetDateBuffer(), DateBuffer::kDateBufferLen);
|
||
|
|
||
|
// Append dates, and have this response expire immediately
|
||
|
this->AppendHeader(qtssDateHeader, &theDate);
|
||
|
this->AppendHeader(qtssExpiresHeader, &theDate);
|
||
|
}
|
||
|
|
||
|
|
||
|
void RTSPRequestInterface::AppendSessionHeaderWithTimeout( StrPtrLen* inSessionID, StrPtrLen* inTimeout )
|
||
|
{
|
||
|
|
||
|
// Append a session header if there wasn't one already
|
||
|
if ( GetHeaderDictionary()->GetValue(qtssSessionHeader)->Len == 0)
|
||
|
{
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
static StrPtrLen sTimeoutString(";timeout=");
|
||
|
|
||
|
// Just write out the session header and session ID
|
||
|
if (inSessionID != NULL && inSessionID->Len > 0)
|
||
|
{
|
||
|
fOutputStream->Put( RTSPProtocol::GetHeaderString(qtssSessionHeader ) );
|
||
|
fOutputStream->Put(sColonSpace);
|
||
|
fOutputStream->Put( *inSessionID );
|
||
|
|
||
|
|
||
|
if ( inTimeout != NULL && inTimeout->Len != 0)
|
||
|
{
|
||
|
fOutputStream->Put( sTimeoutString );
|
||
|
fOutputStream->Put( *inTimeout );
|
||
|
}
|
||
|
|
||
|
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::PutTransportStripped(StrPtrLen &fullTransportHeader, StrPtrLen &fieldToStrip)
|
||
|
{
|
||
|
|
||
|
// skip the fieldToStrip and echo the rest back
|
||
|
UInt32 offset = (UInt32) (fieldToStrip.Ptr - fullTransportHeader.Ptr);
|
||
|
StrPtrLen transportStart(fullTransportHeader.Ptr,offset);
|
||
|
while (transportStart.Len > 0) // back up removing chars up to and including ;
|
||
|
{
|
||
|
transportStart.Len --;
|
||
|
if (transportStart[transportStart.Len] == ';')
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
StrPtrLen transportRemainder(fieldToStrip.Ptr,fullTransportHeader.Len - offset);
|
||
|
StringParser transportParser(&transportRemainder);
|
||
|
transportParser.ConsumeUntil(&fieldToStrip, ';'); //remainder starts with ;
|
||
|
transportRemainder.Set(transportParser.GetCurrentPosition(),transportParser.GetDataRemaining());
|
||
|
|
||
|
fOutputStream->Put(transportStart);
|
||
|
fOutputStream->Put(transportRemainder);
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::AppendTransportHeader(StrPtrLen* serverPortA,
|
||
|
StrPtrLen* serverPortB,
|
||
|
StrPtrLen* channelA,
|
||
|
StrPtrLen* channelB,
|
||
|
StrPtrLen* serverIPAddr,
|
||
|
StrPtrLen* ssrc)
|
||
|
{
|
||
|
static StrPtrLen sServerPortString(";server_port=");
|
||
|
static StrPtrLen sSourceString(";source=");
|
||
|
static StrPtrLen sInterleavedString(";interleaved=");
|
||
|
static StrPtrLen sSSRC(";ssrc=");
|
||
|
static StrPtrLen sInterLeaved("interleaved");//match the interleaved tag
|
||
|
static StrPtrLen sClientPort("client_port");
|
||
|
static StrPtrLen sClientPortString(";client_port=");
|
||
|
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
// Just write out the same transport header the client sent to us.
|
||
|
fOutputStream->Put(RTSPProtocol::GetHeaderString(qtssTransportHeader));
|
||
|
fOutputStream->Put(sColonSpace);
|
||
|
|
||
|
StrPtrLen outFirstTransport(fFirstTransport.GetAsCString());
|
||
|
OSCharArrayDeleter outFirstTransportDeleter(outFirstTransport.Ptr);
|
||
|
outFirstTransport.RemoveWhitespace();
|
||
|
while (outFirstTransport[outFirstTransport.Len - 1] == ';')
|
||
|
outFirstTransport.Len --;
|
||
|
|
||
|
// see if it contains an interleaved field or client port field
|
||
|
StrPtrLen stripClientPortStr;
|
||
|
StrPtrLen stripInterleavedStr;
|
||
|
(void) outFirstTransport.FindStringIgnoreCase(sClientPort, &stripClientPortStr);
|
||
|
(void) outFirstTransport.FindStringIgnoreCase(sInterLeaved, &stripInterleavedStr);
|
||
|
|
||
|
// echo back the transport without the interleaved or client ports fields we will add those in ourselves
|
||
|
if (stripClientPortStr.Len != 0)
|
||
|
PutTransportStripped(outFirstTransport, stripClientPortStr);
|
||
|
else if (stripInterleavedStr.Len != 0)
|
||
|
PutTransportStripped(outFirstTransport, stripInterleavedStr);
|
||
|
else
|
||
|
fOutputStream->Put(outFirstTransport);
|
||
|
|
||
|
|
||
|
//The source IP addr is optional, only append it if it is provided
|
||
|
if (serverIPAddr != NULL)
|
||
|
{
|
||
|
fOutputStream->Put(sSourceString);
|
||
|
fOutputStream->Put(*serverIPAddr);
|
||
|
}
|
||
|
|
||
|
// Append the client ports,
|
||
|
if (stripClientPortStr.Len != 0)
|
||
|
{
|
||
|
fOutputStream->Put(sClientPortString);
|
||
|
UInt16 portA = this->GetClientPortA();
|
||
|
UInt16 portB = this->GetClientPortB();
|
||
|
StrPtrLenDel clientPortA(QTSSDataConverter::ValueToString( &portA, sizeof(portA), qtssAttrDataTypeUInt16));
|
||
|
StrPtrLenDel clientPortB(QTSSDataConverter::ValueToString( &portB, sizeof(portB), qtssAttrDataTypeUInt16));
|
||
|
|
||
|
fOutputStream->Put(clientPortA);
|
||
|
fOutputStream->PutChar('-');
|
||
|
fOutputStream->Put(clientPortB);
|
||
|
}
|
||
|
|
||
|
// Append the server ports, if provided.
|
||
|
if (serverPortA != NULL)
|
||
|
{
|
||
|
fOutputStream->Put(sServerPortString);
|
||
|
fOutputStream->Put(*serverPortA);
|
||
|
fOutputStream->PutChar('-');
|
||
|
fOutputStream->Put(*serverPortB);
|
||
|
}
|
||
|
|
||
|
// Append channel #'s, if provided
|
||
|
if (channelA != NULL)
|
||
|
{
|
||
|
fOutputStream->Put(sInterleavedString);
|
||
|
fOutputStream->Put(*channelA);
|
||
|
fOutputStream->PutChar('-');
|
||
|
fOutputStream->Put(*channelB);
|
||
|
}
|
||
|
|
||
|
if (ssrc != NULL && ssrc->Ptr != NULL && ssrc->Len != 0 && fNetworkMode == qtssRTPNetworkModeUnicast && fTransportMode == qtssRTPTransportModePlay)
|
||
|
{
|
||
|
char* theCString = ssrc->GetAsCString();
|
||
|
OSCharArrayDeleter cStrDeleter(theCString);
|
||
|
|
||
|
UInt32 ssrcVal = 0;
|
||
|
::sscanf(theCString, "%"_U32BITARG_"", &ssrcVal);
|
||
|
ssrcVal = htonl(ssrcVal);
|
||
|
|
||
|
StrPtrLen hexSSRC(QTSSDataConverter::ValueToString( &ssrcVal, sizeof(ssrcVal), qtssAttrDataTypeUnknown));
|
||
|
OSCharArrayDeleter hexStrDeleter(hexSSRC.Ptr);
|
||
|
|
||
|
fOutputStream->Put(sSSRC);
|
||
|
fOutputStream->Put(hexSSRC);
|
||
|
}
|
||
|
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::AppendContentBaseHeader(StrPtrLen* theURL)
|
||
|
{
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
fOutputStream->Put(RTSPProtocol::GetHeaderString(qtssContentBaseHeader));
|
||
|
fOutputStream->Put(sColonSpace);
|
||
|
fOutputStream->Put(*theURL);
|
||
|
fOutputStream->PutChar('/');
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::AppendRetransmitHeader(UInt32 inAckTimeout)
|
||
|
{
|
||
|
static const StrPtrLen kAckTimeout("ack-timeout=");
|
||
|
|
||
|
fOutputStream->Put(RTSPProtocol::GetHeaderString(qtssXRetransmitHeader));
|
||
|
fOutputStream->Put(sColonSpace);
|
||
|
fOutputStream->Put(RTSPProtocol::GetRetransmitProtocolName());
|
||
|
fOutputStream->PutChar(';');
|
||
|
fOutputStream->Put(kAckTimeout);
|
||
|
fOutputStream->Put(inAckTimeout);
|
||
|
|
||
|
if (fWindowSizeStr.Len > 0)
|
||
|
{
|
||
|
//
|
||
|
// If the client provided a window size, append that as well.
|
||
|
fOutputStream->PutChar(';');
|
||
|
fOutputStream->Put(fWindowSizeStr);
|
||
|
}
|
||
|
|
||
|
fOutputStream->PutEOL();
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void RTSPRequestInterface::AppendRTPInfoHeader(QTSS_RTSPHeader inHeader,
|
||
|
StrPtrLen* url, StrPtrLen* seqNumber,
|
||
|
StrPtrLen* ssrc, StrPtrLen* rtpTime, Bool16 lastRTPInfo)
|
||
|
{
|
||
|
static StrPtrLen sURL("url=", 4);
|
||
|
static StrPtrLen sSeq(";seq=", 5);
|
||
|
static StrPtrLen sSsrc(";ssrc=", 6);
|
||
|
static StrPtrLen sRTPTime(";rtptime=", 9);
|
||
|
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
|
||
|
fOutputStream->Put(RTSPProtocol::GetHeaderString(inHeader));
|
||
|
if (inHeader != qtssSameAsLastHeader)
|
||
|
fOutputStream->Put(sColonSpace);
|
||
|
|
||
|
//Only append the various bits of RTP information if they actually have been
|
||
|
//providied
|
||
|
if ((url != NULL) && (url->Len > 0))
|
||
|
{
|
||
|
fOutputStream->Put(sURL);
|
||
|
|
||
|
if (true) //3gpp requires this and it follows RTSP RFC.
|
||
|
{
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)this;
|
||
|
StrPtrLen *path = (StrPtrLen *) theRequest->GetValue(qtssRTSPReqAbsoluteURL);
|
||
|
|
||
|
if (path != NULL && path->Len > 0)
|
||
|
{ fOutputStream->Put(*path);
|
||
|
if(path->Ptr[path->Len-1] != '/')
|
||
|
fOutputStream->PutChar('/');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fOutputStream->Put(*url);
|
||
|
}
|
||
|
if ((seqNumber != NULL) && (seqNumber->Len > 0))
|
||
|
{
|
||
|
fOutputStream->Put(sSeq);
|
||
|
fOutputStream->Put(*seqNumber);
|
||
|
}
|
||
|
if ((ssrc != NULL) && (ssrc->Len > 0))
|
||
|
{
|
||
|
fOutputStream->Put(sSsrc);
|
||
|
fOutputStream->Put(*ssrc);
|
||
|
}
|
||
|
if ((rtpTime != NULL) && (rtpTime->Len > 0))
|
||
|
{
|
||
|
fOutputStream->Put(sRTPTime);
|
||
|
fOutputStream->Put(*rtpTime);
|
||
|
}
|
||
|
|
||
|
if (lastRTPInfo)
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void RTSPRequestInterface::WriteStandardHeaders()
|
||
|
{
|
||
|
static StrPtrLen sCloseString("Close", 5);
|
||
|
|
||
|
Assert(sPremadeHeader != NULL);
|
||
|
fStandardHeadersWritten = true; //must be done here to prevent recursive calls
|
||
|
|
||
|
//if this is a "200 OK" response (most HTTP responses), we have some special
|
||
|
//optmizations here
|
||
|
Bool16 sendServerInfo = QTSServerInterface::GetServer()->GetPrefs()->GetRTSPServerInfoEnabled();
|
||
|
if (fStatus == qtssSuccessOK)
|
||
|
{
|
||
|
|
||
|
if (sendServerInfo)
|
||
|
{ fOutputStream->Put(sPremadeHeaderPtr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fOutputStream->Put(sPremadeNoHeaderPtr);
|
||
|
}
|
||
|
StrPtrLen* cSeq = fHeaderDictionary.GetValue(qtssCSeqHeader);
|
||
|
Assert(cSeq != NULL);
|
||
|
if (cSeq->Len > 1)
|
||
|
fOutputStream->Put(*cSeq);
|
||
|
else if (cSeq->Len == 1)
|
||
|
fOutputStream->PutChar(*cSeq->Ptr);
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if 0
|
||
|
// if you want the connection to stay alive when we don't grok
|
||
|
// the specfied parameter than eneable this code. - [sfu]
|
||
|
if (fStatus == qtssClientParameterNotUnderstood) {
|
||
|
fResponseKeepAlive = true;
|
||
|
}
|
||
|
#endif
|
||
|
//other status codes just get built on the fly
|
||
|
PutStatusLine(fOutputStream, fStatus, RTSPProtocol::k10Version);
|
||
|
if (sendServerInfo)
|
||
|
{
|
||
|
fOutputStream->Put(QTSServerInterface::GetServerHeader());
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
AppendHeader(qtssCSeqHeader, fHeaderDictionary.GetValue(qtssCSeqHeader));
|
||
|
}
|
||
|
|
||
|
//append sessionID header
|
||
|
StrPtrLen* incomingID = fHeaderDictionary.GetValue(qtssSessionHeader);
|
||
|
if ((incomingID != NULL) && (incomingID->Len > 0))
|
||
|
AppendHeader(qtssSessionHeader, incomingID);
|
||
|
|
||
|
//follows the HTTP/1.1 convention: if server wants to close the connection, it
|
||
|
//tags the response with the Connection: close header
|
||
|
if (!fResponseKeepAlive)
|
||
|
AppendHeader(qtssConnectionHeader, &sCloseString);
|
||
|
|
||
|
// 3gpp release 6 rate adaptation calls for echoing the rate adapt header back
|
||
|
// some clients use this header in the response to signal whether to send rate adapt
|
||
|
// NADU rtcp reports.
|
||
|
Bool16 doRateAdaptation = QTSServerInterface::GetServer()->GetPrefs()->Get3GPPEnabled() && QTSServerInterface::GetServer()->GetPrefs()->Get3GPPRateAdaptationEnabled();
|
||
|
if (doRateAdaptation)
|
||
|
{ StrPtrLen* rateAdaptHeader = fHeaderDictionary.GetValue(qtss3GPPAdaptationHeader);
|
||
|
if (rateAdaptHeader && rateAdaptHeader->Ptr && rateAdaptHeader->Len > 0)
|
||
|
AppendHeader(qtss3GPPAdaptationHeader, fHeaderDictionary.GetValue(qtss3GPPAdaptationHeader));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequestInterface::SendHeader()
|
||
|
{
|
||
|
if (!fStandardHeadersWritten)
|
||
|
this->WriteStandardHeaders();
|
||
|
fOutputStream->PutEOL();
|
||
|
}
|
||
|
|
||
|
QTSS_Error
|
||
|
RTSPRequestInterface::Write(void* inBuffer, UInt32 inLength, UInt32* outLenWritten, UInt32 /*inFlags*/)
|
||
|
{
|
||
|
//now just write whatever remains into the output buffer
|
||
|
fOutputStream->Put((char*)inBuffer, inLength);
|
||
|
|
||
|
if (outLenWritten != NULL)
|
||
|
*outLenWritten = inLength;
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error
|
||
|
RTSPRequestInterface::WriteV(iovec* inVec, UInt32 inNumVectors, UInt32 inTotalLength, UInt32* outLenWritten)
|
||
|
{
|
||
|
(void)fOutputStream->WriteV(inVec, inNumVectors, inTotalLength, NULL,
|
||
|
RTSPResponseStream::kAlwaysBuffer);
|
||
|
if (outLenWritten != NULL)
|
||
|
*outLenWritten = inTotalLength;
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
//param retrieval functions described in .h file
|
||
|
void* RTSPRequestInterface::GetAbsTruncatedPath(QTSSDictionary* inRequest, UInt32* /*outLen*/)
|
||
|
{
|
||
|
// This function gets called only once
|
||
|
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)inRequest;
|
||
|
theRequest->SetVal(qtssRTSPReqTruncAbsoluteURL, theRequest->GetValue(qtssRTSPReqAbsoluteURL));
|
||
|
|
||
|
//Adjust the length to truncate off the last file in the path
|
||
|
|
||
|
StrPtrLen* theAbsTruncPathParam = theRequest->GetValue(qtssRTSPReqTruncAbsoluteURL);
|
||
|
theAbsTruncPathParam->Len--;
|
||
|
while (theAbsTruncPathParam->Ptr[theAbsTruncPathParam->Len] != kPathDelimiterChar)
|
||
|
theAbsTruncPathParam->Len--;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void* RTSPRequestInterface::GetTruncatedPath(QTSSDictionary* inRequest, UInt32* /*outLen*/)
|
||
|
{
|
||
|
// This function always gets called
|
||
|
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)inRequest;
|
||
|
theRequest->SetVal(qtssRTSPReqFilePathTrunc, theRequest->GetValue(qtssRTSPReqFilePath));
|
||
|
|
||
|
//Adjust the length to truncate off the last file in the path
|
||
|
StrPtrLen* theTruncPathParam = theRequest->GetValue(qtssRTSPReqFilePathTrunc);
|
||
|
|
||
|
if (theTruncPathParam->Len > 0)
|
||
|
{
|
||
|
theTruncPathParam->Len--;
|
||
|
while ( (theTruncPathParam->Len != 0) && (theTruncPathParam->Ptr[theTruncPathParam->Len] != kPathDelimiterChar) )
|
||
|
theTruncPathParam->Len--;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void* RTSPRequestInterface::GetFileName(QTSSDictionary* inRequest, UInt32* /*outLen*/)
|
||
|
{
|
||
|
// This function always gets called
|
||
|
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)inRequest;
|
||
|
theRequest->SetVal(qtssRTSPReqFileName, theRequest->GetValue(qtssRTSPReqFilePath));
|
||
|
|
||
|
StrPtrLen* theFileNameParam = theRequest->GetValue(qtssRTSPReqFileName);
|
||
|
|
||
|
//paranoid check
|
||
|
if (theFileNameParam->Len == 0)
|
||
|
return theFileNameParam;
|
||
|
|
||
|
//walk back in the file name until we hit a /
|
||
|
SInt32 x = theFileNameParam->Len - 1;
|
||
|
for (; x > 0; x--)
|
||
|
if (theFileNameParam->Ptr[x] == kPathDelimiterChar)
|
||
|
break;
|
||
|
//once we do, make the tempPtr point to the next character after the slash,
|
||
|
//and adjust the length accordingly
|
||
|
if (theFileNameParam->Ptr[x] == kPathDelimiterChar )
|
||
|
{
|
||
|
theFileNameParam->Ptr = (&theFileNameParam->Ptr[x]) + 1;
|
||
|
theFileNameParam->Len -= (x + 1);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
void* RTSPRequestInterface::GetFileDigit(QTSSDictionary* inRequest, UInt32* /*outLen*/)
|
||
|
{
|
||
|
// This function always gets called
|
||
|
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)inRequest;
|
||
|
theRequest->SetVal(qtssRTSPReqFileDigit, theRequest->GetValue(qtssRTSPReqFilePath));
|
||
|
|
||
|
StrPtrLen* theFileDigit = theRequest->GetValue(qtssRTSPReqFileDigit);
|
||
|
|
||
|
UInt32 theFilePathLen = theRequest->GetValue(qtssRTSPReqFilePath)->Len;
|
||
|
theFileDigit->Ptr += theFilePathLen - 1;
|
||
|
theFileDigit->Len = 0;
|
||
|
while ((StringParser::sDigitMask[(unsigned int) *(*theFileDigit).Ptr] != '\0') &&
|
||
|
(theFileDigit->Len <= theFilePathLen))
|
||
|
{
|
||
|
theFileDigit->Ptr--;
|
||
|
theFileDigit->Len++;
|
||
|
}
|
||
|
//termination condition means that we aren't actually on a digit right now.
|
||
|
//Move pointer back onto the digit
|
||
|
theFileDigit->Ptr++;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void* RTSPRequestInterface::GetRealStatusCode(QTSSDictionary* inRequest, UInt32* outLen)
|
||
|
{
|
||
|
// Set the fRealStatusCode variable based on the current fStatusCode.
|
||
|
// This function always gets called
|
||
|
RTSPRequestInterface* theReq = (RTSPRequestInterface*)inRequest;
|
||
|
theReq->fRealStatusCode = RTSPProtocol::GetStatusCode(theReq->fStatus);
|
||
|
*outLen = sizeof(UInt32);
|
||
|
return &theReq->fRealStatusCode;
|
||
|
}
|
||
|
|
||
|
void* RTSPRequestInterface::GetLocalPath(QTSSDictionary* inRequest, UInt32* outLen)
|
||
|
{
|
||
|
// This function always gets called
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)inRequest;
|
||
|
QTSS_AttributeID theID = qtssRTSPReqFilePath;
|
||
|
|
||
|
// Get the truncated path on a setup, because setups have the trackID appended
|
||
|
if (theRequest->GetMethod() == qtssSetupMethod)
|
||
|
{
|
||
|
theID = qtssRTSPReqFilePathTrunc;
|
||
|
// invoke the param retrieval function here so that we can use the internal GetValue function later
|
||
|
RTSPRequestInterface::GetTruncatedPath(inRequest, outLen);
|
||
|
}
|
||
|
|
||
|
StrPtrLen* thePath = theRequest->GetValue(theID);
|
||
|
StrPtrLen filePath(thePath->Ptr, thePath->Len);
|
||
|
StrPtrLen* theRootDir = theRequest->GetValue(qtssRTSPReqRootDir);
|
||
|
if (theRootDir->Len && theRootDir->Ptr[theRootDir->Len -1] == kPathDelimiterChar
|
||
|
&& thePath->Len && thePath->Ptr[0] == kPathDelimiterChar)
|
||
|
{
|
||
|
char *thePathEnd = &(filePath.Ptr[filePath.Len]);
|
||
|
while (filePath.Ptr != thePathEnd)
|
||
|
{
|
||
|
if (*filePath.Ptr != kPathDelimiterChar)
|
||
|
break;
|
||
|
|
||
|
filePath.Ptr ++;
|
||
|
filePath.Len --;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UInt32 fullPathLen = filePath.Len + theRootDir->Len;
|
||
|
char* theFullPath = NEW char[fullPathLen+1];
|
||
|
theFullPath[fullPathLen] = '\0';
|
||
|
|
||
|
::memcpy(theFullPath, theRootDir->Ptr, theRootDir->Len);
|
||
|
::memcpy(theFullPath + theRootDir->Len, filePath.Ptr, filePath.Len);
|
||
|
|
||
|
(void)theRequest->SetValue(qtssRTSPReqLocalPath, 0, theFullPath,fullPathLen , QTSSDictionary::kDontObeyReadOnly);
|
||
|
|
||
|
// delete our copy of the data
|
||
|
delete [] theFullPath;
|
||
|
*outLen = 0;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void* RTSPRequestInterface::GetAuthDigestResponse(QTSSDictionary* inRequest, UInt32* )
|
||
|
{
|
||
|
RTSPRequestInterface* theRequest = (RTSPRequestInterface*)inRequest;
|
||
|
(void)theRequest->SetValue(qtssRTSPReqDigestResponse, 0, theRequest->fAuthDigestResponse.Ptr,theRequest->fAuthDigestResponse.Len , QTSSDictionary::kDontObeyReadOnly);
|
||
|
return NULL;
|
||
|
}
|
||
|
|