Very rough first start, not even everything added

This commit is contained in:
Darren VanBuren 2017-03-07 14:51:44 -08:00
commit af3619d4fa
88 changed files with 24251 additions and 0 deletions

View file

@ -0,0 +1,681 @@
/*
*
* @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: QTAccessFile.cpp
Contains: This file contains the implementation for finding and parsing qtaccess files.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include "QTSS.h"
#include "OSHeaders.h"
#include "StrPtrLen.h"
#include "StringParser.h"
#include "QTSSModuleUtils.h"
#include "OSFileSource.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "QTAccessFile.h"
#include "OSArrayObjectDeleter.h"
#include <grp.h>
#if __MacOSX__
#include <membership.h>
#endif
#include <pwd.h>
#include <signal.h>
#include <unistd.h>
#define DEBUG_QTACCESS 0
#define debug_printf if (DEBUG_QTACCESS) qtss_printf
char* QTAccessFile::sAccessValidUser = "require valid-user\n";
char* QTAccessFile::sAccessAnyUser = "require any-user\n";
UInt8 QTAccessFile::sWhitespaceAndGreaterThanMask[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 // \t is a stop
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, //10-19 //'\r' & '\n' are stop conditions
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //30-39 ' ' is a stop
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //60-69 // '>' is a stop
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
0, 0, 0, 0, 0, 0 //250-255
};
char* QTAccessFile::sQTAccessFileName = "qtaccess";
Bool16 QTAccessFile::sAllocatedName = false;
OSMutex* QTAccessFile::sAccessFileMutex = NULL;//QTAccessFile isn't reentrant
const int kBuffLen = 512;
void QTAccessFile::Initialize() // called by server at initialize never call again
{
if (NULL == sAccessFileMutex)
{ sAccessFileMutex = NEW OSMutex();
}
}
void QTAccessFile::SetAccessFileName(const char *inQTAccessFileName)
{
OSMutexLocker locker(sAccessFileMutex);
if (NULL == inQTAccessFileName)
{ Assert(NULL != inQTAccessFileName);
return;
}
if (sAllocatedName)
{ delete [] sQTAccessFileName;
}
sAllocatedName = true;
sQTAccessFileName = NEW char[strlen(inQTAccessFileName)+1];
::strcpy(sQTAccessFileName, inQTAccessFileName);
}
Bool16 QTAccessFile::HaveUser(char *userName, void* extraDataPtr)
{
Bool16 result = false;
if (NULL != userName && 0 != userName[0])
result = true;
return result;
}
Bool16 QTAccessFile::HaveGroups( char** groupArray, UInt32 numGroups, void* extraDataPtr)
{
Bool16 result = false;
if (numGroups > 0 && groupArray != NULL)
result = true;
return result;
}
Bool16 QTAccessFile::HaveRealm( char *userName, StrPtrLen* ioRealmNameStr, void *extraData )
{
Bool16 result = false;
if (ioRealmNameStr != NULL && ioRealmNameStr->Ptr != NULL && ioRealmNameStr->Len > 0)
result = true;
return result;
}
void QTAccessFile::GetRealm(StrPtrLen* accessRealm, StrPtrLen* ioRealmNameStr, char *userName,void *extraDataPtr )
{
if (ioRealmNameStr->Len <= accessRealm->Len)
accessRealm->Len = ioRealmNameStr->Len -1; // just copy what we can
::memcpy(ioRealmNameStr->Ptr, accessRealm->Ptr,accessRealm->Len);
ioRealmNameStr->Ptr[accessRealm->Len] = 0;
}
Bool16 QTAccessFile::TestUser(StrPtrLen* accessUser, char *userName,void *extraDataPtr )
{
Bool16 result = false;
if ( accessUser->Equal(userName) )
result = true;
return result;
}
Bool16 QTAccessFile::TestGroup( StrPtrLen* accessGroup, char *userName, char**groupArray, UInt32 numGroups, void *extraDataPtr)
{
for (UInt32 index = 0; index < numGroups; index ++)
{ if(accessGroup->Equal(groupArray[index]))
return true;
}
return false;
}
Bool16 QTAccessFile::TestExtraData( StrPtrLen* wordPtr, StringParser* lineParserPtr, void* extraDataPtr)
{
return false;
}
Bool16 QTAccessFile::AccessAllowed ( char *userName, char**groupArray, UInt32 numGroups, StrPtrLen *accessFileBufPtr,
QTSS_ActionFlags inFlags,StrPtrLen* ioRealmNameStr, Bool16 *outAllowAnyUserPtr, void *extraDataPtr
)
{
if (NULL == accessFileBufPtr || NULL == accessFileBufPtr->Ptr || 0 == accessFileBufPtr->Len)
return true; // nothing to check
if (ioRealmNameStr != NULL && ioRealmNameStr->Ptr != NULL && ioRealmNameStr->Len > 0)
ioRealmNameStr->Ptr[0] = 0;
StringParser accessFileParser(accessFileBufPtr);
QTSS_ActionFlags currentFlags = qtssActionFlagsRead;
StrPtrLen line;
StrPtrLen word;
Bool16 haveUserName = false;
Bool16 haveRealmResultBuffer = false;
Bool16 haveGroups = false;
*outAllowAnyUserPtr = false;
haveUserName = HaveUser(userName, extraDataPtr);
haveGroups = HaveGroups(groupArray, numGroups, extraDataPtr);
haveRealmResultBuffer = HaveRealm(userName, ioRealmNameStr, extraDataPtr );
while( accessFileParser.GetDataRemaining() != 0 )
{
accessFileParser.GetThruEOL(&line); // Read each line
StringParser lineParser(&line);
lineParser.ConsumeWhitespace();//skip over leading whitespace
if (lineParser.GetDataRemaining() == 0) // must be an empty line
continue;
char firstChar = lineParser.PeekFast();
if ( (firstChar == '#') || (firstChar == '\0') )
continue; //skip over comments and blank lines...
lineParser.ConsumeUntilWhitespace(&word);
if ( word.Equal("<Limit") ) // a limit line
{
currentFlags = qtssActionFlagsNoFlags; // clear to no access
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntil( &word, sWhitespaceAndGreaterThanMask); // the flag <limit Read> or <limit Read >
while (word.Len != 0) // compare each word in the line
{
if (word.Equal("WRITE") )
{ currentFlags |= inFlags & qtssActionFlagsWrite; // accept following lines if inFlags has write
}
if (word.Equal("READ") )
{ currentFlags |= inFlags & qtssActionFlagsRead; // accept following lines if inFlags has read
}
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntil(&word, sWhitespaceAndGreaterThanMask);
}
continue; //done with limit line
}
if ( word.Equal("</Limit>") )
currentFlags = qtssActionFlagsRead; // set the current access state to the default of read access
if (0 == (currentFlags & inFlags))
continue; // ignore lines because inFlags doesn't match the current access state
if (haveRealmResultBuffer && word.Equal("AuthName") ) //realm name
{
lineParser.ConsumeWhitespace();
lineParser.GetThruEOL(&word);
StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.
GetRealm(&word, ioRealmNameStr, userName, extraDataPtr );
// we don't change the buffer len ioRealmNameStr->Len because we might have another AuthName tag to copy
continue; // done with AuthName (realm)
}
if (word.Equal("require") )
{
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntilWhitespace(&word);
if (haveUserName && word.Equal("valid-user") )
{ return true;
}
if ( word.Equal("any-user") )
{
*outAllowAnyUserPtr = true;
return true;
}
if (!haveUserName)
continue;
if (word.Equal("user") )
{
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntilWhitespace(&word);
while (word.Len != 0) // compare each word in the line
{
if (TestUser(&word, userName, extraDataPtr ))
return true;
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntilWhitespace(&word);
}
continue; // done with "require user" line
}
if (haveGroups && word.Equal("group")) // check if we have groups for the user
{
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntilWhitespace(&word);
while (word.Len != 0) // compare each word in the line
{
if (TestGroup(&word, userName, groupArray, numGroups, extraDataPtr) )
return true;
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntilWhitespace(&word);
}
continue; // done with "require group" line
}
if (TestExtraData(&word, &lineParser, extraDataPtr))
return true;
continue; // done with unparsed "require" line
}
}
return false; // user or group not found
}
char* QTAccessFile::GetAccessFile_Copy( const char* movieRootDir, const char* dirPath)
{
OSMutexLocker locker(sAccessFileMutex);
char* currentDir= NULL;
char* lastSlash = NULL;
int movieRootDirLen = ::strlen(movieRootDir);
int maxLen = strlen(dirPath)+strlen(sQTAccessFileName) + strlen(kPathDelimiterString) + 1;
currentDir = NEW char[maxLen];
::strcpy(currentDir, dirPath);
//strip off filename
lastSlash = ::strrchr(currentDir, kPathDelimiterChar);
if (lastSlash != NULL)
lastSlash[0] = '\0';
//check qtaccess files
while ( true ) //walk backward up the dir tree.
{
int curLen = strlen(currentDir) + strlen(sQTAccessFileName) + strlen(kPathDelimiterString);
if ( curLen >= maxLen )
break;
::strcat(currentDir, kPathDelimiterString);
::strcat(currentDir, sQTAccessFileName);
QTSS_Object fileObject = NULL;
if( QTSS_OpenFileObject(currentDir, qtssOpenFileNoFlags, &fileObject) == QTSS_NoErr)
{
(void)QTSS_CloseFileObject(fileObject);
return currentDir;
}
//strip off the "/qtaccess"
lastSlash = ::strrchr(currentDir, kPathDelimiterChar);
lastSlash[0] = '\0';
//strip of the tailing directory
lastSlash = ::strrchr(currentDir, kPathDelimiterChar);
if (lastSlash == NULL)
break;
else
lastSlash[0] = '\0';
if ( (lastSlash-currentDir) < movieRootDirLen ) //bail if we start eating our way out of fMovieRootDir
break;
}
delete[] currentDir;
return NULL;
}
// allocates memory for outUsersFilePath and outGroupsFilePath - remember to delete
// returns the auth scheme
QTSS_AuthScheme QTAccessFile::FindUsersAndGroupsFilesAndAuthScheme(char* inAccessFilePath, QTSS_ActionFlags inAction, char** outUsersFilePath, char** outGroupsFilePath)
{
QTSS_AuthScheme authScheme = qtssAuthNone;
QTSS_ActionFlags currentFlags = qtssActionFlagsRead;
if (inAccessFilePath == NULL)
return authScheme;
*outUsersFilePath = NULL;
*outGroupsFilePath = NULL;
//Assert(outUsersFilePath == NULL);
//Assert(outGroupsFilePath == NULL);
StrPtrLen accessFileBuf;
(void)QTSSModuleUtils::ReadEntireFile(inAccessFilePath, &accessFileBuf);
OSCharArrayDeleter accessFileBufDeleter(accessFileBuf.Ptr);
StringParser accessFileParser(&accessFileBuf);
StrPtrLen line;
StrPtrLen word;
while( accessFileParser.GetDataRemaining() != 0 )
{
accessFileParser.GetThruEOL(&line); // Read each line
StringParser lineParser(&line);
lineParser.ConsumeWhitespace(); //skip over leading whitespace
if (lineParser.GetDataRemaining() == 0) // must be an empty line
continue;
char firstChar = lineParser.PeekFast();
if ( (firstChar == '#') || (firstChar == '\0') )
continue; //skip over comments and blank lines...
lineParser.ConsumeUntilWhitespace(&word);
if ( word.Equal("<Limit") ) // a limit line
{
currentFlags = qtssActionFlagsNoFlags; // clear to no access
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntil( &word, sWhitespaceAndGreaterThanMask); // the flag <limit Read> or <limit Read >
while (word.Len != 0) // compare each word in the line
{
if (word.Equal("WRITE") )
{
currentFlags |= inAction & qtssActionFlagsWrite; // accept following lines if inFlags has write
}
if (word.Equal("READ") )
{
currentFlags |= inAction & qtssActionFlagsRead; // accept following lines if inFlags has read
}
lineParser.ConsumeWhitespace();
lineParser.ConsumeUntil(&word, sWhitespaceAndGreaterThanMask);
}
continue; //done with limit line
}
if ( word.Equal("</Limit>") )
currentFlags = qtssActionFlagsRead; // set the current access state to the default of read access
if (0 == (currentFlags & inAction))
continue; // ignore lines because inFlags doesn't match the current access state
if (word.Equal("AuthUserFile") )
{
lineParser.ConsumeWhitespace();
lineParser.GetThruEOL(&word);
StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.
if(*outUsersFilePath != NULL) // we are encountering the AuthUserFile keyword twice!
delete[] *outUsersFilePath; // The last one found takes precedence...delete the previous path
*outUsersFilePath = word.GetAsCString();
continue;
}
if (word.Equal("AuthGroupFile") )
{
lineParser.ConsumeWhitespace();
lineParser.GetThruEOL(&word);
StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.
if(*outGroupsFilePath != NULL) // we are encountering the AuthGroupFile keyword twice!
delete[] *outGroupsFilePath; // The last one found takes precedence...delete the previous path
*outGroupsFilePath = word.GetAsCString();
continue;
}
if (word.Equal("AuthScheme") )
{
lineParser.ConsumeWhitespace();
lineParser.GetThruEOL(&word);
StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.
if (word.Equal("basic"))
authScheme = qtssAuthBasic;
else if (word.Equal("digest"))
authScheme = qtssAuthDigest;
continue;
}
}
return authScheme;
}
QTSS_Error QTAccessFile::AuthorizeRequest(QTSS_StandardRTSP_Params* inParams, Bool16 allowNoAccessFiles, QTSS_ActionFlags noAction, QTSS_ActionFlags authorizeAction, Bool16 *outAuthorizedPtr, Bool16 *outAllowAnyUserPtr)
{
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) || (NULL == outAllowAnyUserPtr) || (NULL == outAuthorizedPtr) )
return QTSS_RequestFailed;
*outAllowAnyUserPtr = false;
*outAuthorizedPtr = false;
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
// get the type of request
// Don't touch write requests
QTSS_ActionFlags action = QTSSModuleUtils::GetRequestActions(theRTSPRequest);
if(action == qtssActionFlagsNoFlags)
return QTSS_RequestFailed;
if( (action & noAction) != 0)
return QTSS_NoErr; // we don't handle
//get the local file path
char* pathBuffStr = QTSSModuleUtils::GetLocalPath_Copy(theRTSPRequest);
OSCharArrayDeleter pathBuffDeleter(pathBuffStr);
if (NULL == pathBuffStr)
return QTSS_RequestFailed;
//get the root movie directory
char* movieRootDirStr = QTSSModuleUtils::GetMoviesRootDir_Copy(theRTSPRequest);
OSCharArrayDeleter movieRootDeleter(movieRootDirStr);
if (NULL == movieRootDirStr)
return QTSS_RequestFailed;
QTSS_UserProfileObject theUserProfile = QTSSModuleUtils::GetUserProfileObject(theRTSPRequest);
if (NULL == theUserProfile)
return QTSS_RequestFailed;
char* accessFilePath = QTAccessFile::GetAccessFile_Copy(movieRootDirStr, pathBuffStr);
OSCharArrayDeleter accessFilePathDeleter(accessFilePath);
char* username = QTSSModuleUtils::GetUserName_Copy(theUserProfile);
OSCharArrayDeleter usernameDeleter(username);
UInt32 numGroups = 0;
char** groupCharPtrArray = QTSSModuleUtils::GetGroupsArray_Copy(theUserProfile, &numGroups);
OSCharPointerArrayDeleter groupCharPtrArrayDeleter(groupCharPtrArray);
StrPtrLen accessFileBuf;
(void)QTSSModuleUtils::ReadEntireFile(accessFilePath, &accessFileBuf);
OSCharArrayDeleter accessFileBufDeleter(accessFileBuf.Ptr);
if (accessFileBuf.Len == 0 && !allowNoAccessFiles)
{ accessFileBuf.Set(sAccessValidUser);
if (DEBUG_QTACCESS)
qtss_printf("QTAccessFile::AuthorizeRequest SET Accessfile valid user for no accessfile %s\n", sAccessValidUser);
}
if (accessFileBuf.Len == 0 && allowNoAccessFiles)
{ accessFileBuf.Set(sAccessAnyUser);
if (DEBUG_QTACCESS)
qtss_printf("QTAccessFile::AuthorizeRequest SET Accessfile any user for no access file %s\n", sAccessAnyUser);
}
char realmName[kBuffLen] = { 0 };
StrPtrLen realmNameStr(realmName,kBuffLen -1);
//check if this user is allowed to see this movie
Bool16 allowRequest = this->AccessAllowed(username, groupCharPtrArray, numGroups, &accessFileBuf, authorizeAction,&realmNameStr, outAllowAnyUserPtr );
debug_printf("accessFile.AccessAllowed for user=%s returned %d\n", username, allowRequest);
// Get the auth scheme
QTSS_AuthScheme theAuthScheme = qtssAuthNone;
UInt32 len = sizeof(theAuthScheme);
QTSS_Error theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&theAuthScheme, &len);
Assert(len == sizeof(theAuthScheme));
if(theErr != QTSS_NoErr)
return theErr;
// If auth scheme is basic and the realm is present in the access file, use it
if((theAuthScheme == qtssAuthBasic) && (realmNameStr.Ptr[0] != '\0')) //set the realm if we have one
(void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, realmNameStr.Ptr, ::strlen(realmNameStr.Ptr));
else // if auth scheme is basic and no realm is present, or if the auth scheme is digest, use the realm from the users file
{
char* userRealm = NULL;
(void) QTSS_GetValueAsString(theUserProfile, qtssUserRealm, 0, &userRealm);
if(userRealm != NULL)
{
OSCharArrayDeleter userRealmDeleter(userRealm);
(void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, userRealm, ::strlen(userRealm));
}
}
*outAuthorizedPtr = allowRequest;
Bool16 founduser = this->HaveUser(username, NULL);
Bool16 authContinue = true;
char nameBuff[256];
StrPtrLen reqNameStr(nameBuff, kBuffLen);
StrPtrLen profileNameStr(username);
theErr = QTSS_GetValue (theRTSPRequest,qtssRTSPReqUserName,0, (void *) reqNameStr.Ptr, &reqNameStr.Len);
if (DEBUG_QTACCESS)
{ qtss_printf("QTAccessFile::AuthorizeRequest qtaccess profile user =%s ", username);
reqNameStr.PrintStr("request user=","\n");
qtss_printf("QTAccessFile::AuthorizeRequest allowRequest=%d founduser=%d authContinue=%d\n", allowRequest, founduser, authContinue);
}
if (allowRequest && founduser)
theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue);
if (!allowRequest && !founduser)
theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue);
if (DEBUG_QTACCESS)
{ qtss_printf("QTAccessFile::AuthorizeRequest QTSSModuleUtils::AuthorizeRequest qtaccess profile user =%s ", username);
reqNameStr.PrintStr("request user=","\n");
qtss_printf("QTAccessFile::AuthorizeRequest allowRequest=%d founduser=%d authContinue=%d\n", allowRequest, founduser, authContinue);
}
return theErr;
}
bool DSAccessFile::CheckGroupMembership(const char* inUsername, const char* inGroupName)
{
struct passwd *user = NULL;
struct group *group = NULL;
#if __MacOSX__
uuid_t userID;
uuid_t groupID;
int isMember = 0;
#else
int numGroups = 50;
gid_t *groupList;
groupList = (gid_t *) malloc(numGroups * sizeof(gid_t));
#endif
// Look up the user using the POSIX APIs: only care about the UID.
user = getpwnam(inUsername);
if ( user == NULL )
return false;
#if __MacOSX__
uuid_clear(userID);
if ( mbr_uid_to_uuid(user->pw_uid, userID) )
return false;
#endif
// Look up the group using the POSIX APIs: only care about the GID.
group = getgrnam(inGroupName);
endgrent();
if ( group == NULL )
return false;
#if __MacOSX__
uuid_clear(groupID);
if ( mbr_gid_to_uuid(group->gr_gid, groupID) )
return false;
// In Tiger, group membership is painfully simple: we ask memberd for it!
// mbr_check_membership() returns 0 on success and error code on failure.
if ( mbr_check_membership(userID, groupID, &isMember) )
return false;
return (bool)isMember;
#else
if(getgrouplist(inUsername, user->pw_gid, groupList, &numGroups) == -1) {
qtss_printf("QTAccessFile::CheckGroupMembership getgrouplist for user %s returned -1 and numGroups = %d", inUsername, numGroups);
return false;
}
for(int i = 0; i < numGroups; i++) {
if(groupList[i] == group->gr_gid) {
return true;
}
}
return false;
#endif
}
Bool16 DSAccessFile::ValidUser( char*userName, void* extraDataPtr)
{
struct passwd *user = getpwnam(userName);
Bool16 result =true;
if ( user == NULL )
{
return result;
}
return true;
}

View file

@ -0,0 +1,117 @@
/*
*
* @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: QTAccessFile.h
Contains: This object contains an interface for finding and parsing qtaccess files.
*/
#ifndef _QT_ACCESS_FILE_H_
#define _QT_ACCESS_FILE_H_
#include <stdlib.h>
#include "SafeStdLib.h"
#include "QTSS.h"
#include "StrPtrLen.h"
#include "OSHeaders.h"
#include "StringParser.h"
#include "OSMutex.h"
class QTAccessFile
{
public:
static UInt8 sWhitespaceAndGreaterThanMask[];
static void Initialize();
static char * GetUserNameCopy(QTSS_UserProfileObject inUserProfile);
//GetGroupsArrayCopy
//
// GetGroupsArrayCopy allocates outGroupCharPtrArray. Caller must "delete [] outGroupCharPtrArray" when done.
static char* GetAccessFile_Copy( const char* movieRootDir, const char* dirPath);
//over ride these in a sub class
virtual Bool16 HaveUser(char *userName, void* extraDataPtr);
virtual Bool16 HaveGroups( char** groupArray, UInt32 numGroups, void* extraDataPtr);
virtual Bool16 HaveRealm( char *userName, StrPtrLen* ioRealmNameStr, void *extraData );
virtual Bool16 TestUser(StrPtrLen* accessUser, char *userName,void *extraDataPtr );
virtual Bool16 TestGroup( StrPtrLen* accessGroup, char *userName, char**groupArray, UInt32 numGroups, void *extraDataPtr);
virtual Bool16 TestExtraData( StrPtrLen* wordPtr, StringParser* lineParserPtr, void* extraDataPtr);
virtual void GetRealm(StrPtrLen* accessRealm, StrPtrLen* ioRealmNameStr, char *userName,void *extraDataPtr );
virtual Bool16 ValidUser(char* userName, void* extraDataPtr) { return false; };
//AccessAllowed
//
// This routine is used to get the Realm to send back to a user and to check if a user has access
// userName: may be null.
// accessFileBufPtr:If accessFileBufPtr is NULL or contains a NULL PTR or 0 LEN then false is returned
// ioRealmNameStr: ioRealmNameStr and ioRealmNameStr->Ptr may be null.
// To get a returned ioRealmNameStr value the ioRealmNameStr and ioRealmNameStr->Ptr must be non-NULL
// valid pointers. The ioRealmNameStr.Len should be set to the ioRealmNameStr->Ptr's allocated len.
// numGroups: The number of groups in the groupArray. Use GetGroupsArrayCopy to create the groupArray.
Bool16 AccessAllowed ( char *userName, char**groupArray, UInt32 numGroups,
StrPtrLen *accessFileBufPtr,QTSS_ActionFlags inFlags,StrPtrLen* ioRealmNameStr,
Bool16* outAllowAnyUserPtr,
void *extraDataPtr = NULL
);
static void SetAccessFileName(const char *inQTAccessFileName); //makes a copy and stores it
static char* GetAccessFileName() { return sQTAccessFileName; }; // a reference. Don't delete!
// allocates memory for outUsersFilePath and outGroupsFilePath - remember to delete
// returns the auth scheme
static QTSS_AuthScheme FindUsersAndGroupsFilesAndAuthScheme(char* inAccessFilePath, QTSS_ActionFlags inAction, char** outUsersFilePath, char** outGroupsFilePath);
QTSS_Error AuthorizeRequest(QTSS_StandardRTSP_Params* inParams, Bool16 allowNoAccessFiles, QTSS_ActionFlags noAction, QTSS_ActionFlags authorizeAction, Bool16 *outAuthorizedPtr, Bool16* outAllowAnyUserPtr = NULL);
virtual ~QTAccessFile() {};
private:
static char* sQTAccessFileName; // managed by the QTAccess module
static Bool16 sAllocatedName;
static OSMutex* sAccessFileMutex;
static char* sAccessValidUser;
static char* sAccessAnyUser;
};
class DSAccessFile : public QTAccessFile
{
public:
virtual ~DSAccessFile() {}
virtual Bool16 HaveGroups( char** groupArray, UInt32 numGroups, void* extraDataPtr) { return true; }
virtual Bool16 TestGroup( StrPtrLen* accessGroup, char *userName, char**groupArray, UInt32 numGroups, void *extraDataPtr)
{ StrPtrLenDel deleter( accessGroup->GetAsCString() );
return this->CheckGroupMembership(userName, deleter.Ptr );
}
virtual Bool16 ValidUser(char* userName, void* extraDataPtr);
bool CheckGroupMembership(const char* inUsername, const char* inGroupName);
};
#endif //_QT_ACCESS_FILE_H_

View file

@ -0,0 +1,128 @@
/*
*
* @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: QTSS3GPPModuleUtils.cpp
Contains: Implements utility routines defined in QTSS3GPPModuleUtils.h.
*/
#include "QTSS3GPPModuleUtils.h"
#include "QTSSModuleUtils.h"
#include "QTSS_Private.h"
#include "StrPtrLen.h"
#include "OSArrayObjectDeleter.h"
#include "OSMemory.h"
#include "MyAssert.h"
#include "StringFormatter.h"
#include "ResizeableStringFormatter.h"
#include "StringParser.h"
#include "SafeStdLib.h"
QTSS_TextMessagesObject QTSS3GPPModuleUtils::sMessages = NULL;
QTSS_ServerObject QTSS3GPPModuleUtils::sServer = NULL;
QTSS_PrefsObject QTSS3GPPModuleUtils::sServerPrefs = NULL;
QTSS_StreamRef QTSS3GPPModuleUtils::sErrorLog = NULL;
StrPtrLen QTSS3GPPModuleUtils::s3gppBitRateAdaptationSDPStr("a=3GPP-Adaptation-Support:");
const char* k3gppRateAdaptationReportFreqPrefName = "3gpp_protocol_rate_adaptation_report_frequency";
Bool16 QTSS3GPPModuleUtils::s3gppEnabled = false;
Bool16 QTSS3GPPModuleUtils::s3gppRateAdaptationEnabled = false;
UInt16 QTSS3GPPModuleUtils::s3gppRateAdaptationReportFrequency = 1;
void QTSS3GPPModuleUtils::Initialize(QTSS_Initialize_Params* initParams)
{
if (NULL == initParams)
return;
sServer = initParams->inServer;
sServerPrefs = initParams->inPrefs;
sMessages = initParams->inMessages;
sErrorLog = initParams->inErrorLogStream;
}
void QTSS3GPPModuleUtils::ValidatePrefs()
{
// min and max values per 3gpp rel-6 A26234 5.3.3.5
if (s3gppRateAdaptationReportFrequency < 1 || s3gppRateAdaptationReportFrequency > 9)
QTSSModuleUtils::LogPrefErrorStr( qtssWarningVerbosity, (char*) k3gppRateAdaptationReportFreqPrefName, "has an invalid value: valid range is [1..9]");
if (s3gppRateAdaptationReportFrequency < 1)
s3gppRateAdaptationReportFrequency = 1;
if (s3gppRateAdaptationReportFrequency > 9)
s3gppRateAdaptationReportFrequency = 9;
}
void QTSS3GPPModuleUtils::ReadPrefs()
{
const Bool16 kDefaultEnable = true;
const UInt16 kDefaultReportFreq = 1;
QTSSModuleUtils::GetAttribute(sServerPrefs,"enable_3gpp_protocol", qtssAttrDataTypeBool16,
&s3gppEnabled,(void *)&kDefaultEnable, sizeof(s3gppEnabled));
QTSSModuleUtils::GetAttribute(sServerPrefs,"enable_3gpp_protocol_rate_adaptation", qtssAttrDataTypeBool16,
&s3gppRateAdaptationEnabled,(void *)&kDefaultEnable, sizeof(s3gppRateAdaptationEnabled));
QTSSModuleUtils::GetAttribute(sServerPrefs, (char *) k3gppRateAdaptationReportFreqPrefName, qtssAttrDataTypeUInt16,
&s3gppRateAdaptationReportFrequency,(void *)&kDefaultReportFreq, sizeof(s3gppRateAdaptationReportFrequency));
QTSS3GPPModuleUtils::ValidatePrefs();
}
SDPContainer* QTSS3GPPModuleUtils::Get3GPPSDPFeatureListCopy(ResizeableStringFormatter &buffer)
{
SDPContainer* resultList = NEW SDPContainer;
StrPtrLen theLinePtr;
if (s3gppEnabled)
{
if (s3gppRateAdaptationEnabled)
{
buffer.Put(s3gppBitRateAdaptationSDPStr);
buffer.Put((SInt32) s3gppRateAdaptationReportFrequency);
buffer.PutEOL();
theLinePtr.Set(buffer.GetBufPtr(),buffer.GetBytesWritten());
resultList->AddHeaderLine(&theLinePtr);
buffer.Reset();
}
}
return resultList;
}

View file

@ -0,0 +1,74 @@
/*
*
* @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: QTSS3GPPModuleUtils.h
Contains: Utility routines for 3GPP modules to use.
*/
#ifndef _QTSS_3GPP_MODULE_UTILS_H_
#define _QTSS_3GPP_MODULE_UTILS_H_
#include <stdlib.h>
#include "SafeStdLib.h"
#include "QTSS.h"
#include "StrPtrLen.h"
#include "SDPUtils.h"
class QTSS3GPPModuleUtils
{
public:
static void Initialize(QTSS_Initialize_Params* initParams);
static void ReadPrefs();
static SDPContainer* Get3GPPSDPFeatureListCopy(ResizeableStringFormatter &buffer);
private:
static void ValidatePrefs();
//
// Used in the implementation of the above functions
static QTSS_TextMessagesObject sMessages;
static QTSS_ServerObject sServer;
static QTSS_PrefsObject sServerPrefs;
static QTSS_StreamRef sErrorLog;
static StrPtrLen s3gppBitRateAdaptationSDPStr;
static Bool16 s3gppEnabled;
static Bool16 s3gppRateAdaptationEnabled;
static UInt16 s3gppRateAdaptationReportFrequency;
};
#endif //_QTSS_3GPP_MODULE_UTILS_H_

View file

@ -0,0 +1,66 @@
/*
*
* @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: QTSSMemoryDeleter.h
Contains: Auto object for deleting memory allocated by QTSS API callbacks,
such as QTSS_GetValueAsString
*/
#ifndef __QTSS_MEMORY_DELETER_H__
#define __QTSS_MEMORY_DELETER_H__
#include "MyAssert.h"
#include "QTSS.h"
template <class T>
class QTSSMemoryDeleter
{
public:
QTSSMemoryDeleter(T* victim) : fT(victim) {}
~QTSSMemoryDeleter() { QTSS_Delete(fT); }
void ClearObject() { fT = NULL; }
void SetObject(T* victim)
{
Assert(fT == NULL);
fT = victim;
}
T* GetObject() { return fT; }
operator T*() { return fT; }
private:
T* fT;
};
typedef QTSSMemoryDeleter<char> QTSSCharArrayDeleter;
#endif //__QTSS_MEMORY_DELETER_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,291 @@
/*
*
* @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,QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr);
* 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: QTSSModuleUtils.h
Contains: Utility routines for modules to use.
*/
#ifndef _QTSS_MODULE_UTILS_H_
#define _QTSS_MODULE_UTILS_H_
#include <stdlib.h>
#include "SafeStdLib.h"
#include "QTSS.h"
#include "StrPtrLen.h"
#include "RTPMetaInfoPacket.h"
class QTSSModuleUtils
{
public:
// compatibiltiy features for certain players
enum {
kRequiresRTPInfoSeqAndTime = 0,
kAdjustBandwidth = 1,
kDisablePauseAdjustedRTPTime= 2,
kDelayRTPStreamsUntilAfterRTSPResponse = 3,
kDisable3gppRateAdaptation =4,
kAdjust3gppTargetTime = 5,
kDisableThinning =6,
};
static void Initialize( QTSS_TextMessagesObject inMessages,
QTSS_ServerObject inServer,
QTSS_StreamRef inErrorLog);
// Read the complete contents of the file at inPath into the StrPtrLen.
// This function allocates memory for the file data.
static QTSS_Error ReadEntireFile(char* inPath, StrPtrLen* outData, QTSS_TimeVal inModDate = -1, QTSS_TimeVal* outModDate = NULL);
// If your module supports RTSP methods, call this function from your QTSS_Initialize
// role to tell the server what those methods are.
static void SetupSupportedMethods( QTSS_Object inServer,
QTSS_RTSPMethod* inMethodArray,
UInt32 inNumMethods);
// Using a message out of the text messages dictionary is a common
// way to log errors to the error log. Here is a function to
// make that process very easy.
static void LogError( QTSS_ErrorVerbosity inVerbosity,
QTSS_AttributeID inTextMessage,
UInt32 inErrNumber,
char* inArgument = NULL,
char* inArg2 = NULL);
static void LogErrorStr( QTSS_ErrorVerbosity inVerbosity, char* inMessage);
static void LogPrefErrorStr( QTSS_ErrorVerbosity inVerbosity, char* preference, char* inMessage);
// This function constructs a C-string of the full path to the file being requested.
// You may opt to append an optional suffix, or pass in NULL. You are responsible
// for disposing this memory
static char* GetFullPath( QTSS_RTSPRequestObject inRequest,
QTSS_AttributeID whichFileType,
UInt32* outLen,
StrPtrLen* suffix = NULL);
//
// This function does 2 things:
// 1. Compares the enabled fields in the field ID array with the fields in the
// x-RTP-Meta-Info header. Turns off the fields in the array that aren't in the request.
//
// 2. Appends the x-RTP-Meta-Info header to the response, using the proper
// fields from the array, as well as the IDs provided in the array
static QTSS_Error AppendRTPMetaInfoHeader( QTSS_RTSPRequestObject inRequest,
StrPtrLen* inRTPMetaInfoHeader,
RTPMetaInfoPacket::FieldID* inFieldIDArray);
// This function sends an error to the RTSP client. You must provide a
// status code for the error, and a text message ID to describe the error.
//
// It always returns QTSS_RequestFailed.
static QTSS_Error SendErrorResponse( QTSS_RTSPRequestObject inRequest,
QTSS_RTSPStatusCode inStatusCode,
QTSS_AttributeID inTextMessage,
StrPtrLen* inStringArg = NULL);
// This function sends an error to the RTSP client. You don't have to provide
// a text message ID, but instead you need to provide the error message in a
// string
//
// It always returns QTSS_RequestFailed
static QTSS_Error SendErrorResponseWithMessage( QTSS_RTSPRequestObject inRequest,
QTSS_RTSPStatusCode inStatusCode,
StrPtrLen* inErrorMessageStr);
// Sends and HTTP 1.1 error message with an error message in HTML if errorMessage != NULL.
// The session must be flagged by KillSession set to true to kill.
// Use the QTSS_RTSPStatusCodes for the inStatusCode, for now they are the same as HTTP.
//
// It always returns QTSS_RequestFailed
static QTSS_Error SendHTTPErrorResponse( QTSS_RTSPRequestObject inRequest,
QTSS_SessionStatusCode inStatusCode,
Bool16 inKillSession,
char *errorMessage);
//Modules most certainly don't NEED to use this function, but it is awfully handy
//if they want to take advantage of it. Using the SDP data provided in the iovec,
//this function sends a standard describe response.
//NOTE: THE FIRST ENTRY OF THE IOVEC MUST BE EMPTY!!!!
static void SendDescribeResponse(QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inSession,
iovec* describeData,
UInt32 inNumVectors,
UInt32 inTotalLength);
// Called by SendDescribeResponse to coalesce iovec to a buffer
// Allocates memory - remember to delete it!
static char* CoalesceVectors(iovec* inVec, UInt32 inNumVectors, UInt32 inTotalLength);
//
// SEARCH FOR A SPECIFIC MODULE OBJECT
static QTSS_ModulePrefsObject GetModuleObjectByName(const StrPtrLen& inModuleName);
//
// GET MODULE PREFS OBJECT
static QTSS_ModulePrefsObject GetModulePrefsObject(QTSS_ModuleObject inModObject);
// GET MODULE ATTRIBUTES OBJECT
static QTSS_Object GetModuleAttributesObject(QTSS_ModuleObject inModObject);
//
// GET ATTRIBUTE
//
// This function retrieves an attribute
// (from any QTSS_Object, including the QTSS_ModulePrefsObject)
// with the specified name and type
// out of the specified object.
//
// Caller should pass in a buffer for ioBuffer that is large enough
// to hold the attribute value. inBufferLen should be set to the length
// of this buffer.
//
// Pass in a buffer containing a default value to use for the attribute
// in the inDefaultValue parameter. If the attribute isn't found, or is
// of the wrong type, the default value will be copied into ioBuffer.
// Also, this function adds the default value to object if it is not
// found or is of the wrong type. If no default value is provided, the
// attribute is still added but no value is assigned to it.
//
// Pass in NULL for the default value or 0 for the default value length if it is not known.
//
// This function logs an error if there was a default value provided.
static void GetAttribute(QTSS_Object inObject, char* inAttributeName, QTSS_AttrDataType inType,
void* ioBuffer, void* inDefaultValue, UInt32 inBufferLen);
static void GetIOAttribute(QTSS_Object inObject, char* inAttributeName, QTSS_AttrDataType inType,
void* ioDefaultResultBuffer, UInt32 inBufferLen);
//
// GET STRING ATTRIBUTE
//
// Does the same thing as GetAttribute, but does it for string attribute. Returns a newly
// allocated buffer with the attribute value inside it.
//
// Pass in NULL for the default value or an empty string if the default is not known.
static char* GetStringAttribute(QTSS_Object inObject, char* inAttributeName, char* inDefaultValue);
//
// GET ATTR ID
//
// Given an attribute in an object, returns its attribute ID
// or qtssIllegalAttrID if it isn't found.
static QTSS_AttributeID GetAttrID(QTSS_Object inObject, char* inAttributeName);
//
//
//
/// Get the type of request. Returns qtssActionFlagsNoFlags on failure.
// Result is a bitmap of flags
//
static QTSS_ActionFlags GetRequestActions(QTSS_RTSPRequestObject theRTSPRequest);
static char* GetLocalPath_Copy(QTSS_RTSPRequestObject theRTSPRequest);
static char* GetMoviesRootDir_Copy(QTSS_RTSPRequestObject theRTSPRequest);
static QTSS_UserProfileObject GetUserProfileObject(QTSS_RTSPRequestObject theRTSPRequest);
static QTSS_AttrRights GetRights(QTSS_UserProfileObject theUserProfileObject);
static char* GetExtendedRights(QTSS_UserProfileObject theUserProfileObject, UInt32 index);
static char* GetUserName_Copy(QTSS_UserProfileObject inUserProfile);
static char** GetGroupsArray_Copy(QTSS_UserProfileObject inUserProfile, UInt32 *outNumGroupsPtr);
static Bool16 UserInGroup(QTSS_UserProfileObject inUserProfile, char* inGroupName, UInt32 inGroupNameLen);
static void SetEnableRTSPErrorMsg(Bool16 enable) {QTSSModuleUtils::sEnableRTSPErrorMsg = enable; }
static QTSS_AttributeID CreateAttribute(QTSS_Object inObject, char* inAttributeName, QTSS_AttrDataType inType, void* inDefaultValue, UInt32 inBufferLen);
static Bool16 AddressInList(QTSS_Object inObject, QTSS_AttributeID listID, StrPtrLen *theAddressPtr);
static void SetMisingPrefLogVerbosity(QTSS_ErrorVerbosity verbosityLevel) { QTSSModuleUtils::sMissingPrefVerbosity = verbosityLevel;}
static QTSS_ErrorVerbosity GetMisingPrefLogVerbosity() { return QTSSModuleUtils::sMissingPrefVerbosity;}
static Bool16 FindStringInAttributeList(QTSS_Object inObject, QTSS_AttributeID listID, StrPtrLen *inStrPtr);
static Bool16 HavePlayerProfile(QTSS_PrefsObject inPrefObjectToCheck, QTSS_StandardRTSP_Params* inParams, UInt32 feature);
static QTSS_Error AuthorizeRequest(QTSS_RTSPRequestObject theRTSPRequest, Bool16* allowed, Bool16*haveUser,Bool16 *authContinue);
private:
//
// Used in the implementation of the above functions
static QTSS_AttributeID CheckAttributeDataType(QTSS_Object inObject, char* inAttributeName, QTSS_AttrDataType inType, void* inDefaultValue, UInt32 inBufferLen);
static QTSS_TextMessagesObject sMessages;
static QTSS_ServerObject sServer;
static QTSS_StreamRef sErrorLog;
static Bool16 sEnableRTSPErrorMsg;
static QTSS_ErrorVerbosity sMissingPrefVerbosity;
};
class IPComponentStr
{
public:
enum { kNumComponents = 4 };
StrPtrLen fAddressComponent[kNumComponents];
Bool16 fIsValid;
static IPComponentStr sLocalIPCompStr;
IPComponentStr() : fIsValid(false) {}
IPComponentStr(char *theAddress);
IPComponentStr(StrPtrLen *sourceStrPtr);
inline StrPtrLen* GetComponent(UInt16 which);
Bool16 Equal(IPComponentStr *testAddressPtr);
Bool16 Set(StrPtrLen *theAddressStrPtr);
Bool16 Valid() { return fIsValid; }
inline Bool16 IsLocal();
};
Bool16 IPComponentStr::IsLocal()
{
if (this->Equal(&sLocalIPCompStr))
return true;
return false;
}
StrPtrLen* IPComponentStr::GetComponent(UInt16 which)
{
if (which < IPComponentStr::kNumComponents)
return &fAddressComponent[which];
Assert(0);
return NULL;
}
#endif //_QTSS_MODULE_UTILS_H_

View file

@ -0,0 +1,550 @@
/*
*
* @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: QTSSRollingLog.cpp
Contains: Implements object defined in .h file
*/
#include <time.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#ifndef __Win32__
#include <sys/time.h>
#endif
#include "QTSSRollingLog.h"
#include "OS.h"
#include "OSMemory.h"
#include "OSArrayObjectDeleter.h"
#include "ResizeableStringFormatter.h"
static Bool16 sCloseOnWrite = true;
QTSSRollingLog::QTSSRollingLog() :
fLog(NULL),
fLogCreateTime(-1),
fLogFullPath(NULL),
fAppendDotLog(true),
fLogging(true)
{
this->SetTaskName("QTSSRollingLog");
}
QTSSRollingLog::~QTSSRollingLog()
{
//
// Log should already be closed, but just in case...
this->CloseLog();
delete [] fLogFullPath;
}
// Set this to true to get the log to close the file between writes.
void QTSSRollingLog::SetCloseOnWrite(Bool16 closeOnWrite)
{
sCloseOnWrite = closeOnWrite;
}
Bool16 QTSSRollingLog::IsLogEnabled()
{
return sCloseOnWrite || (fLog != NULL);
}
void QTSSRollingLog::WriteToLog(char* inLogData, Bool16 allowLogToRoll)
{
OSMutexLocker locker(&fMutex);
if (fLogging == false)
return;
if (sCloseOnWrite && fLog == NULL)
this->EnableLog(fAppendDotLog ); //re-open log file before we write
if (allowLogToRoll)
(void)this->CheckRollLog();
if (fLog != NULL)
{
qtss_fprintf(fLog, "%s", inLogData);
::fflush(fLog);
}
if (sCloseOnWrite)
this->CloseLog( false );
}
Bool16 QTSSRollingLog::RollLog()
{
OSMutexLocker locker(&fMutex);
//returns false if an error occurred, true otherwise
//close the old file.
if (fLog != NULL)
this->CloseLog();
if (fLogging == false)
return false;
//rename the old file
Bool16 result = this->RenameLogFile(fLogFullPath);
if (result)
this->EnableLog(fAppendDotLog);//re-opens log file
return result;
}
char* QTSSRollingLog::GetLogPath(char *extension)
{
char *thePath = NULL;
OSCharArrayDeleter logDir(this->GetLogDir()); //The string passed into this function is a copy
OSCharArrayDeleter logName(this->GetLogName()); //The string passed into this function is a copy
ResizeableStringFormatter formatPath(NULL,0); //allocate the buffer
formatPath.PutFilePath(logDir, logName);
if ( extension != NULL)
formatPath.Put(extension);
formatPath.PutTerminator();
thePath = formatPath.GetBufPtr();
formatPath.Set(NULL,0); //don't delete buffer we are returning the path as a result
return thePath;
}
void QTSSRollingLog::EnableLog( Bool16 appendDotLog )
{
//
// Start this object running!
this->Signal(Task::kStartEvent);
OSMutexLocker locker(&fMutex);
fAppendDotLog = appendDotLog;
if (fLogging == false)
return;
char *extension = ".log";
if (!appendDotLog)
extension = NULL;
delete[] fLogFullPath;
fLogFullPath = this->GetLogPath(extension);
//we need to make sure that when we create a new log file, we write the
//log header at the top
Bool16 logExists = this->DoesFileExist(fLogFullPath);
//create the log directory if it doesn't already exist
if (!logExists)
{
OSCharArrayDeleter tempDir(this->GetLogDir());
OS::RecursiveMakeDir(tempDir.GetObject());
}
fLog = ::fopen(fLogFullPath, "a+");//open for "append"
if (NULL != fLog)
{
if (!logExists) //the file is new, write a log header with the create time of the file.
{ fLogCreateTime = this->WriteLogHeader(fLog);
#if __MacOSX__
(void) ::chown(fLogFullPath, 76, (gid_t)-1);//set owner to user qtss.
#endif
}
else //the file is old, read the log header to find the create time of the file.
fLogCreateTime = this->ReadLogHeader(fLog);
}
}
void QTSSRollingLog::CloseLog( Bool16 leaveEnabled )
{
OSMutexLocker locker(&fMutex);
if (leaveEnabled)
sCloseOnWrite = true;
if (fLog != NULL)
{
::fclose(fLog);
fLog = NULL;
}
}
//returns false if some error has occurred
Bool16 QTSSRollingLog::FormatDate(char *ioDateBuffer, Bool16 logTimeInGMT)
{
Assert(NULL != ioDateBuffer);
//use ansi routines for getting the date.
time_t calendarTime = ::time(NULL);
Assert(-1 != calendarTime);
if (-1 == calendarTime)
return false;
struct tm* theTime = NULL;
struct tm timeResult;
if (logTimeInGMT)
theTime = ::qtss_gmtime(&calendarTime, &timeResult);
else
theTime = qtss_localtime(&calendarTime, &timeResult);
Assert(NULL != theTime);
if (NULL == theTime)
return false;
// date time needs to look like this for extended log file format: 2001-03-16 23:34:54
// this wonderful ANSI routine just does it for you.
// the format is YYYY-MM-DD HH:MM:SS
// the date time is in GMT, unless logTimeInGMT is false, in which case
// the time logged is local time
//qtss_strftime(ioDateBuffer, kMaxDateBufferSize, "%d/%b/%Y:%H:%M:%S", theLocalTime);
qtss_strftime(ioDateBuffer, kMaxDateBufferSizeInBytes, "%Y-%m-%d %H:%M:%S", theTime);
return true;
}
Bool16 QTSSRollingLog::CheckRollLog()
{
//returns false if an error occurred, true otherwise
if (fLog == NULL)
return true;
//first check to see if log rolling should happen because of a date interval.
//This takes precedence over size based log rolling
// this is only if a client connects just between 00:00:00 and 00:01:00
// since the task object runs every minute
// when an entry is written to the log file, only the file size must be checked
// to see if it exceeded the limits
// the roll interval should be monitored in a task object
// and rolled at midnight if the creation time has exceeded.
if ((-1 != fLogCreateTime) && (0 != this->GetRollIntervalInDays()))
{
time_t logCreateTimeMidnight = -1;
QTSSRollingLog::ResetToMidnight(&fLogCreateTime, &logCreateTimeMidnight);
Assert(logCreateTimeMidnight != -1);
time_t calendarTime = ::time(NULL);
Assert(-1 != calendarTime);
if (-1 != calendarTime)
{
double theExactInterval = ::difftime(calendarTime, logCreateTimeMidnight);
SInt32 theCurInterval = (SInt32)::floor(theExactInterval);
//transfer_roll_interval is in days, theCurInterval is in seconds
SInt32 theRollInterval = this->GetRollIntervalInDays() * 60 * 60 * 24;
if (theCurInterval > theRollInterval)
return this->RollLog();
}
}
//now check size based log rolling
UInt32 theCurrentPos = ::ftell(fLog);
//max_transfer_log_size being 0 is a signal to ignore the setting.
if ((this->GetMaxLogBytes() != 0) &&
(theCurrentPos > this->GetMaxLogBytes()))
return this->RollLog();
return true;
}
Bool16 QTSSRollingLog::RenameLogFile(const char* inFileName)
{
//returns false if an error occurred, true otherwise
//this function takes care of renaming a log file from "myLogFile.log" to
//"myLogFile.981217000.log" or if that is already taken, myLogFile.981217001.log", etc
//fix 2287086. Rolled log name can be different than original log name
//GetLogDir returns a copy of the log dir
OSCharArrayDeleter logDirectory(this->GetLogDir());
//create the log directory if it doesn't already exist
OS::RecursiveMakeDir(logDirectory.GetObject());
//GetLogName returns a copy of the log name
OSCharArrayDeleter logBaseName(this->GetLogName());
//QTStreamingServer.981217003.log
//format the new file name
OSCharArrayDeleter theNewNameBuffer(NEW char[::strlen(logDirectory) + kMaxFilenameLengthInBytes + 3]);
//copy over the directory - append a '/' if it's missing
::strcpy(theNewNameBuffer, logDirectory);
if (theNewNameBuffer[::strlen(theNewNameBuffer)-1] != kPathDelimiterChar)
{
::strcat(theNewNameBuffer, kPathDelimiterString);
}
//copy over the base filename
::strcat(theNewNameBuffer, logBaseName.GetObject());
//append the date the file was created
struct tm timeResult;
struct tm* theLocalTime = qtss_localtime(&fLogCreateTime, &timeResult);
char timeString[10];
qtss_strftime(timeString, 10, ".%y%m%d", theLocalTime);
::strcat(theNewNameBuffer, timeString);
SInt32 theBaseNameLength = ::strlen(theNewNameBuffer);
//loop until we find a unique name to rename this file
//and append the log number and suffix
SInt32 theErr = 0;
for (SInt32 x = 0; (theErr == 0) && (x<=1000); x++)
{
if (x == 1000) //we don't have any digits left, so just reuse the "---" until tomorrow...
{
//add a bogus log number and exit the loop
qtss_sprintf(theNewNameBuffer + theBaseNameLength, "---.log");
break;
}
//add the log number & suffix
qtss_sprintf(theNewNameBuffer + theBaseNameLength, "%03"_S32BITARG_".log", x);
//assume that when ::stat returns an error, it is becase
//the file doesnt exist. Once that happens, we have a unique name
// csl - shouldn't you watch for a ENOENT result?
struct stat theIdontCare;
theErr = ::stat(theNewNameBuffer, &theIdontCare);
WarnV((theErr == 0 || OSThread::GetErrno() == ENOENT), "unexpected stat error in RenameLogFile");
}
//rename the file. Use posix rename function
int result = ::rename(inFileName, theNewNameBuffer);
if (result == -1)
theErr = (SInt32)OSThread::GetErrno();
else
theErr = 0;
WarnV(theErr == 0 , "unexpected rename error in RenameLogFile");
if (theErr != 0)
return false;
else
return true;
}
Bool16 QTSSRollingLog::DoesFileExist(const char *inPath)
{
struct stat theStat;
int theErr = ::stat(inPath, &theStat);
if (theErr != 0)
return false;
else
return true;
}
time_t QTSSRollingLog::WriteLogHeader(FILE* inFile)
{
OSMutexLocker locker(&fMutex);
//The point of this header is to record the exact time the log file was created,
//in a format that is easy to parse through whenever we open the file again.
//This is necessary to support log rolling based on a time interval, and POSIX doesn't
//support a create date in files.
time_t calendarTime = ::time(NULL);
Assert(-1 != calendarTime);
if (-1 == calendarTime)
return -1;
struct tm timeResult;
struct tm* theLocalTime = qtss_localtime(&calendarTime, &timeResult);
Assert(NULL != theLocalTime);
if (NULL == theLocalTime)
return -1;
//
// Files are always created at hour 0 (we don't care about the time, we always
// want them to roll at midnight.
//theLocalTime->tm_hour = 0;
//theLocalTime->tm_min = 0;
//theLocalTime->tm_sec = 0;
char tempbuf[1024];
qtss_strftime(tempbuf, sizeof(tempbuf), "#Log File Created On: %m/%d/%Y %H:%M:%S\n", theLocalTime);
//qtss_sprintf(tempbuf, "#Log File Created On: %d/%d/%d %d:%d:%d %d:%d:%d GMT\n",
// theLocalTime->tm_mon, theLocalTime->tm_mday, theLocalTime->tm_year,
// theLocalTime->tm_hour, theLocalTime->tm_min, theLocalTime->tm_sec,
// theLocalTime->tm_yday, theLocalTime->tm_wday, theLocalTime->tm_isdst);
this->WriteToLog(tempbuf, !kAllowLogToRoll);
return this->ReadLogHeader(inFile);
}
time_t QTSSRollingLog::ReadLogHeader(FILE* inFile)
{
OSMutexLocker locker(&fMutex);
//This function reads the header in a log file, returning the time stored
//at the beginning of this file. This value is used to determine when to
//roll the log.
//Returns -1 if the header is bogus. In that case, just ignore time based log rolling
//first seek to the beginning of the file
SInt32 theCurrentPos = ::ftell(inFile);
if (theCurrentPos == -1)
return -1;
(void)::rewind(inFile);
const UInt32 kMaxHeaderLength = 500;
char theFirstLine[kMaxHeaderLength];
if (NULL == ::fgets(theFirstLine, kMaxHeaderLength, inFile))
{
::fseek(inFile, 0, SEEK_END);
return -1;
}
::fseek(inFile, 0, SEEK_END);
struct tm theFileCreateTime;
// Zero out fields we will not be using
theFileCreateTime.tm_isdst = -1;
theFileCreateTime.tm_wday = 0;
theFileCreateTime.tm_yday = 0;
//if (EOF == ::sscanf(theFirstLine, "#Log File Created On: %d/%d/%d %d:%d:%d\n",
// &theFileCreateTime.tm_mon, &theFileCreateTime.tm_mday, &theFileCreateTime.tm_year,
// &theFileCreateTime.tm_hour, &theFileCreateTime.tm_min, &theFileCreateTime.tm_sec))
// return -1;
//
// We always want to roll at hour 0, so ignore the time of creation
if (EOF == ::sscanf(theFirstLine, "#Log File Created On: %d/%d/%d %d:%d:%d\n",
&theFileCreateTime.tm_mon, &theFileCreateTime.tm_mday, &theFileCreateTime.tm_year,
&theFileCreateTime.tm_hour, &theFileCreateTime.tm_min, &theFileCreateTime.tm_sec))
return -1;
//
// It should be like this anyway, but if the log file is legacy, then...
// No! The log file will have the actual time in it but we shall return the exact time
//theFileCreateTime.tm_hour = 0;
//theFileCreateTime.tm_min = 0;
//theFileCreateTime.tm_sec = 0;
// Actually, it seems like all platforms need this.
//#ifdef __Win32__
// Win32 has slightly different atime basis than UNIX.
theFileCreateTime.tm_yday--;
theFileCreateTime.tm_mon--;
theFileCreateTime.tm_year -= 1900;
//#endif
#if 0
//use ansi routines for getting the date.
time_t calendarTime = ::time(NULL);
Assert(-1 != calendarTime);
if (-1 == calendarTime)
return false;
struct tm timeResult;
struct tm* theLocalTime = qtss_localtime(&calendarTime, &timeResult);
Assert(NULL != theLocalTime);
if (NULL == theLocalTime)
return false;
#endif
//ok, we should have a filled in tm struct. Convert it to a time_t.
//time_t thePoopTime = ::mktime(theLocalTime);
time_t theTime = ::mktime(&theFileCreateTime);
return theTime;
}
SInt64 QTSSRollingLog::Run()
{
//
// If we are going away, just return
EventFlags events = this->GetEvents();
if (events & Task::kKillEvent)
return -1;
OSMutexLocker locker(&fMutex);
UInt32 theRollInterval = (this->GetRollIntervalInDays()) * 60 * 60 * 24;
if((fLogCreateTime != -1) && (fLog != NULL))
{
time_t logRollTimeMidnight = -1;
this->ResetToMidnight(&fLogCreateTime, &logRollTimeMidnight);
Assert(logRollTimeMidnight != -1);
if(theRollInterval != 0)
{
time_t calendarTime = ::time(NULL);
Assert(-1 != calendarTime);
double theExactInterval = ::difftime(calendarTime, logRollTimeMidnight);
if(theExactInterval > 0) {
UInt32 theCurInterval = (UInt32)::floor(theExactInterval);
if (theCurInterval >= theRollInterval)
this->RollLog();
}
}
}
return 60 * 1000;
}
void QTSSRollingLog::ResetToMidnight(time_t* inTimePtr, time_t* outTimePtr)
{
if(*inTimePtr == -1)
{
*outTimePtr = -1;
return;
}
struct tm timeResult;
struct tm* theLocalTime = qtss_localtime(inTimePtr, &timeResult);
Assert(theLocalTime != NULL);
theLocalTime->tm_hour = 0;
theLocalTime->tm_min = 0;
theLocalTime->tm_sec = 0;
// some weird stuff
//theLocalTime->tm_yday--;
//theLocalTime->tm_mon--;
//theLocalTime->tm_year -= 1900;
*outTimePtr = ::mktime(theLocalTime);
}

View file

@ -0,0 +1,142 @@
/*
*
* @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: QTSSRollingLog.h
Contains: A log toolkit, log can roll either by time or by size, clients
must derive off of this object ot provide configuration information.
*/
#ifndef __QTSS_ROLLINGLOG_H__
#define __QTSS_ROLLINGLOG_H__
#include <stdio.h>
#include <time.h>
#ifndef __Win32__
#include <sys/time.h>
#endif
#include "OSHeaders.h"
#include "OSMutex.h"
#include "Task.h"
const Bool16 kAllowLogToRoll = true;
class QTSSRollingLog : public Task
{
public:
//pass in whether you'd like the log roller to log errors.
QTSSRollingLog();
//
// Call this to delete. Closes the log and sends a kill event
void Delete()
{ CloseLog(false); this->Signal(Task::kKillEvent); }
//
// Write a log message
void WriteToLog(char* inLogData, Bool16 allowLogToRoll);
//log rolls automatically based on the configuration criteria,
//but you may roll the log manually by calling this function.
//Returns true if no error, false otherwise
Bool16 RollLog();
//
// Call this to open the log file and begin logging
void EnableLog( Bool16 appendDotLog = true);
//
// Call this to close the log
// (pass leaveEnabled as true when we are temporarily closing.)
void CloseLog( Bool16 leaveEnabled = false);
//
//mainly to check and see if errors occurred
Bool16 IsLogEnabled();
//master switch
Bool16 IsLogging() { return fLogging; }
void SetLoggingEnabled( Bool16 logState ) { fLogging = logState; }
//General purpose utility function
//returns false if some error has occurred
static Bool16 FormatDate(char *ioDateBuffer, Bool16 logTimeInGMT);
// Check the log to see if it needs to roll
// (rolls the log if necessary)
Bool16 CheckRollLog();
// Set this to true to get the log to close the file between writes.
static void SetCloseOnWrite(Bool16 closeOnWrite);
enum
{
kMaxDateBufferSizeInBytes = 30, //UInt32
kMaxFilenameLengthInBytes = 31 //UInt32
};
protected:
//
// Task object. Do not delete directly
virtual ~QTSSRollingLog();
//Derived class must provide a way to get the log & rolled log name
virtual char* GetLogName() = 0;
virtual char* GetLogDir() = 0;
virtual UInt32 GetRollIntervalInDays() = 0;//0 means no interval
virtual UInt32 GetMaxLogBytes() = 0;//0 means unlimited
//to record the time the file was created (for time based rolling)
virtual time_t WriteLogHeader(FILE *inFile);
time_t ReadLogHeader(FILE* inFile);
private:
//
// Run function to roll log right at midnight
virtual SInt64 Run();
FILE* fLog;
time_t fLogCreateTime;
char* fLogFullPath;
Bool16 fAppendDotLog;
Bool16 fLogging;
Bool16 RenameLogFile(const char* inFileName);
Bool16 DoesFileExist(const char *inPath);
static void ResetToMidnight(time_t* inTimePtr, time_t* outTimePtr);
char* GetLogPath(char *extension);
// To make sure what happens in Run doesn't also happen at the same time
// in the public functions.
OSMutex fMutex;
};
#endif // __QTSS_ROLLINGLOG_H__

View file

@ -0,0 +1,437 @@
/*
*
* @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: SDPSourceInfo.cpp
Contains: Implementation of object defined in .h file
*/
#include "SDPSourceInfo.h"
#include "StringParser.h"
#include "StringFormatter.h"
#include "OSMemory.h"
#include "SocketUtils.h"
#include "StrPtrLen.h"
#include "SDPUtils.h"
#include "OSArrayObjectDeleter.h"
static StrPtrLen sCLine("c=IN IP4 0.0.0.0");
static StrPtrLen sControlLine("a=control:*");
static StrPtrLen sVideoStr("video");
static StrPtrLen sAudioStr("audio");
static StrPtrLen sRtpMapStr("rtpmap");
static StrPtrLen sControlStr("control");
static StrPtrLen sBufferDelayStr("x-bufferdelay");
static StrPtrLen sBroadcastControlStr("x-broadcastcontrol");
static StrPtrLen sAutoDisconnect("RTSP");
static StrPtrLen sAutoDisconnectTime("TIME");
SDPSourceInfo::~SDPSourceInfo()
{
// Not reqd as the destructor of the
// base class will take care of delete the stream array
// and output array if allocated
/*
if (fStreamArray != NULL)
{
char* theCharArray = (char*)fStreamArray;
delete [] theCharArray;
}
*/
fSDPData.Delete();
}
char* SDPSourceInfo::GetLocalSDP(UInt32* newSDPLen)
{
Assert(fSDPData.Ptr != NULL);
Bool16 appendCLine = true;
UInt32 trackIndex = 0;
char *localSDP = NEW char[fSDPData.Len * 2];
OSCharArrayDeleter charArrayPathDeleter(localSDP);
StringFormatter localSDPFormatter(localSDP, fSDPData.Len * 2);
StrPtrLen sdpLine;
StringParser sdpParser(&fSDPData);
char trackIndexBuffer[50];
// Only generate our own trackIDs if this file doesn't have 'em.
// Our assumption here is that either the file has them, or it doesn't.
// A file with some trackIDs, and some not, won't work.
Bool16 hasControlLine = false;
while (sdpParser.GetDataRemaining() > 0)
{
//stop when we reach an empty line.
sdpParser.GetThruEOL(&sdpLine);
if (sdpLine.Len == 0)
continue;
switch (*sdpLine.Ptr)
{
case 'c':
break;//ignore connection information
case 'm':
{
//append new connection information right before the first 'm'
if (appendCLine)
{
localSDPFormatter.Put(sCLine);
localSDPFormatter.PutEOL();
if (!hasControlLine)
{
localSDPFormatter.Put(sControlLine);
localSDPFormatter.PutEOL();
}
appendCLine = false;
}
//the last "a=" for each m should be the control a=
if ((trackIndex > 0) && (!hasControlLine))
{
qtss_sprintf(trackIndexBuffer, "a=control:trackID=%"_S32BITARG_"\r\n",trackIndex);
localSDPFormatter.Put(trackIndexBuffer, ::strlen(trackIndexBuffer));
}
//now write the 'm' line, but strip off the port information
StringParser mParser(&sdpLine);
StrPtrLen mPrefix;
mParser.ConsumeUntil(&mPrefix, StringParser::sDigitMask);
localSDPFormatter.Put(mPrefix);
localSDPFormatter.Put("0", 1);
(void)mParser.ConsumeInteger(NULL);
localSDPFormatter.Put(mParser.GetCurrentPosition(), mParser.GetDataRemaining());
localSDPFormatter.PutEOL();
trackIndex++;
break;
}
case 'a':
{
StringParser aParser(&sdpLine);
aParser.ConsumeLength(NULL, 2);//go past 'a='
StrPtrLen aLineType;
aParser.ConsumeWord(&aLineType);
if (aLineType.Equal(sControlStr))
{
aParser.ConsumeUntil(NULL, '=');
aParser.ConsumeUntil(NULL, StringParser::sDigitMask);
StrPtrLen aDigitType;
(void)aParser.ConsumeInteger(&aDigitType);
if (aDigitType.Len > 0)
{
localSDPFormatter.Put("a=control:trackID=", 18);
localSDPFormatter.Put(aDigitType);
localSDPFormatter.PutEOL();
hasControlLine = true;
break;
}
}
localSDPFormatter.Put(sdpLine);
localSDPFormatter.PutEOL();
break;
}
default:
{
localSDPFormatter.Put(sdpLine);
localSDPFormatter.PutEOL();
}
}
}
if ((trackIndex > 0) && (!hasControlLine))
{
qtss_sprintf(trackIndexBuffer, "a=control:trackID=%"_S32BITARG_"\r\n",trackIndex);
localSDPFormatter.Put(trackIndexBuffer, ::strlen(trackIndexBuffer));
}
*newSDPLen = (UInt32)localSDPFormatter.GetCurrentOffset();
StrPtrLen theSDPStr(localSDP, *newSDPLen);//localSDP is not 0 terminated so initialize theSDPStr with the len.
SDPContainer rawSDPContainer;
(void) rawSDPContainer.SetSDPBuffer( &theSDPStr );
SDPLineSorter sortedSDP(&rawSDPContainer);
return sortedSDP.GetSortedSDPCopy(); // return a new copy of the sorted SDP
}
void SDPSourceInfo::Parse(char* sdpData, UInt32 sdpLen)
{
//
// There are some situations in which Parse can be called twice.
// If that happens, just return and don't do anything the second time.
if (fSDPData.Ptr != NULL)
return;
Assert(fStreamArray == NULL);
char *sdpDataCopy = NEW char[sdpLen];
Assert(sdpDataCopy != NULL);
memcpy(sdpDataCopy,sdpData, sdpLen);
fSDPData.Set(sdpDataCopy, sdpLen);
// If there is no trackID information in this SDP, we make the track IDs start
// at 1 -> N
UInt32 currentTrack = 1;
Bool16 hasGlobalStreamInfo = false;
StreamInfo theGlobalStreamInfo; //needed if there is one c= header independent of
//individual streams
StrPtrLen sdpLine;
StringParser trackCounter(&fSDPData);
StringParser sdpParser(&fSDPData);
UInt32 theStreamIndex = 0;
//walk through the SDP, counting up the number of tracks
// Repeat until there's no more data in the SDP
while (trackCounter.GetDataRemaining() > 0)
{
//each 'm' line in the SDP file corresponds to another track.
trackCounter.GetThruEOL(&sdpLine);
if ((sdpLine.Len > 0) && (sdpLine.Ptr[0] == 'm'))
fNumStreams++;
}
//We should scale the # of StreamInfos to the # of trax, but we can't because
//of an annoying compiler bug...
fStreamArray = NEW StreamInfo[fNumStreams];
::memset(fStreamArray, 0, sizeof(StreamInfo) * fNumStreams);
// set the default destination as our default IP address and set the default ttl
theGlobalStreamInfo.fDestIPAddr = INADDR_ANY;
theGlobalStreamInfo.fTimeToLive = kDefaultTTL;
//Set bufferdelay to default of 3
theGlobalStreamInfo.fBufferDelay = (Float32) eDefaultBufferDelay;
//Now actually get all the data on all the streams
while (sdpParser.GetDataRemaining() > 0)
{
sdpParser.GetThruEOL(&sdpLine);
if (sdpLine.Len == 0)
continue;//skip over any blank lines
switch (*sdpLine.Ptr)
{
case 't':
{
StringParser mParser(&sdpLine);
mParser.ConsumeUntil(NULL, StringParser::sDigitMask);
UInt32 ntpStart = mParser.ConsumeInteger(NULL);
mParser.ConsumeUntil(NULL, StringParser::sDigitMask);
UInt32 ntpEnd = mParser.ConsumeInteger(NULL);
SetActiveNTPTimes(ntpStart,ntpEnd);
}
break;
case 'm':
{
if (hasGlobalStreamInfo)
{
fStreamArray[theStreamIndex].fDestIPAddr = theGlobalStreamInfo.fDestIPAddr;
fStreamArray[theStreamIndex].fTimeToLive = theGlobalStreamInfo.fTimeToLive;
}
fStreamArray[theStreamIndex].fTrackID = currentTrack;
currentTrack++;
StringParser mParser(&sdpLine);
//find out what type of track this is
mParser.ConsumeLength(NULL, 2);//go past 'm='
StrPtrLen theStreamType;
mParser.ConsumeWord(&theStreamType);
if (theStreamType.Equal(sVideoStr))
fStreamArray[theStreamIndex].fPayloadType = qtssVideoPayloadType;
else if (theStreamType.Equal(sAudioStr))
fStreamArray[theStreamIndex].fPayloadType = qtssAudioPayloadType;
//find the port for this stream
mParser.ConsumeUntil(NULL, StringParser::sDigitMask);
SInt32 tempPort = mParser.ConsumeInteger(NULL);
if ((tempPort > 0) && (tempPort < 65536))
fStreamArray[theStreamIndex].fPort = (UInt16) tempPort;
// find out whether this is TCP or UDP
mParser.ConsumeWhitespace();
StrPtrLen transportID;
mParser.ConsumeWord(&transportID);
static const StrPtrLen kTCPTransportStr("RTP/AVP/TCP");
if (transportID.Equal(kTCPTransportStr))
fStreamArray[theStreamIndex].fIsTCP = true;
theStreamIndex++;
}
break;
case 'a':
{
StringParser aParser(&sdpLine);
aParser.ConsumeLength(NULL, 2);//go past 'a='
StrPtrLen aLineType;
aParser.ConsumeWord(&aLineType);
if (aLineType.Equal(sBroadcastControlStr))
{ // found a control line for the broadcast (delete at time or delete at end of broadcast/server startup)
// qtss_printf("found =%s\n",sBroadcastControlStr);
aParser.ConsumeUntil(NULL,StringParser::sWordMask);
StrPtrLen sessionControlType;
aParser.ConsumeWord(&sessionControlType);
if (sessionControlType.Equal(sAutoDisconnect))
{
fSessionControlType = kRTSPSessionControl;
}
else if (sessionControlType.Equal(sAutoDisconnectTime))
{
fSessionControlType = kSDPTimeControl;
}
}
//if we haven't even hit an 'm' line yet, just ignore all 'a' lines
if (theStreamIndex == 0)
break;
if (aLineType.Equal(sRtpMapStr))
{
//mark the codec type if this line has a codec name on it. If we already
//have a codec type for this track, just ignore this line
if ((fStreamArray[theStreamIndex - 1].fPayloadName.Len == 0) &&
(aParser.GetThru(NULL, ' ')))
{
StrPtrLen payloadNameFromParser;
(void)aParser.GetThruEOL(&payloadNameFromParser);
char* temp = payloadNameFromParser.GetAsCString();
// qtss_printf("payloadNameFromParser (%x) = %s\n", temp, temp);
(fStreamArray[theStreamIndex - 1].fPayloadName).Set(temp, payloadNameFromParser.Len);
// qtss_printf("%s\n", fStreamArray[theStreamIndex - 1].fPayloadName.Ptr);
}
}
else if (aLineType.Equal(sControlStr))
{
//mark the trackID if that's what this line has
aParser.ConsumeUntil(NULL, '=');
aParser.ConsumeUntil(NULL, StringParser::sDigitMask);
fStreamArray[theStreamIndex - 1].fTrackID = aParser.ConsumeInteger(NULL);
}
else if (aLineType.Equal(sBufferDelayStr))
{ // if a BufferDelay is found then set all of the streams to the same buffer delay (it's global)
aParser.ConsumeUntil(NULL, StringParser::sDigitMask);
theGlobalStreamInfo.fBufferDelay = aParser.ConsumeFloat();
}
}
break;
case 'c':
{
//get the IP address off this header
StringParser cParser(&sdpLine);
cParser.ConsumeLength(NULL, 9);//strip off "c=in ip4 "
UInt32 tempIPAddr = SDPSourceInfo::GetIPAddr(&cParser, '/');
//grab the ttl
SInt32 tempTtl = kDefaultTTL;
if (cParser.GetThru(NULL, '/'))
{
tempTtl = cParser.ConsumeInteger(NULL);
Assert(tempTtl >= 0);
Assert(tempTtl < 65536);
}
if (theStreamIndex > 0)
{
//if this c= line is part of a stream, it overrides the
//global stream information
fStreamArray[theStreamIndex - 1].fDestIPAddr = tempIPAddr;
fStreamArray[theStreamIndex - 1].fTimeToLive = (UInt16) tempTtl;
} else {
theGlobalStreamInfo.fDestIPAddr = tempIPAddr;
theGlobalStreamInfo.fTimeToLive = (UInt16) tempTtl;
hasGlobalStreamInfo = true;
}
}
}
}
// Add the default buffer delay
Float32 bufferDelay = (Float32) eDefaultBufferDelay;
if (theGlobalStreamInfo.fBufferDelay != (Float32) eDefaultBufferDelay)
bufferDelay = theGlobalStreamInfo.fBufferDelay;
UInt32 count = 0;
while (count < fNumStreams)
{ fStreamArray[count].fBufferDelay = bufferDelay;
count ++;
}
}
UInt32 SDPSourceInfo::GetIPAddr(StringParser* inParser, char inStopChar)
{
StrPtrLen ipAddrStr;
// Get the IP addr str
inParser->ConsumeUntil(&ipAddrStr, inStopChar);
if (ipAddrStr.Len == 0)
return 0;
// NULL terminate it
char endChar = ipAddrStr.Ptr[ipAddrStr.Len];
ipAddrStr.Ptr[ipAddrStr.Len] = '\0';
//inet_addr returns numeric IP addr in network byte order, make
//sure to convert to host order.
UInt32 ipAddr = SocketUtils::ConvertStringToAddr(ipAddrStr.Ptr);
// Make sure to put the old char back!
ipAddrStr.Ptr[ipAddrStr.Len] = endChar;
return ipAddr;
}

View file

@ -0,0 +1,77 @@
/*
*
* @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: SDPSourceInfo.h
Contains: This object takes input SDP data, and uses it to support the SourceInfo
API.
Works only for QTSS
*/
#ifndef __SDP_SOURCE_INFO_H__
#define __SDP_SOURCE_INFO_H__
#include "StrPtrLen.h"
#include "SourceInfo.h"
#include "StringParser.h"
class SDPSourceInfo : public SourceInfo
{
public:
// Uses the SDP Data to build up the StreamInfo structures
SDPSourceInfo(char* sdpData, UInt32 sdpLen) { Parse(sdpData, sdpLen); }
SDPSourceInfo() {}
virtual ~SDPSourceInfo();
// Parses out the SDP file provided, sets up the StreamInfo structures
void Parse(char* sdpData, UInt32 sdpLen);
// This function uses the Parsed SDP file, and strips out all the network information,
// producing an SDP file that appears to be local.
virtual char* GetLocalSDP(UInt32* newSDPLen);
// Returns the SDP data
StrPtrLen* GetSDPData() { return &fSDPData; }
// Utility routines
// Assuming the parser is currently pointing at the beginning of an dotted-
// decimal IP address, this consumes it (stopping at inStopChar), and returns
// the IP address (host ordered) as a UInt32
static UInt32 GetIPAddr(StringParser* inParser, char inStopChar);
private:
enum
{
kDefaultTTL = 15 //UInt16
};
StrPtrLen fSDPData;
};
#endif // __SDP_SOURCE_INFO_H__

View file

@ -0,0 +1,329 @@
/*
*
* @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: SourceInfo.cpp
Contains: Implements object defined in .h file.
*/
#include "SourceInfo.h"
#include "SocketUtils.h"
#include "SDPSourceInfo.h"
#include "OSMemory.h"
#include "StringParser.h"
SourceInfo::SourceInfo(const SourceInfo& copy)
: fStreamArray(NULL), fNumStreams(copy.fNumStreams),
fOutputArray(NULL), fNumOutputs(copy.fNumOutputs),
fTimeSet(copy.fTimeSet),fStartTimeUnixSecs(copy.fStartTimeUnixSecs),
fEndTimeUnixSecs(copy.fEndTimeUnixSecs), fSessionControlType(copy.fSessionControlType),
fHasValidTime(false)
{
if(copy.fStreamArray != NULL && fNumStreams != 0)
{
fStreamArray = NEW StreamInfo[fNumStreams];
for (UInt32 index=0; index < fNumStreams; index++)
fStreamArray[index].Copy(copy.fStreamArray[index]);
}
if(copy.fOutputArray != NULL && fNumOutputs != 0)
{
fOutputArray = NEW OutputInfo[fNumOutputs];
for (UInt32 index2=0; index2 < fNumOutputs; index2++)
fOutputArray[index2].Copy(copy.fOutputArray[index2]);
}
}
SourceInfo::~SourceInfo()
{
if(fStreamArray != NULL)
delete [] fStreamArray;
if(fOutputArray != NULL)
delete [] fOutputArray;
}
Bool16 SourceInfo::IsReflectable()
{
if (fStreamArray == NULL)
return false;
if (fNumStreams == 0)
return false;
//each stream's info must meet certain criteria
for (UInt32 x = 0; x < fNumStreams; x++)
{
if (fStreamArray[x].fIsTCP)
continue;
if ((!this->IsReflectableIPAddr(fStreamArray[x].fDestIPAddr)) ||
(fStreamArray[x].fTimeToLive == 0))
return false;
}
return true;
}
Bool16 SourceInfo::IsReflectableIPAddr(UInt32 inIPAddr)
{
if (SocketUtils::IsMulticastIPAddr(inIPAddr) || SocketUtils::IsLocalIPAddr(inIPAddr))
return true;
return false;
}
Bool16 SourceInfo::HasTCPStreams()
{
//each stream's info must meet certain criteria
for (UInt32 x = 0; x < fNumStreams; x++)
{
if (fStreamArray[x].fIsTCP)
return true;
}
return false;
}
Bool16 SourceInfo::HasIncomingBroacast()
{
//each stream's info must meet certain criteria
for (UInt32 x = 0; x < fNumStreams; x++)
{
if (fStreamArray[x].fSetupToReceive)
return true;
}
return false;
}
SourceInfo::StreamInfo* SourceInfo::GetStreamInfo(UInt32 inIndex)
{
Assert(inIndex < fNumStreams);
if (fStreamArray == NULL)
return NULL;
if (inIndex < fNumStreams)
return &fStreamArray[inIndex];
else
return NULL;
}
SourceInfo::StreamInfo* SourceInfo::GetStreamInfoByTrackID(UInt32 inTrackID)
{
if (fStreamArray == NULL)
return NULL;
for (UInt32 x = 0; x < fNumStreams; x++)
{
if (fStreamArray[x].fTrackID == inTrackID)
return &fStreamArray[x];
}
return NULL;
}
SourceInfo::OutputInfo* SourceInfo::GetOutputInfo(UInt32 inIndex)
{
Assert(inIndex < fNumOutputs);
if (fOutputArray == NULL)
return NULL;
if (inIndex < fNumOutputs)
return &fOutputArray[inIndex];
else
return NULL;
}
UInt32 SourceInfo::GetNumNewOutputs()
{
UInt32 theNumNewOutputs = 0;
for (UInt32 x = 0; x < fNumOutputs; x++)
{
if (!fOutputArray[x].fAlreadySetup)
theNumNewOutputs++;
}
return theNumNewOutputs;
}
Bool16 SourceInfo::SetActiveNTPTimes(UInt32 startTimeNTP,UInt32 endTimeNTP)
{ // right now only handles earliest start and latest end time.
//qtss_printf("SourceInfo::SetActiveNTPTimes start=%"_U32BITARG_" end=%"_U32BITARG_"\n",startTimeNTP,endTimeNTP);
Bool16 accepted = false;
do
{
if ((startTimeNTP > 0) && (endTimeNTP > 0) && (endTimeNTP < startTimeNTP)) break; // not valid NTP time
UInt32 startTimeUnixSecs = 0;
UInt32 endTimeUnixSecs = 0;
if (startTimeNTP != 0 && IsValidNTPSecs(startTimeNTP)) // allow anything less than 1970
startTimeUnixSecs = NTPSecs_to_UnixSecs(startTimeNTP);// convert to 1970 time
if (endTimeNTP != 0 && !IsValidNTPSecs(endTimeNTP)) // don't allow anything less than 1970
break;
if (endTimeNTP != 0) // convert to 1970 time
endTimeUnixSecs = NTPSecs_to_UnixSecs(endTimeNTP);
fStartTimeUnixSecs = startTimeUnixSecs;
fEndTimeUnixSecs = endTimeUnixSecs;
accepted = true;
} while(0);
//char buffer[kTimeStrSize];
//qtss_printf("SourceInfo::SetActiveNTPTimes fStartTimeUnixSecs=%"_U32BITARG_" fEndTimeUnixSecs=%"_U32BITARG_"\n",fStartTimeUnixSecs,fEndTimeUnixSecs);
//qtss_printf("SourceInfo::SetActiveNTPTimes start time = %s",qtss_ctime(&fStartTimeUnixSecs, buffer, sizeof(buffer)) );
//qtss_printf("SourceInfo::SetActiveNTPTimes end time = %s",qtss_ctime(&fEndTimeUnixSecs, buffer, sizeof(buffer)) );
fHasValidTime = accepted;
return accepted;
}
Bool16 SourceInfo::IsActiveTime(time_t unixTimeSecs)
{
// order of tests are important here
// we do it this way because of the special case time value of 0 for end time
// start - 0 = unbounded
// 0 - 0 = permanent
if (false == fHasValidTime)
return false;
if (unixTimeSecs < 0) //check valid value
return false;
if (IsPermanentSource()) //check for 0 0
return true;
if (unixTimeSecs < fStartTimeUnixSecs)
return false; //too early
if (fEndTimeUnixSecs == 0)
return true;// accept any time after start
if (unixTimeSecs > fEndTimeUnixSecs)
return false; // too late
return true; // ok start <= time <= end
}
UInt32 SourceInfo::GetDurationSecs()
{
if (fEndTimeUnixSecs == 0) // unbounded time
return (UInt32) ~0; // max time
time_t timeNow = OS::UnixTime_Secs();
if (fEndTimeUnixSecs <= timeNow) // the active time has past or duration is 0 so return the minimum duration
return (UInt32) 0;
if (fStartTimeUnixSecs == 0) // relative duration = from "now" to end time
return fEndTimeUnixSecs - timeNow;
return fEndTimeUnixSecs - fStartTimeUnixSecs; // this must be a duration because of test for endtime above
}
Bool16 SourceInfo::Equal(SourceInfo* inInfo)
{
// Check to make sure the # of streams matches up
if (this->GetNumStreams() != inInfo->GetNumStreams())
return false;
// Check the src & dest addr, and port of each stream.
for (UInt32 x = 0; x < this->GetNumStreams(); x++)
{
if (GetStreamInfo(x)->fDestIPAddr != inInfo->GetStreamInfo(x)->fDestIPAddr)
return false;
if (GetStreamInfo(x)->fSrcIPAddr != inInfo->GetStreamInfo(x)->fSrcIPAddr)
return false;
// If either one of the comparators is 0 (the "wildcard" port), then we know at this point
// they are equivalent
if ((GetStreamInfo(x)->fPort == 0) || (inInfo->GetStreamInfo(x)->fPort == 0))
return true;
// Neither one is the wildcard port, so they must be the same
if (GetStreamInfo(x)->fPort != inInfo->GetStreamInfo(x)->fPort)
return false;
}
return true;
}
void SourceInfo::StreamInfo::Copy(const StreamInfo& copy)
{
fSrcIPAddr = copy.fSrcIPAddr;
fDestIPAddr = copy.fDestIPAddr;
fPort = copy.fPort;
fTimeToLive = copy.fTimeToLive;
fPayloadType = copy.fPayloadType;
if ((copy.fPayloadName).Ptr != NULL)
fPayloadName.Set((copy.fPayloadName).GetAsCString(), (copy.fPayloadName).Len);
fTrackID = copy.fTrackID;
fBufferDelay = copy.fBufferDelay;
fIsTCP = copy.fIsTCP;
fSetupToReceive = copy.fSetupToReceive;
fTimeScale = copy.fTimeScale;
}
SourceInfo::StreamInfo::~StreamInfo()
{
if (fPayloadName.Ptr != NULL)
delete fPayloadName.Ptr;
fPayloadName.Len = 0;
}
void SourceInfo::OutputInfo::Copy(const OutputInfo& copy)
{
fDestAddr = copy.fDestAddr;
fLocalAddr = copy.fLocalAddr;
fTimeToLive = copy.fTimeToLive;
fNumPorts = copy.fNumPorts;
if(fNumPorts != 0)
{
fPortArray = NEW UInt16[fNumPorts];
::memcpy(fPortArray, copy.fPortArray, fNumPorts * sizeof(UInt16));
}
fBasePort = copy.fBasePort;
fAlreadySetup = copy.fAlreadySetup;
}
SourceInfo::OutputInfo::~OutputInfo()
{
if (fPortArray != NULL)
delete [] fPortArray;
}
Bool16 SourceInfo::OutputInfo::Equal(const OutputInfo& info)
{
if ((fDestAddr == info.fDestAddr) && (fLocalAddr == info.fLocalAddr) && (fTimeToLive == info.fTimeToLive))
{
if ((fBasePort != 0) && (fBasePort == info.fBasePort))
return true;
else if ((fNumPorts == 0) || ((fNumPorts == info.fNumPorts) && (fPortArray[0] == info.fPortArray[0])))
return true;
}
return false;
}

174
APICommonCode/SourceInfo.h Normal file
View file

@ -0,0 +1,174 @@
/*
*
* @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: SourceInfo.h
Contains: This object contains an interface for getting at any bit
of "interesting" information regarding a content source in a
format - independent manner.
For instance, the derived object SDPSourceInfo parses an
SDP file and retrieves all the SourceInfo information from that file.
*/
#ifndef __SOURCE_INFO_H__
#define __SOURCE_INFO_H__
#include "QTSS.h"
#include "StrPtrLen.h"
#include "OSQueue.h"
#include "OS.h"
class SourceInfo
{
public:
SourceInfo() : fStreamArray(NULL), fNumStreams(0),
fOutputArray(NULL), fNumOutputs(0),
fTimeSet(false),fStartTimeUnixSecs(0),fEndTimeUnixSecs(0),
fSessionControlType(kRTSPSessionControl) {}
SourceInfo(const SourceInfo& copy);// Does copy dynamically allocated data
virtual ~SourceInfo(); // Deletes the dynamically allocated data
enum
{
eDefaultBufferDelay = 3
};
// Returns whether this source is reflectable.
Bool16 IsReflectable();
// Each source is comprised of a set of streams. Those streams have
// the following metadata.
struct StreamInfo
{
StreamInfo() : fSrcIPAddr(0), fDestIPAddr(0), fPort(0), fTimeToLive(0), fPayloadType(0), fPayloadName(NULL), fTrackID(0), fBufferDelay((Float32) eDefaultBufferDelay), fIsTCP(false),fSetupToReceive(false), fTimeScale(0){}
~StreamInfo(); // Deletes the memory allocated for the fPayloadName string
void Copy(const StreamInfo& copy);// Does copy dynamically allocated data
UInt32 fSrcIPAddr; // Src IP address of content (this may be 0 if not known for sure)
UInt32 fDestIPAddr; // Dest IP address of content (destination IP addr for source broadcast!)
UInt16 fPort; // Dest (RTP) port of source content
UInt16 fTimeToLive; // Ttl for this stream
QTSS_RTPPayloadType fPayloadType; // Payload type of this stream
StrPtrLen fPayloadName; // Payload name of this stream
UInt32 fTrackID; // ID of this stream
Float32 fBufferDelay; // buffer delay (default is 3 seconds)
Bool16 fIsTCP; // Is this a TCP broadcast? If this is the case, the port and ttl are not valid
Bool16 fSetupToReceive; // If true then a push to the server is setup on this stream.
UInt32 fTimeScale;
};
// Returns the number of StreamInfo objects (number of Streams in this source)
UInt32 GetNumStreams() { return fNumStreams; }
StreamInfo* GetStreamInfo(UInt32 inStreamIndex);
StreamInfo* GetStreamInfoByTrackID(UInt32 inTrackID);
// If this source is to be Relayed, it may have "Output" information. This
// tells the reader where to forward the incoming streams onto. There may be
// 0 -> N OutputInfo objects in this SourceInfo. Each OutputInfo refers to a
// single, complete copy of ALL the input streams. The fPortArray field
// contains one RTP port for each incoming stream.
struct OutputInfo
{
OutputInfo() : fDestAddr(0), fLocalAddr(0), fTimeToLive(0), fPortArray(NULL), fNumPorts(0), fBasePort(0), fAlreadySetup(false) {}
~OutputInfo(); // Deletes the memory allocated for fPortArray
// Returns true if the two are equal
Bool16 Equal(const OutputInfo& info);
void Copy(const OutputInfo& copy);// Does copy dynamically allocated data
UInt32 fDestAddr; // Destination address to forward the input onto
UInt32 fLocalAddr; // Address of local interface to send out on (may be 0)
UInt16 fTimeToLive; // Time to live for resulting output (if multicast)
UInt16* fPortArray; // 1 destination RTP port for each Stream.
UInt32 fNumPorts; // Size of the fPortArray (usually equal to fNumStreams)
UInt16 fBasePort; // The base destination RTP port - for i=1 to fNumStreams fPortArray[i] = fPortArray[i-1] + 2
Bool16 fAlreadySetup; // A flag used in QTSSReflectorModule.cpp
};
// Returns the number of OutputInfo objects.
UInt32 GetNumOutputs() { return fNumOutputs; }
UInt32 GetNumNewOutputs(); // Returns # of outputs not already setup
OutputInfo* GetOutputInfo(UInt32 inOutputIndex);
// GetLocalSDP. This may or may not be supported by sources. Typically, if
// the source is reflectable, this must be supported. It returns a newly
// allocated buffer (that the caller is responsible for) containing an SDP
// description of the source, stripped of all network info.
virtual char* GetLocalSDP(UInt32* /*newSDPLen*/) { return NULL; }
// This is only supported by the RTSPSourceInfo sub class
virtual Bool16 IsRTSPSourceInfo() { return false; }
// This is only supported by the RCFSourceInfo sub class and its derived classes
virtual char* Name() { return NULL; }
virtual Bool16 Equal(SourceInfo* inInfo);
// SDP scheduled times supports earliest start and latest end -- doesn't handle repeat times or multiple active times.
#define kNTP_Offset_From_1970 2208988800LU
time_t NTPSecs_to_UnixSecs(time_t time) {return (time_t) (time - (UInt32)kNTP_Offset_From_1970);}
UInt32 UnixSecs_to_NTPSecs(time_t time) {return (UInt32) (time + (UInt32)kNTP_Offset_From_1970);}
Bool16 SetActiveNTPTimes(UInt32 startNTPTime,UInt32 endNTPTime);
Bool16 IsValidNTPSecs(UInt32 time) {return time >= (UInt32) kNTP_Offset_From_1970 ? true : false;}
Bool16 IsPermanentSource() { return ((fStartTimeUnixSecs == 0) && (fEndTimeUnixSecs == 0)) ? true : false; }
Bool16 IsActiveTime(time_t unixTimeSecs);
Bool16 IsActiveNow() { return IsActiveTime(OS::UnixTime_Secs()); }
Bool16 IsRTSPControlled() {return (fSessionControlType == kRTSPSessionControl) ? true : false; }
Bool16 HasTCPStreams();
Bool16 HasIncomingBroacast();
time_t GetStartTimeUnixSecs() {return fStartTimeUnixSecs; }
time_t GetEndTimeUnixSecs() {return fEndTimeUnixSecs; }
UInt32 GetDurationSecs();
enum {kSDPTimeControl, kRTSPSessionControl};
protected:
//utility function used by IsReflectable
Bool16 IsReflectableIPAddr(UInt32 inIPAddr);
StreamInfo* fStreamArray;
UInt32 fNumStreams;
OutputInfo* fOutputArray;
UInt32 fNumOutputs;
Bool16 fTimeSet;
time_t fStartTimeUnixSecs;
time_t fEndTimeUnixSecs;
UInt32 fSessionControlType;
Bool16 fHasValidTime;
};
#endif //__SOURCE_INFO_H__