Add even more of the source

This should be about everything needed to build so far?
This commit is contained in:
Darren VanBuren 2017-03-07 17:14:16 -08:00
parent af3619d4fa
commit 849723c9cf
547 changed files with 149239 additions and 0 deletions

4
.gitignore vendored
View file

@ -1 +1,5 @@
*.o
Makefile.POSIX
cmake-build-debug
build

1
.idea/.name generated Normal file
View file

@ -0,0 +1 @@
DarwinStreamingServer

2
.idea/DSS6.0.3.iml generated Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/DSS6.0.3.iml" filepath="$PROJECT_DIR$/.idea/DSS6.0.3.iml" />
</modules>
</component>
</project>

View file

@ -0,0 +1,72 @@
/*
*
* @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: OSMemory_Modules.cpp
Contains:
*/
#include "OSMemory.h"
#include "QTSS.h"
#if MEMORY_DEBUGGING
void* operator new(size_t s, char* /*inFile*/, int /*inLine*/)
{
return QTSS_New (FOUR_CHARS_TO_INT('0', '0', '0', '0'), s);
}
void* operator new[](size_t s, char* /*inFile*/, int /*inLine*/)
{
return QTSS_New (FOUR_CHARS_TO_INT('0', '0', '0', '0'), s);
}
#else
void* operator new (size_t s)
{
return QTSS_New (FOUR_CHARS_TO_INT('0', '0', '0', '0'), s);
}
void* operator new[](size_t s)
{
return QTSS_New (FOUR_CHARS_TO_INT('0', '0', '0', '0'), s);
}
void operator delete(void* mem)
{
QTSS_Delete(mem);
}
void operator delete[](void* mem)
{
QTSS_Delete(mem);
}
#endif //__OS_MEMORY_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
/*
*
* @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: QTSSAccessLogModule.h
Contains: A QTSS API module that runs as an RTSP Post Processor and an RTP
timeout processor. It generates access log files compatible with
the Lariat Logging tool.
*/
#ifndef _QTSSACCESSLOGMODULE_H_
#define _QTSSACCESSLOGMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSAccessLogModule_Main(void* inPrivateArgs);
}
#endif //_QTSSACCESSLOGMODULE_H_

View file

@ -0,0 +1,426 @@
/*
*
* @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: AccessChecker.cpp
Contains:
*/
#include <signal.h>
#ifndef __Win32__
#ifndef __USE_XOPEN
#define __USE_XOPEN 1
#endif
#include <unistd.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include "StringParser.h"
#include "OSFileSource.h"
#include "base64.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "AccessChecker.h"
#include "QTSSModuleUtils.h"
#include "OSArrayObjectDeleter.h"
static StrPtrLen sAuthWord("realm", 5);
// Constructor
// Allocates no memory
AccessChecker::AccessChecker() :
fGroupsFilePath(NULL),
fUsersFilePath(NULL),
fUsersFileModDate(-1),
fGroupsFileModDate(-1),
fProfiles(NULL),
fNumUsers(0),
fCurrentSize(0)
{
}
// Destructor
// Deletes the fUsersFilePath, fGroupsFilePath,
// and calls the function to delete the authRealm and all the profiles
AccessChecker::~AccessChecker()
{
delete[] fGroupsFilePath;
delete[] fUsersFilePath;
DeleteProfilesAndRealm();
}
// Allocates memory for the fUsersFilePath and fGroupsFilePath
// Before this call is made, make sure that the previous memory allocated is deleted
// or that memory will be orphaned!
void AccessChecker::UpdateFilePaths(const char* inUsersFilePath, const char* inGroupsFilePath) {
// Assert input arguments are not null
Assert(inUsersFilePath != NULL);
Assert(inGroupsFilePath != NULL);
// Before reassigning, delete old paths
delete[] fGroupsFilePath;
delete[] fUsersFilePath;
fUsersFilePath = NEW char[strlen(inUsersFilePath)+1];
::strcpy(fUsersFilePath, inUsersFilePath);
fGroupsFilePath = NEW char[strlen(inGroupsFilePath)+1];
::strcpy(fGroupsFilePath, inGroupsFilePath);
}
// Function to delete memory allocated for all the profiles, and the authRealm
// For each profile, memory is allocated for
// username
// cryptPassword
// digestPassword
// each group that the user belongs to (array of group names)
// All of the above are deleted
void AccessChecker::DeleteProfilesAndRealm()
{
UInt32 i, j;
// delete the profiles
if(fProfiles != NULL)
{
// For each profile
for(i = 0; i < fNumUsers; i++)
{
UserProfile* profile = fProfiles[i];
// delete the username
if((profile->username).Len != 0)
{
delete (profile->username).Ptr;
(profile->username).Len = 0;
}
// delete cryptPassword
if((profile->cryptPassword).Len != 0)
{
delete (profile->cryptPassword).Ptr;
(profile->cryptPassword).Len = 0;
}
// delete digestPassword
if((profile->digestPassword).Len != 0)
{
delete (profile->digestPassword).Ptr;
(profile->digestPassword).Len = 0;
}
// delete each group name
for(j = 0; j < profile->numGroups; j++)
{
delete [] profile->groups[j];
}
// delete the array of pointers to the group names
delete [] profile->groups;
profile->groups = NULL;
profile->maxGroupNameLen = 0;
profile->numGroups = 0;
profile->groupsSize = 0;
delete profile;
}
// delete the array of profile pointers
delete [] fProfiles;
fProfiles = NULL;
}
// delete the fAuthRealm field
if(fAuthRealm.Len != 0) {
delete fAuthRealm.Ptr;
fAuthRealm.Len = 0;
}
fNumUsers = 0;
fCurrentSize = 0;
}
// Memory is allocated for each username record found in the users file
// Memory is also allocated for each group name found in the groups file per user
// All this memory must be deleted if the profiles are deleted, before parsing
// the file again
UInt32 AccessChecker::UpdateUserProfiles() {
UInt32 index = 0;
UInt32 i = 0, j = 0;
UInt32 resultErr = kNoErr;
Bool16 groupFileErrors = true;
Bool16 userFileErrors = true;
StrPtrLen line;
QTSS_TimeVal oldUsersFileModDate = fUsersFileModDate;
QTSS_TimeVal oldGroupsFileModDate = fGroupsFileModDate;
// Read the users file into a buffer
StrPtrLen userData;
QTSS_TimeVal newModDate = -1;
QTSS_Error err = QTSS_NoErr;
// QTSSModuleUtils::ReadEntireFile allocates memory for userData
err = QTSSModuleUtils::ReadEntireFile(fUsersFilePath, &userData, fUsersFileModDate, &newModDate);
if(err == QTSS_FileNotFound)
resultErr |= kUsersFileNotFoundErr;
else if(err != QTSS_NoErr)
resultErr |= kUsersFileUnknownErr;
else
userFileErrors = false;
if(userFileErrors)
fUsersFileModDate = -1;
else if(userData.Len != 0)
fUsersFileModDate = newModDate;
newModDate = -1;
// Read the groups file into a buffer
StrPtrLen groupData;
// QTSSModuleUtils::ReadEntireFile allocates memory for groupData
err = QTSSModuleUtils::ReadEntireFile(fGroupsFilePath, &groupData, fGroupsFileModDate, &newModDate);
if(err == QTSS_FileNotFound)
resultErr |= kGroupsFileNotFoundErr;
else if(err != QTSS_NoErr)
resultErr |= kGroupsFileUnknownErr;
else
groupFileErrors = false;
if(groupFileErrors)
fGroupsFileModDate = -1;
else if(groupData.Len != 0)
fGroupsFileModDate = newModDate;
if(userFileErrors)
{
// delete user profiles and exit
DeleteProfilesAndRealm();
return resultErr;
}
if((fUsersFileModDate == oldUsersFileModDate) && (fGroupsFileModDate == oldGroupsFileModDate))
return resultErr;
// If either the users or groups file has changed
// the old profiles and the old realm should be deleted
// before a new array of profiles is created and a new realm from the users file is read
DeleteProfilesAndRealm();
// Since one or both of the files has changed, reread the files and create user profiles
if(userData.Len == 0)
(void)QTSSModuleUtils::ReadEntireFile(fUsersFilePath, &userData, -1, NULL);
if(groupData.Len == 0 && !groupFileErrors)
(void)QTSSModuleUtils::ReadEntireFile(fGroupsFilePath, &groupData, -1, NULL);
// This will delete the memory allocated for userData when we return from this function
OSCharArrayDeleter userDataPtrDeleter(userData.Ptr);
// This will delete the memory allocated for groupData when we return from this function
OSCharArrayDeleter groupDataPtrDeleter(groupData.Ptr);
// Create the fProfiles array of size kDefaultNumProfiles
fProfiles = NEW UserProfile*[kDefaultNumProfiles];
fCurrentSize = kDefaultNumProfiles;
StringParser userDataParser(&userData);
// check if the first line is "realm"
while(true) {
StrPtrLen word;
userDataParser.GetThruEOL(&line);
StringParser authLineParser(&line);
// Skip over leading whitespace
authLineParser.ConsumeUntil(NULL, StringParser::sWhitespaceMask);
// Skip over comments and blank lines
if ((authLineParser.GetDataRemaining() == 0) || (authLineParser[0] == '#') || (authLineParser[0] == '\0') )
continue;
authLineParser.ConsumeWord(&word);
if(sAuthWord.Equal(word)) {
authLineParser.ConsumeWhitespace();
authLineParser.ConsumeUntil(&word, StringParser::sEOLMask);
fAuthRealm.Set(word.GetAsCString(), word.Len);
}
else {
// This shouldn't happen because it means that the realm line
// is not the first non-commented out line in the file
// Implies the users file is corrupted!
resultErr |= kBadUsersFileErr;
// Create a new user profile for the first username
UserProfile* profile = NEW UserProfile;
profile->groups = NEW char*[kDefaultNumGroups];
profile->maxGroupNameLen = 0;
profile->numGroups = 0;
profile->groupsSize = kDefaultNumGroups;
(profile->username).Set(word.GetAsCString(), word.Len);
// Get the crypted password
if ( authLineParser.Expect(':') )
{
authLineParser.ConsumeUntil(&word, ':');
(profile->cryptPassword).Set(word.GetAsCString(), word.Len);
// Get the digest password
authLineParser.GetThruEOL(&word);
(profile->digestPassword).Set(word.GetAsCString(), word.Len);
}
fProfiles[index] = profile;
index ++;
}
break;
}
while(userDataParser.GetDataRemaining() != 0) {
// Read each line
userDataParser.GetThruEOL(&line);
StringParser userLineParser(&line);
//parse the line
//skip over leading whitespace
userLineParser.ConsumeUntil(NULL, StringParser::sWhitespaceMask);
//skip over comments and blank lines
if ((userLineParser.GetDataRemaining() == 0) || (userLineParser[0] == '#') || (userLineParser[0] == '\0') )
continue;
// Create a new user profile for each username found
UserProfile* profile = NEW UserProfile;
profile->groups = NEW char*[kDefaultNumGroups];
profile->maxGroupNameLen = 0;
profile->numGroups = 0;
profile->groupsSize = kDefaultNumGroups;
StrPtrLen word;
userLineParser.ConsumeUntil(&word, ':');
(profile->username).Set(word.GetAsCString(), word.Len);
// Get the crypted password
if ( userLineParser.Expect(':') )
{
userLineParser.ConsumeUntil(&word, ':');
(profile->cryptPassword).Set(word.GetAsCString(), word.Len);
if(userLineParser.Expect(':')) {
// Get the digest password
userLineParser.GetThruEOL(&word);
(profile->digestPassword).Set(word.GetAsCString(), word.Len);
}
}
if(index >= fCurrentSize) {
UserProfile** oldProfiles = fProfiles;
fProfiles = NEW UserProfile*[fCurrentSize * 2];
for(i = 0; i < fCurrentSize; i++)
{
fProfiles[i] = oldProfiles[i];
}
fCurrentSize *= 2;
delete [] oldProfiles;
}
fProfiles[index] = profile;
index ++;
}
fNumUsers = index;
if(!groupFileErrors)
{
StringParser groupDataParser(&groupData);
while(groupDataParser.GetDataRemaining() != 0) {
// Read each line
groupDataParser.GetThruEOL(&line);
StringParser groupLineParser(&line);
//parse the line
//skip over leading whitespace
groupLineParser.ConsumeUntil(NULL, StringParser::sWhitespaceMask);
//skip over comments and blank lines
if ((groupLineParser.GetDataRemaining() == 0) || (groupLineParser[0] == '#') || (groupLineParser[0] == '\0') )
continue;
//parse the groupname
StrPtrLen groupName;
groupLineParser.ConsumeUntil(&groupName, ':');
if (groupLineParser.Expect(':')) {
StrPtrLen groupUser;
UInt32 nameLen = groupName.Len + 1;
while(groupLineParser.GetDataRemaining() != 0)
{
groupLineParser.ConsumeWhitespace();
groupLineParser.ConsumeUntilWhitespace(&groupUser);
for(i = 0; i < fNumUsers; i++) {
if(fProfiles[i]->username.Equal(groupUser))
{
UInt32 grpSize = fProfiles[i]->groupsSize;
if(fProfiles[i]->numGroups >= grpSize) {
char** oldGroups = fProfiles[i]->groups;
fProfiles[i]->groups = NEW char*[grpSize * 2];
for(j = 0; j < grpSize; j++) {
fProfiles[i]->groups[j] = oldGroups[j];
}
fProfiles[i]->groupsSize *= 2;
delete [] oldGroups;
}
fProfiles[i]->groups[fProfiles[i]->numGroups] = groupName.GetAsCString();
if(nameLen > fProfiles[i]->maxGroupNameLen)
fProfiles[i]->maxGroupNameLen = nameLen;
fProfiles[i]->numGroups++;
break;
}
}
}
}
}
}
return resultErr;
}
// No memory is allocated
Bool16 AccessChecker::HaveFilePathsChanged(const char* inUsersFilePath, const char* inGroupsFilePath)
{
Bool16 changed = true;
if((inUsersFilePath != NULL) && (inGroupsFilePath != NULL) && (fUsersFilePath != NULL) && (fGroupsFilePath != NULL)) {
if((strcmp(inUsersFilePath, fUsersFilePath) == 0) && (strcmp(inGroupsFilePath, fGroupsFilePath) == 0))
changed = false;
}
return changed;
}
// No memory is allocated
AccessChecker::UserProfile* AccessChecker::RetrieveUserProfile(const StrPtrLen* inUserName)
{
UInt32 index = 0;
for(index = 0; index < fNumUsers; index++) {
if(fProfiles[index]->username.Equal(*inUserName))
return fProfiles[index];
}
return NULL;
}

View file

@ -0,0 +1,115 @@
/*
*
* @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: AccessChecker.h
Contains:
*/
#ifndef _QTSSACCESSCHECKER_H_
#define _QTSSACCESSCHECKER_H_
#include "QTSS.h"
#include "StrPtrLen.h"
#include "OSHeaders.h"
class AccessChecker
{
/*
Access check logic:
If "modAccess_enabled" == "enabled,
Starting at URL dir, walk up directories to Movie Folder until a "qtaccess" file is found
If not found,
allow access
If found,
send a challenge to the client
verify user against QTSSPasswd
verify that user or member group is in the lowest ".qtacess"
walk up directories until a ".qtaccess" is found
If found,
allow access
If not found,
deny access
ToDo:
would probably be a good idea to do some caching of ".qtaccess" data to avoid
multiple directory walks
*/
public:
struct UserProfile
{
StrPtrLen username;
StrPtrLen cryptPassword;
StrPtrLen digestPassword;
char** groups;
UInt32 maxGroupNameLen;
UInt32 numGroups;
UInt32 groupsSize;
};
AccessChecker();
virtual ~AccessChecker();
void UpdateFilePaths(const char* inUsersFilePath, const char* inGroupsFilePath);
UInt32 UpdateUserProfiles();
Bool16 HaveFilePathsChanged(const char* inUsersFilePath, const char* inGroupsFilePath);
UserProfile* RetrieveUserProfile(const StrPtrLen* inUserName);
inline StrPtrLen* GetAuthRealm() {return &fAuthRealm;}
inline char* GetUsersFilePathPtr() {return fUsersFilePath;}
inline char* GetGroupsFilePathPtr() {return fGroupsFilePath;}
enum { kDefaultNumProfiles = 10, kDefaultNumGroups = 2 };
enum { kNoErr = 0x00000000,
kUsersFileNotFoundErr = 0x00000001,
kGroupsFileNotFoundErr = 0x00000002,
kBadUsersFileErr = 0x00000004,
kBadGroupsFileErr = 0x00000008,
kUsersFileUnknownErr = 0x00000010,
kGroupsFileUnknownErr = 0x00000020
};
protected:
char* fGroupsFilePath;
char* fUsersFilePath;
QTSS_TimeVal fUsersFileModDate;
QTSS_TimeVal fGroupsFileModDate;
StrPtrLen fAuthRealm;
UserProfile** fProfiles;
UInt32 fNumUsers;
UInt32 fCurrentSize;
static const char* kDefaultUsersFilePath;
static const char* kDefaultGroupsFilePath;
private:
void DeleteProfilesAndRealm();
};
#endif //_QTSSACCESSCHECKER_H_

View file

@ -0,0 +1,536 @@
/*
*
* @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: QTSSAccessModule.cpp
Contains: Implementation of QTSSAccessModule.
*/
#include "QTSSAccessModule.h"
#include "../defaultPaths.h"
#include "OSArrayObjectDeleter.h"
#include "QTSS_Private.h"
#include "StrPtrLen.h"
#include "OSMemory.h"
#include "MyAssert.h"
#include "StringFormatter.h"
#include "StrPtrLen.h"
#include "StringParser.h"
#include "base64.h"
#include "OS.h"
#include "AccessChecker.h"
#include "QTAccessFile.h"
#include "QTSSModuleUtils.h"
#ifndef __Win32__
#include <unistd.h>
#endif
#include <fcntl.h>
#include <errno.h>
// ATTRIBUTES
// STATIC DATA
#define MODPREFIX_ "modAccess_"
static StrPtrLen sSDPSuffix(".sdp");
static OSMutex* sUserMutex = NULL;
static Bool16 sDefaultAuthenticationEnabled = true;
static Bool16 sAuthenticationEnabled = true;
static char* sDefaultUsersFilePath = DEFAULTPATHS_ETC_DIR "qtusers";
static char* sUsersFilePath = NULL;
static char* sDefaultGroupsFilePath = DEFAULTPATHS_ETC_DIR "qtgroups";
static char* sGroupsFilePath = NULL;
static char* sDefaultAccessFileName = "qtaccess";
static QTSS_AttributeID sBadNameMessageAttrID = qtssIllegalAttrID;
static QTSS_AttributeID sUsersFileNotFoundMessageAttrID = qtssIllegalAttrID;
static QTSS_AttributeID sGroupsFileNotFoundMessageAttrID = qtssIllegalAttrID;
static QTSS_AttributeID sBadUsersFileMessageAttrID = qtssIllegalAttrID;
static QTSS_AttributeID sBadGroupsFileMessageAttrID = qtssIllegalAttrID;
static QTSS_StreamRef sErrorLogStream = NULL;
static QTSS_TextMessagesObject sMessages = NULL;
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_PrefsObject sServerPrefs = NULL;
static AccessChecker** sAccessCheckers;
static UInt32 sNumAccessCheckers = 0;
static UInt32 sAccessCheckerArraySize = 0;
static Bool16 sAllowGuestDefaultEnabled = true;
static Bool16 sDefaultGuestEnabled = true;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSAccessModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register();
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error RereadPrefs();
static QTSS_Error AuthenticateRTSPRequest(QTSS_RTSPAuth_Params* inParams);
static QTSS_Error AccessAuthorizeRTSPRequest(QTSS_StandardRTSP_Params* inParams);
static char* GetCheckedFileName();
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSAccessModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSAccessModuleDispatch);
}
QTSS_Error QTSSAccessModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register();
break;
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
break;
case QTSS_RereadPrefs_Role:
return RereadPrefs();
break;
case QTSS_RTSPAuthenticate_Role:
if (sAuthenticationEnabled)
return AuthenticateRTSPRequest(&inParams->rtspAthnParams);
break;
case QTSS_RTSPAuthorize_Role:
if (sAuthenticationEnabled)
return AccessAuthorizeRTSPRequest(&inParams->rtspRequestParams);
break;
case QTSS_Shutdown_Role:
return Shutdown();
break;
}
return QTSS_NoErr;
}
QTSS_Error Register()
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthenticate_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
// Add AuthenticateName and Password attributes
static char* sBadAccessFileName = "QTSSAccessModuleBadAccessFileName";
static char* sUsersFileNotFound = "QTSSAccessModuleUsersFileNotFound";
static char* sGroupsFileNotFound = "QTSSAccessModuleGroupsFileNotFound";
static char* sBadUsersFile = "QTSSAccessModuleBadUsersFile";
static char* sBadGroupsFile = "QTSSAccessModuleBadGroupsFile";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sBadAccessFileName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sBadAccessFileName, &sBadNameMessageAttrID);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sUsersFileNotFound, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sUsersFileNotFound, &sUsersFileNotFoundMessageAttrID);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sGroupsFileNotFound, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sGroupsFileNotFound, &sGroupsFileNotFoundMessageAttrID);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sBadUsersFile, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sBadUsersFile, &sBadUsersFileMessageAttrID);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sBadGroupsFile, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sBadGroupsFile, &sBadGroupsFileMessageAttrID);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Create an array of AccessCheckers
sAccessCheckers = NEW AccessChecker*[2];
sAccessCheckers[0] = NEW AccessChecker();
sNumAccessCheckers = 1;
sAccessCheckerArraySize = 2;
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sErrorLogStream = inParams->inErrorLogStream;
sMessages = inParams->inMessages;
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
sServerPrefs = inParams->inPrefs;
sUserMutex = NEW OSMutex();
RereadPrefs();
QTAccessFile::Initialize();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
//cleanup
// delete all the AccessCheckers
UInt32 index;
for(index = 0; index < sNumAccessCheckers; index++)
delete sAccessCheckers[index];
delete[] sAccessCheckers;
sNumAccessCheckers = 0;
// delete the main users and groups path
//if(sUsersFilePath != sDefaultUsersFilePath)
// sUsersFilePath is assigned by a call to QTSSModuleUtils::GetStringAttribute which always
// allocates memory even if it just returns the default value
delete[] sUsersFilePath;
sUsersFilePath = NULL;
//if(sGroupsFilePath != sDefaultGroupsFilePath)
// sGroupsFilePath is assigned by a call to QTSSModuleUtils::GetStringAttribute which always
// allocates memory even if it just returns the default value
delete[] sGroupsFilePath;
sGroupsFilePath = NULL;
return QTSS_NoErr;
}
char* GetCheckedFileName()
{
char *result = NULL;
static char *badChars = "/'\"";
char theBadCharMessage[] = "' '";
char *theBadChar = NULL;
result = QTSSModuleUtils::GetStringAttribute(sPrefs, MODPREFIX_"qtaccessfilename", sDefaultAccessFileName);
StrPtrLen searchStr(result);
theBadChar = strpbrk(searchStr.Ptr, badChars);
if ( theBadChar!= NULL)
{
theBadCharMessage[1] = theBadChar[0];
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadNameMessageAttrID, 0, theBadCharMessage, result);
delete[] result;
result = NEW char[::strlen(sDefaultAccessFileName) + 2];
::strcpy(result, sDefaultAccessFileName);
}
return result;
}
QTSS_Error RereadPrefs()
{
OSMutexLocker locker(sUserMutex);
//
// Use the standard GetAttribute routine to retrieve the correct values for our preferences
QTSSModuleUtils::GetAttribute(sPrefs, MODPREFIX_"enabled", qtssAttrDataTypeBool16,
&sAuthenticationEnabled, &sDefaultAuthenticationEnabled, sizeof(sAuthenticationEnabled));
//if(sUsersFilePath != sDefaultUsersFilePath)
// sUsersFilePath is assigned by a call to QTSSModuleUtils::GetStringAttribute which always
// allocates memory even if it just returns the default value
// delete this old memory before reassigning it to new memory
delete[] sUsersFilePath;
sUsersFilePath = NULL;
//if(sGroupsFilePath != sDefaultGroupsFilePath)
// sGroupsFilePath is assigned by a call to QTSSModuleUtils::GetStringAttribute which always
// allocates memory even if it just returns the default value
// delete this old memory before reassigning it to new memory
delete[] sGroupsFilePath;
sGroupsFilePath = NULL;
sUsersFilePath = QTSSModuleUtils::GetStringAttribute(sPrefs, MODPREFIX_"usersfilepath", sDefaultUsersFilePath);
sGroupsFilePath = QTSSModuleUtils::GetStringAttribute(sPrefs, MODPREFIX_"groupsfilepath", sDefaultGroupsFilePath);
// GetCheckedFileName always allocates memory
char* accessFile = GetCheckedFileName();
// QTAccessFile::SetAccessFileName makes its own copy,
// so delete the previous allocated memory after this call
QTAccessFile::SetAccessFileName(accessFile);
delete [] accessFile;
if(sAccessCheckers[0]->HaveFilePathsChanged(sUsersFilePath, sGroupsFilePath))
{
sAccessCheckers[0]->UpdateFilePaths(sUsersFilePath, sGroupsFilePath);
UInt32 err;
err = sAccessCheckers[0]->UpdateUserProfiles();
if(err & AccessChecker::kUsersFileNotFoundErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sUsersFileNotFoundMessageAttrID, 0, sUsersFilePath, NULL);
else if(err & AccessChecker::kBadUsersFileErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadUsersFileMessageAttrID, 0, sUsersFilePath, NULL);
if(err & AccessChecker::kGroupsFileNotFoundErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sGroupsFileNotFoundMessageAttrID, 0, sGroupsFilePath, NULL);
else if(err & AccessChecker::kBadGroupsFileErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadGroupsFileMessageAttrID, 0, sGroupsFilePath, NULL);
}
QTSSModuleUtils::GetAttribute(sServerPrefs,"enable_allow_guest_default", qtssAttrDataTypeBool16,
&sAllowGuestDefaultEnabled,(void *)&sDefaultGuestEnabled, sizeof(sAllowGuestDefaultEnabled));
return QTSS_NoErr;
}
QTSS_Error AuthenticateRTSPRequest(QTSS_RTSPAuth_Params* inParams)
{
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
UInt32 fileErr;
OSMutexLocker locker(sUserMutex);
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) )
return QTSS_RequestFailed;
// Get the user profile object from the request object
QTSS_UserProfileObject theUserProfile = NULL;
UInt32 len = sizeof(QTSS_UserProfileObject);
QTSS_Error theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqUserProfile, 0, (void*)&theUserProfile, &len);
Assert(len == sizeof(QTSS_UserProfileObject));
if (theErr != QTSS_NoErr)
return theErr;
Bool16 defaultPaths = true;
// Check for a users and groups file in the access file
// For this, first get local file path and root movie directory
//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;
// Now get the access file path
char* accessFilePath = QTAccessFile::GetAccessFile_Copy(movieRootDirStr, pathBuffStr);
OSCharArrayDeleter accessFilePathDeleter(accessFilePath);
// Parse the access file for the AuthUserFile and AuthGroupFile keywords
char* usersFilePath = NULL;
char* groupsFilePath = NULL;
// Get the request action from the request object
QTSS_ActionFlags action = qtssActionFlagsNoFlags;
len = sizeof(action);
theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAction, 0, (void*)&action, &len);
Assert(len == sizeof(action));
if (theErr != QTSS_NoErr)
return theErr;
// Allocates memory for usersFilePath and groupsFilePath
QTSS_AuthScheme authScheme = QTAccessFile::FindUsersAndGroupsFilesAndAuthScheme(accessFilePath, action, &usersFilePath, &groupsFilePath);
if((usersFilePath != NULL) || (groupsFilePath != NULL))
defaultPaths = false;
if(usersFilePath == NULL)
usersFilePath = strdup(sUsersFilePath);
if(groupsFilePath == NULL)
groupsFilePath = strdup(sGroupsFilePath);
OSCharArrayDeleter userPathDeleter(usersFilePath);
OSCharArrayDeleter groupPathDeleter(groupsFilePath);
AccessChecker* currentChecker = NULL;
UInt32 index;
// If the default users and groups file are not the ones we need
if(!defaultPaths)
{
// check if there is one AccessChecker that matches the needed paths
// Don't have to check for the first one (or element zero) because it has the default paths
for(index = 1; index < sNumAccessCheckers; index++)
{
// If an access checker that matches the users and groups file paths is found
if(!sAccessCheckers[index]->HaveFilePathsChanged(usersFilePath, groupsFilePath))
{
currentChecker = sAccessCheckers[index];
break;
}
}
// If an existing AccessChecker for the needed paths isn't found
if(currentChecker == NULL)
{
// Grow the AccessChecker array if needed
if(sNumAccessCheckers == sAccessCheckerArraySize)
{
AccessChecker** oldAccessCheckers = sAccessCheckers;
sAccessCheckers = NEW AccessChecker*[sAccessCheckerArraySize * 2];
for(index = 0; index < sNumAccessCheckers; index++)
{
sAccessCheckers[index] = oldAccessCheckers[index];
}
sAccessCheckerArraySize *= 2;
delete [] oldAccessCheckers;
}
// And create a new AccessChecker for the paths
sAccessCheckers[sNumAccessCheckers] = NEW AccessChecker();
sAccessCheckers[sNumAccessCheckers]->UpdateFilePaths(usersFilePath, groupsFilePath);
fileErr = sAccessCheckers[sNumAccessCheckers]->UpdateUserProfiles();
if(fileErr & AccessChecker::kUsersFileNotFoundErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sUsersFileNotFoundMessageAttrID, 0, usersFilePath, NULL);
else if(fileErr & AccessChecker::kBadUsersFileErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadUsersFileMessageAttrID, 0, usersFilePath, NULL);
if(fileErr & AccessChecker::kGroupsFileNotFoundErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sGroupsFileNotFoundMessageAttrID, 0, groupsFilePath, NULL);
else if(fileErr & AccessChecker::kBadGroupsFileErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadGroupsFileMessageAttrID, 0, groupsFilePath, NULL);
currentChecker = sAccessCheckers[sNumAccessCheckers];
sNumAccessCheckers++;
}
}
else
{
currentChecker = sAccessCheckers[0];
}
// Before retrieving the user profile information
// check if the groups/users files have been modified and update them otherwise
fileErr = currentChecker->UpdateUserProfiles();
/*
// This is for logging the errors if users file and/or the groups file is not found or corrupted
char* usersFile = currentChecker->GetUsersFilePathPtr();
char* groupsFile = currentChecker->GetGroupsFilePathPtr();
if(fileErr & AccessChecker::kUsersFileNotFoundErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sUsersFileNotFoundMessageAttrID, 0, usersFile, NULL);
else if(fileErr & AccessChecker::kBadUsersFileErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadUsersFileMessageAttrID, 0, usersFile, NULL);
if(fileErr & AccessChecker::kGroupsFileNotFoundErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sGroupsFileNotFoundMessageAttrID, 0, groupsFile, NULL);
else if(fileErr & AccessChecker::kBadGroupsFileErr)
QTSSModuleUtils::LogError(qtssWarningVerbosity,sBadGroupsFileMessageAttrID, 0, groupsFile, NULL);
*/
// Retrieve the password data and group information for the user and set them
// in the qtssRTSPReqUserProfile attr
// The password data is crypt of the real password for Basic authentication
// and it is MD5(username:realm:password) for Digest authentication
// It the access file didn't contain an auth scheme, then get the auth scheme out of the request object
// else, set the qtssRTSPReqAuthScheme to that found in the access file
if (authScheme == qtssAuthNone)
{
// Get the authentication scheme from the request object
len = sizeof(authScheme);
theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&authScheme, &len);
Assert(len == sizeof(authScheme));
if (theErr != QTSS_NoErr)
return theErr;
}
else
{
theErr = QTSS_SetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&authScheme, sizeof(authScheme));
if (theErr != QTSS_NoErr)
return theErr;
}
// Set the qtssUserRealm to the realm value retrieved from the users file
// This should be used for digest auth scheme, and if no realm is found in the qtaccess file, then
// it should be used for basic auth scheme.
// No memory is allocated; just a pointer is returned
StrPtrLen* authRealm = currentChecker->GetAuthRealm();
(void)QTSS_SetValue(theUserProfile, qtssUserRealm, 0, (void*)(authRealm->Ptr), (authRealm->Len));
// Get the username from the user profile object
char* usernameBuf = NULL;
theErr = QTSS_GetValueAsString(theUserProfile, qtssUserName, 0, &usernameBuf);
OSCharArrayDeleter usernameBufDeleter(usernameBuf);
StrPtrLen username(usernameBuf);
if (theErr != QTSS_NoErr)
return theErr;
// No memory is allocated; just a pointer to the profile is returned
AccessChecker::UserProfile* profile = currentChecker->RetrieveUserProfile(&username);
if(profile == NULL)
return QTSS_NoErr;
// Set the qtssUserPassword attribute to either the crypted password or the digest password
// based on the authentication scheme
if (authScheme == qtssAuthBasic)
(void)QTSS_SetValue(theUserProfile, qtssUserPassword, 0, (void*)((profile->cryptPassword).Ptr), (profile->cryptPassword).Len);
else if (authScheme == qtssAuthDigest)
(void)QTSS_SetValue(theUserProfile, qtssUserPassword, 0, (void*)((profile->digestPassword).Ptr), (profile->digestPassword).Len);
// Set the multivalued qtssUserGroups attr to the groups the user belongs to, if any
UInt32 maxLen = profile->maxGroupNameLen;
for(index = 0; index < profile->numGroups; index++)
{
UInt32 curLen = ::strlen(profile->groups[index]);
if(curLen < maxLen)
{
char* groupWithPaddedZeros = NEW char[maxLen]; // memory allocated
::memcpy(groupWithPaddedZeros, profile->groups[index], curLen);
::memset(groupWithPaddedZeros+curLen, '\0', maxLen-curLen);
(void)QTSS_SetValue(theUserProfile, qtssUserGroups, index, (void*)groupWithPaddedZeros, maxLen);
delete [] groupWithPaddedZeros; // memory deleted
}
else
{
(void)QTSS_SetValue(theUserProfile, qtssUserGroups, index, (void*)(profile->groups[index]), maxLen);
}
}
return QTSS_NoErr;
}
QTSS_Error AccessAuthorizeRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
Bool16 allowNoAccessFiles = sAllowGuestDefaultEnabled; //no access files allowed means allowing guest access (unknown users)
QTSS_ActionFlags noAction = ~qtssActionFlagsRead; // allow any action
QTSS_ActionFlags authorizeAction = QTSSModuleUtils::GetRequestActions(inParams->inRTSPRequest);
Bool16 authorized =false;
Bool16 allowAnyUser = false;
QTAccessFile accessFile;
return accessFile.AuthorizeRequest(inParams,allowNoAccessFiles, noAction, authorizeAction, &authorized, &allowAnyUser);
}

View file

@ -0,0 +1,46 @@
/*
*
* @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: QTSSAccessModule.h
Contains: Module that handles authentication and authorization independent of the file system
*/
#ifndef _QTSSACCESSMODULE_H_
#define _QTSSACCESSMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSAccessModule_Main(void* inPrivateArgs);
}
#endif //_QTSSACCESSMODULE_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,311 @@
/*
*
* @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: AdminElements.h
Contains: implements various Admin Elements class
*/
#ifndef _ADMINELEMENTNODE_H_
#define _ADMINELEMENTNODE_H_
#ifndef __Win32__
#include <unistd.h> /* for getopt() et al */
#endif
#include <time.h>
#include <stdio.h> /* for //qtss_printf */
#include <stdlib.h> /* for getloadavg & other useful stuff */
#include "QTSSAdminModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "OSHashTable.h"
#include "OSMutex.h"
#include "StrPtrLen.h"
#include "OSRef.h"
#include "AdminQuery.h"
void PRINT_STR(StrPtrLen *spl);
void COPYBUFFER(char *dest,char *src,SInt8 size);
void ElementNode_InitPtrArray();
void ElementNode_InsertPtr(void *ptr, char * src);
void ElementNode_RemovePtr(void *ptr, char * src);
SInt32 ElementNode_CountPtrs();
void ElementNode_ShowPtrs();
class ClientSession {
public:
ClientSession(void) : fRTSPSessionID(0), fBitrate(0), fPacketLossPercent(0), fBytesSent(0),fTimeConnected(0) {};
~ClientSession() { } ;
UInt32 fRTSPSessionID;
char fIPAddressStr[32];
char fURLBuffer[512];
UInt32 fBitrate;
Float32 fPacketLossPercent;
UInt64 fBytesSent;
UInt64 fTimeConnected;
};
class ElementNode
{
public:
enum { eMaxAccessSize = 32, eMaxAttributeNameSize = 63, eMaxAPITypeSize = 63, eMaxAttrIDSize = sizeof(UInt32) };
enum { eData = 0, eArrayNode, eNode};
#define kEmptyRef (OSRef *)NULL
#define kEmptyData (char *)NULL
enum { kFirstIndexItem = 0 };
typedef enum
{ eStatic = 0,
eDynamic = 1,
} DataFieldsType;
struct ElementDataFields
{
UInt32 fKey;
UInt32 fAPI_ID;
UInt32 fIndex;
char fFieldName[eMaxAttributeNameSize + 1];
UInt32 fFieldLen;
QTSS_AttrPermission fAccessPermissions;
char fAccessData[eMaxAccessSize + 1];
UInt32 fAccessLen;
UInt32 fAPI_Type;
UInt32 fFieldType;
QTSS_Object fAPISource;
};
SInt32 fDataFieldsStop;
UInt32 CountElements();
SInt32 GetMyStopItem() { Assert (fSelfPtr); return fDataFieldsStop; };
UInt32 GetMyKey() { Assert (fSelfPtr); return fSelfPtr->fKey; };
char* GetMyName() { Assert (fSelfPtr); return fSelfPtr->fFieldName; };
UInt32 GetMyNameLen() { Assert (fSelfPtr); return fSelfPtr->fFieldLen; };
UInt32 GetMyAPI_ID() { Assert (fSelfPtr); return fSelfPtr->fAPI_ID; };
UInt32 GetMyIndex() { Assert (fSelfPtr); return fSelfPtr->fIndex; };
UInt32 GetMyAPI_Type() { Assert (fSelfPtr); return fSelfPtr->fAPI_Type; };
char* GetMyAPI_TypeStr() { Assert (fSelfPtr); char* theTypeString = NULL; (void)QTSS_TypeToTypeString(GetMyAPI_Type(), &theTypeString); return theTypeString; };
UInt32 GetMyFieldType() { Assert (fSelfPtr); return fSelfPtr->fFieldType; };
char* GetMyAccessData() { Assert (fSelfPtr); return fSelfPtr->fAccessData; };
UInt32 GetMyAccessLen() { Assert (fSelfPtr); return fSelfPtr->fAccessLen; };
UInt32 GetMyAccessPermissions() { Assert (fSelfPtr); return fSelfPtr->fAccessPermissions; };
void GetMyNameSPL(StrPtrLen* str) { Assert(str); if (str != NULL) str->Set(fSelfPtr->fFieldName,fSelfPtr->fFieldLen); };
void GetMyAccess(StrPtrLen* str) { Assert(str); if (str != NULL) str->Set(fSelfPtr->fAccessData,fSelfPtr->fAccessLen);};
QTSS_Object GetMySource() {
Assert(fSelfPtr != NULL);
//qtss_printf("GetMySource fSelfPtr->fAPISource = %"_U32BITARG_" \n", fSelfPtr->fAPISource);
return fSelfPtr->fAPISource;
};
Bool16 IsNodeElement() { Assert(this); return (this->GetMyFieldType() == eNode || this->GetMyFieldType() == eArrayNode); }
Bool16 IsStopItem(SInt32 index) { return index == GetMyStopItem(); };
UInt32 GetKey(SInt32 index) { return fFieldIDs[index].fKey; };
char* GetName(SInt32 index) { return fFieldIDs[index].fFieldName; };
UInt32 GetNameLen(SInt32 index) { return fFieldIDs[index].fFieldLen; };
UInt32 GetAPI_ID(SInt32 index) { return fFieldIDs[index].fAPI_ID; };
UInt32 GetAttributeIndex(SInt32 index) { return fFieldIDs[index].fIndex; };
UInt32 GetAPI_Type(SInt32 index) { return fFieldIDs[index].fAPI_Type; };
char* GetAPI_TypeStr(SInt32 index) { char* theTypeStr = NULL; (void)QTSS_TypeToTypeString(GetAPI_Type(index), &theTypeStr); return theTypeStr; };
UInt32 GetFieldType(SInt32 index) { return fFieldIDs[index].fFieldType; };
char* GetAccessData(SInt32 index) { return fFieldIDs[index].fAccessData; };
UInt32 GetAccessLen(SInt32 index) { return fFieldIDs[index].fAccessLen; };
UInt32 GetAccessPermissions(SInt32 index) { return fFieldIDs[index].fAccessPermissions; };
void GetNameSPL(SInt32 index,StrPtrLen* str) { if (str != NULL) str->Set(fFieldIDs[index].fFieldName,fFieldIDs[index].fFieldLen); };
void GetAccess(SInt32 index,StrPtrLen* str) { if (str != NULL) str->Set(fFieldIDs[index].fAccessData,fFieldIDs[index].fAccessLen); };
QTSS_Object GetAPISource(SInt32 index) { return fFieldIDs[index].fAPISource; };
Bool16 IsNodeElement(SInt32 index) { return (GetFieldType(index) == eNode || GetFieldType(index) == eArrayNode); }
enum
{ eAPI_ID = 0,
eAPI_Name = 1,
eAccess = 2,
ePath = 3,
eType = 4,
eNumAttributes = 5
};
ElementNode();
void Initialize(SInt32 index, ElementNode *parentPtr, QueryURI *queryPtr, StrPtrLen *currentSegmentPtr,QTSS_Initialize_Params *initParams,QTSS_Object nodeSource, DataFieldsType dataFieldsType);
virtual ~ElementNode();
void SetNodeName(char *namePtr);
char * GetNodeName() { return fNodeNameSPL.Ptr;};
UInt32 GetNodeNameLen() { return fNodeNameSPL.Len;};
StrPtrLen* GetNodeNameSPL() { return &fNodeNameSPL;};
void SetParentNode(ElementNode *parentPtr) { fParentNodePtr = parentPtr;};
ElementNode* GetParentNode() { return fParentNodePtr;};
void GetFullPath(StrPtrLen *resultPtr);
OSRef* GetOSRef(SInt32 index);
void SetOSRef(SInt32 index, OSRef* refPtr);
SInt32 ResolveSPLKeyToIndex(StrPtrLen *keyPtr);
virtual Bool16 SetUpOneDataField(UInt32 index);
ElementDataFields *GetElementFieldPtr(SInt32 index);
char *GetElementDataPtr(SInt32 index);
void SetElementDataPtr(SInt32 index, char * data, Bool16 isNode);
void SetMyElementDataPtr(char * data) { fSelfDataPtr = data; }
char* GetMyElementDataPtr() { return fSelfDataPtr; }
Bool16 IsFiltered(SInt32 index,QueryURI *queryPtr);
ElementDataFields *GetNodeInfoPtr(SInt32 index);
void SetNodeInfo(ElementDataFields *nodeInfo);
void SetSource(void * dataSource) { fDataSource = dataSource;};
void * GetSource() {
QTSS_Object source = GetMySource();
if (source != NULL)
return source;
else
{ //qtss_printf("GetSource return fDataSource = %"_U32BITARG_" \n",fDataSource);
return fDataSource;
}
};
virtual void SetUpSingleNode(QueryURI *queryPtr, StrPtrLen *currentSegmentPtr, StrPtrLen *nextSegmentPtr, SInt32 index, QTSS_Initialize_Params *initParams);
virtual void SetUpAllNodes(QueryURI *queryPtr, StrPtrLen *currentSegmentPtr,StrPtrLen *nextSegmentPtr, QTSS_Initialize_Params *initParams);
virtual void SetUpSingleElement(QueryURI *queryPtr, StrPtrLen *currentSegmentPtr, StrPtrLen *nextSegmentPtr, SInt32 index, QTSS_Initialize_Params *initParams);
virtual void SetUpAllElements(QueryURI *queryPtr, StrPtrLen *currentSegmentPtr, StrPtrLen *nextSegmentPtr, QTSS_Initialize_Params *initParams);
virtual void SetupNodes(QueryURI *queryPtr,StrPtrLen *currentPathPtr,QTSS_Initialize_Params *initParams);
void RespondWithSelfAdd(QTSS_StreamRef inStream, QueryURI *queryPtr);
void RespondToAdd(QTSS_StreamRef inStream, SInt32 index,QueryURI *queryPtr);
void RespondToSet(QTSS_StreamRef inStream, SInt32 index,QueryURI *queryPtr);
void RespondToGet(QTSS_StreamRef inStream, SInt32 index,QueryURI *queryPtr);
void RespondToDel(QTSS_StreamRef inStream, SInt32 index,QueryURI *queryPtr,Bool16 delAttribute);
void RespondToKey(QTSS_StreamRef inStream, SInt32 index,QueryURI *queryPtr);
void RespondWithNodeName(QTSS_StreamRef inStream, QueryURI *queryPtr);
void RespondWithSelf(QTSS_StreamRef inStream, QueryURI *queryPtr);
void RespondWithSingleElement(QTSS_StreamRef inStream,QueryURI *queryPtr, StrPtrLen *currentSegmentPtr);
void RespondWithAllElements(QTSS_StreamRef inStream,QueryURI *queryPtr, StrPtrLen *currentSegmentPtr);
void RespondWithAllNodes(QTSS_StreamRef inStream,QueryURI *queryPtr, StrPtrLen *currentSegmentPtr);
void RespondToQuery(QTSS_StreamRef inStream, QueryURI *queryPtr,StrPtrLen *currentPathPtr);
UInt32 CountAttributes(QTSS_Object source);
UInt32 CountValues(QTSS_Object source, UInt32 apiID);
QTSS_Error AllocateFields(UInt32 numFields);
void InitializeAllFields(Bool16 allocateFields, QTSS_Object defaultAttributeInfo, QTSS_Object source, QueryURI *queryPtr, StrPtrLen *currentSegmentPtr,Bool16 forceAll);
void InitializeSingleField(StrPtrLen *currentSegmentPtr);
void SetFields(UInt32 i, QTSS_Object attrInfoObject);
ElementNode* CreateArrayAttributeNode(UInt32 index, QTSS_Object source, QTSS_Object attributeInfo, UInt32 arraySize);
QTSS_Error GetAttributeSize (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex, UInt32* outLenPtr);
char* NewIndexElement (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex);
UInt32 GetNumFields() { return fNumFields; };
void SetNumFields(UInt32 numFields) { fNumFields = numFields; fDataFieldsStop = numFields; };
ElementDataFields* GetFields() {return fFieldIDs;};
void SetFields(ElementDataFields *fieldsPtr) {fFieldIDs = fieldsPtr;};
void SetFieldsType(DataFieldsType fDataFieldsType){fDataFieldsType = fDataFieldsType;};
static void GetFilteredAttributeName(ElementDataFields* fieldPtr, QTSS_AttributeID theID);
static Bool16 GetFilteredAttributeID(char *parentName, char *nodeName, QTSS_AttributeID* foundID);
static Bool16 IsPreferenceContainer(char *nodeName, QTSS_AttributeID* foundID);
enum { kmaxPathlen = 1048 };
char fPathBuffer[kmaxPathlen];
StrPtrLen fPathSPL;
StrPtrLen fNodeNameSPL;
QTSS_Object fDataSource;
SInt32 fNumFields;
SInt32 fPathLen;
Bool16 fInitialized;
ElementDataFields* fFieldIDs;
ElementDataFields* fSelfPtr;
DataFieldsType fDataFieldsType;
char* fSelfDataPtr;
char** fFieldDataPtrs;
OSRef** fFieldOSRefPtrs;
ElementNode* fParentNodePtr;
OSRefTable* fElementMap;
Bool16 fIsTop;
private:
inline void DebugShowFieldDataType(SInt32 index);
inline void DebugShowFieldValue(SInt32 index);
};
class AdminClass : public ElementNode
{
public:
QueryURI *fQueryPtr;
ElementNode *fNodePtr;
void SetUpSingleElement(QueryURI *queryPtr, StrPtrLen *currentSegmentPtr,StrPtrLen *nextSegmentPtr, SInt32 index, QTSS_Initialize_Params *initParams);
void SetUpSingleNode(QueryURI *queryPtr, StrPtrLen *currentSegmentPtr, StrPtrLen *nextSegmentPtr, SInt32 index, QTSS_Initialize_Params *initParams);
void Initialize(QTSS_Initialize_Params *initParams, QueryURI *queryPtr);
AdminClass():fQueryPtr(NULL), fNodePtr(NULL) {};
~AdminClass();
static ElementNode::ElementDataFields sAdminSelf[];
static ElementNode::ElementDataFields sAdminFieldIDs[];
enum
{ eServer = 0,
eNumAttributes
};
};
#endif // _ADMINELEMENTNODE_H_

View file

@ -0,0 +1,849 @@
/*
*
* @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: AdminQuery.cpp
Contains: Implements Admin Query
*/
#ifndef __Win32__
#include <unistd.h> /* for getopt() et al */
#endif
#include <time.h>
#include <stdio.h> /* for //qtss_printf */
#include <stdlib.h> /* for getloadavg & other useful stuff */
#include "QTSSAdminModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "OSHashTable.h"
#include "OSMutex.h"
#include "StrPtrLen.h"
#include "OSRef.h"
#include "AdminElementNode.h"
#include "AdminQuery.h"
#include "OSMemory.h"
#include "StringTranslator.h"
/*
r = recurse -> walk downward in hierarchy
v = verbose -> return full path in name
a = access -> return read/write access
t = type -> return type of value ** perhaps better not to support
f = filter -> return filter
p = path -> return path
i = indexed -> return indexed representation of attributes
d = debug -> return debug info on errors
*/
StrPtrLen * QueryURI::NextSegment(StrPtrLen *currentPathPtr, StrPtrLen *outNextPtr)
{
StrPtrLen *result = NULL;
StrPtrLen *theURLPtr = GetURL();
if (outNextPtr)
outNextPtr->Set(NULL,0);
if (currentPathPtr && outNextPtr && theURLPtr && currentPathPtr->Len > 0)
{
if ( (currentPathPtr->Ptr >= theURLPtr->Ptr)
&& (currentPathPtr->Ptr < &theURLPtr->Ptr[theURLPtr->Len] )
)
{
//qtss_printf("theURLPtr="); PRINT_STR(theURLPtr);
//qtss_printf("QueryURI::NextSegment currentPathPtr="); PRINT_STR(currentPathPtr);
UInt32 len = (PointerSizedInt)&(theURLPtr->Ptr[theURLPtr->Len -1]) - ((PointerSizedInt) currentPathPtr->Ptr + (currentPathPtr->Len -1));
char *startPtr = (char *) ( (PointerSizedInt) currentPathPtr->Ptr + currentPathPtr->Len) ;
StrPtrLen tempPath(startPtr,len);
StringParser URLparser(&tempPath);
URLparser.ConsumeLength(NULL, 1);
URLparser.ConsumeUntil(outNextPtr,(UInt8*) sNotQueryData);
result = outNextPtr;
//qtss_printf("QueryURI::NextSegment nextPathPtr=");PRINT_STR(outNextPtr);
}
}
return result;
};
QueryURI::URIField QueryURI::sURIFields[] =
{ /* fAttrName, len, id, fDataptr*/
{ "modules", strlen("modules"), eModuleID, NULL },
{ "admin", strlen("admin"), eRootID, NULL },
{ "URL", strlen("URL"), eURL, NULL },
{ "QUERY", strlen("QUERY"), eQuery, NULL },
{ "parameters", strlen("parameters"), eParameters,NULL },
{ "snapshot", strlen("snapshot"), eSnapshot, NULL },
{ "command", strlen("command"), eCommand, NULL },
{ "value", strlen("value"), eValue, NULL },
{ "type", strlen("type"), eType, NULL },
{ "access", strlen("access"), eAccess, NULL },
{ "name", strlen("name"), eName, NULL },
{ "filter1", strlen("filter1"), eFilter1, NULL },
{ "filter2", strlen("filter2"), eFilter2, NULL },
{ "filter3", strlen("filter3"), eFilter3, NULL },
{ "filter4", strlen("filter4"), eFilter4, NULL },
{ "filter5", strlen("filter5"), eFilter5, NULL },
{ "filter6", strlen("filter6"), eFilter6, NULL },
{ "filter7", strlen("filter7"), eFilter7, NULL },
{ "filter8", strlen("filter8"), eFilter8, NULL },
{ "filter9", strlen("filter9"), eFilter9, NULL },
{ "filter10", strlen("filter10"), eFilter10, NULL },
{ "", 0, -1, NULL }
};
char *QueryURI::sCommandDefs[] =
{
"GET",
"SET",
"ADD",
"DEL"
};
UInt8 QueryURI::sNotQueryData[] = // query stops
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //0-9
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //10-19
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //20-29
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, //30-39
1, 1, 0, 1, 1, 0, 0, 1, 0, 0, //40-49 allow * . and - and all numbers
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //50-59 allow :
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, //60-69 //stop on every character except a letter
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, 1, 1, 1, 1, 0, 1, 0, 0, 0, //90-99 _ is a word
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, 1, 1, 1, 1, 1, 1, 1, //120-129
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //130-139
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //140-149
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //150-159
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //160-169
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //170-179
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //180-189
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //190-199
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //200-209
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //210-219
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //220-229
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //230-239
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //240-249
1, 1, 1, 1, 1, 0 //250-255
};
UInt8 QueryURI::sWhiteQuoteOrEOL[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 // \t is a stop
1, 0, 0, 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, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, //60-69
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
};
UInt8 QueryURI::sWhitespaceOrQuoteMask[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 // \t is a stop
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10-19
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
0, 0, 1, 0, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, //60-69
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
};
QueryURI::QueryURI (StrPtrLen *inStream)
{
fIsAdminQuery = false;
fUseSnapShot = false;
fURIFieldsPtr = &sURIFields[0];
fTheCommand = -1;
for (short testField = 0; testField < eNumAttributes; testField++)
fURIFieldsPtr[testField].fData = NULL;
memset(fURIBuffer,0,QueryURI::eMaxBufferSize);
memset(fQueryBuffer,0,QueryURI::eMaxBufferSize);
fParamBits = 0;
fSnapshotID = 0;
fAccessFlags = 0;
fQueryHasResponse = false;
fLastPath[0] = 0;
fIsPref = false;
fNumFilters = 0;
fHasQuery = false;
URLParse(inStream);
SetQueryData();
}
void QueryURI::SetSnapShot()
{
StrPtrLen *snapshotSPL = GetSnapshot();
if (snapshotSPL != NULL)
{
StringParser parser(snapshotSPL);
fSnapshotID = parser.ConsumeInteger(NULL);
fUseSnapShot = true;
}
};
void QueryURI::SetCommand()
{
StrPtrLen commandDef;
StrPtrLen *queryCommandPtr;
Bool16 foundCommand = false;
SInt16 commandIndex;
queryCommandPtr = this->GetCommand();
if (queryCommandPtr == NULL || queryCommandPtr->Len == 0) // Default command
{
fTheCommand = kGETCommand;
return;
}
for (commandIndex = 0; commandIndex < kLastCommand; commandIndex ++)
{
foundCommand = queryCommandPtr->EqualIgnoreCase(sCommandDefs[commandIndex],strlen(sCommandDefs[commandIndex]));
if (foundCommand)
{ fTheCommand = commandIndex;
}
}
};
void QueryURI::SetAccessFlags()
{
StrPtrLen tempStr;
SInt16 theChar;
StringParser parser(GetAccess());
fAccessFlags = 0;
while (parser.GetDataRemaining() != 0)
{
parser.ConsumeLength(&tempStr, 1);
if(tempStr.Len > 0)
{
theChar = *tempStr.Ptr;
switch(theChar)
{
case 'r': fAccessFlags |= qtssAttrModeRead;
break;
case 'w': fAccessFlags |= qtssAttrModeWrite;
break;
case 'p': fAccessFlags |= qtssAttrModePreempSafe;
break;
//case 'd': fAccessFlags |= qtssAttrModeRemoveable;
//break;
}
}
tempStr.Len = 0;
}
}
void QueryURI::SetParamBits(UInt32 forcebits)
{
StrPtrLen tempStr;
SInt16 theChar;
StringParser parser(GetParameters());
fParamBits = forcebits;
while (parser.GetDataRemaining() != 0)
{
parser.ConsumeLength(&tempStr, 1);
if(tempStr.Len > 0)
{
theChar = *tempStr.Ptr;
switch(theChar)
{
case 'r': fParamBits |= kRecurseParam;
break;
case 'v': fParamBits |= kVerboseParam;
break;
case 'a': fParamBits |= kAccessParam;
break;
case 't': fParamBits |= kTypeParam;
break;
case 'f': fParamBits |= kFilterParam;
break;
case 'p': fParamBits |= kPathParam;
break;
case 'd': fParamBits |= kDebugParam;
break;
case 'i': fParamBits |= kIndexParam;
break;
}
}
tempStr.Len = 0;
}
}
QueryURI::~QueryURI()
{
/*
for (int count = 0; fURIFieldsPtr[count].fID != -1 ; count++)
{ //qtss_printf("QueryURI::~QueryURI delete %s=",fURIFieldsPtr[count].fFieldName); PRINT_STR(fURIFieldsPtr[count].fData);
if (fURIFieldsPtr[count].fData && fURIFieldsPtr[count].fData->Ptr)
delete fURIFieldsPtr[count].fData->Ptr;
fURIFieldsPtr[count].fData = NULL;
}
*/
}
UInt32 QueryURI::CheckInvalidIterator(char* evalMessageBuff)
{
UInt32 result = 0;
StringParser parser(GetURL());
parser.ConsumeUntil(NULL,'*');
if (parser.PeekFast() == '*')
{ result = 405;
static char *message = "* iterator not valid";
qtss_sprintf(evalMessageBuff, "%s",message);
}
return result;
}
UInt32 QueryURI::CheckInvalidArrayIterator(char* evalMessageBuff)
{
UInt32 result = 0;
StringParser parser(GetURL());
parser.ConsumeUntil(NULL,':');
if (parser.PeekFast() == ':')
{ result = 405;
static char *message = ": array iterator not valid";
qtss_sprintf(evalMessageBuff, "%s",message);
}
return result;
}
UInt32 QueryURI::CheckInvalidRecurseParam(char* evalMessageBuff)
{
UInt32 result = 0;
if (RecurseParam())
{ result = 405;
static char *message = "(r)ecurse parameter not valid";
qtss_sprintf(evalMessageBuff, "%s",message);
}
return result;
}
UInt32 QueryURI::EvalQuery(UInt32 *forceResultPtr, char *forceMessagePtr)
{
UInt32 result = 0;
const SInt16 messageLen = 512;
char evalMessageBuff[messageLen] = {0};
StrPtrLen evalMessage;
evalMessage.Set(evalMessageBuff, messageLen);
if (forceResultPtr != NULL)
{
result = *forceResultPtr;
switch(*forceResultPtr)
{
case 404:
qtss_sprintf(fQueryMessageBuff,"reason=\"No data found\"" );
fQueryEvalMessage.Set(fQueryMessageBuff, strlen(fQueryMessageBuff));
break;
default:
{ SInt32 theID = GetCommandID();
StrPtrLen *commandPtr = GetCommand();
if (theID < 0)
{ if (commandPtr)
{ qtss_sprintf(fQueryMessageBuff,"reason=\"%s for command %s\"",forceMessagePtr,commandPtr->Ptr);
}
}
else
{ qtss_sprintf(fQueryMessageBuff,"reason=\"%s for command %s\"",forceMessagePtr, QueryURI::sCommandDefs[GetCommandID()]);
}
}
fQueryEvalMessage.Set(fQueryMessageBuff, strlen(fQueryMessageBuff));
}
}
else
{
switch(GetCommandID())
{
case kGETCommand:
{ // special case test. A query with no parameters is a get. A query with parameters requires a command.
if (fHasQuery && (this->GetCommand() == NULL || this->GetCommand()->Len == 0))
{ result = 400;
static char *message = "reason=\"command parameter is missing\"";
qtss_sprintf(evalMessageBuff, "%s",message);
fQueryEvalMessage.Set(evalMessageBuff, strlen(evalMessageBuff));
fQueryEvalResult = result;
return result;
}
}
break;
case kSETCommand:
{
if (NULL==GetValue())
{ result = 400;
static char *message = "No value";
qtss_sprintf(evalMessageBuff, "%s",message);
break;
}
result = CheckInvalidRecurseParam( (char *) evalMessageBuff);
if (result != 0)
break;
result = CheckInvalidIterator( (char *) evalMessageBuff);
if (result != 0)
break;
result = CheckInvalidArrayIterator( (char *) evalMessageBuff);
if (result != 0)
break;
}
break;
case kADDCommand:
{
if (0)
{
result = 501;
static char *message="No implementation";
qtss_sprintf(evalMessageBuff, "%s",message);
break;
}
if (NULL==GetValue())
{ result = 400;
static char *message = "Attribute value not defined";
qtss_sprintf(evalMessageBuff, "%s",message);
break;
}
result = CheckInvalidRecurseParam( (char *) evalMessageBuff);
if (result != 0)
break;
result = CheckInvalidIterator( (char *) evalMessageBuff);
if (result != 0)
break;
result = CheckInvalidArrayIterator( (char *) evalMessageBuff);
if (result != 0)
break;
}
break;
case kDELCommand:
{
result = CheckInvalidRecurseParam( (char *) evalMessageBuff);
if (result != 0)
break;
result = CheckInvalidIterator( (char *) evalMessageBuff);
if (result != 0)
break;
result = CheckInvalidArrayIterator( (char *) evalMessageBuff);
if (result != 0)
break;
}
break;
default:
{
result = 501;
static char *message="No implementation";
qtss_sprintf(evalMessageBuff, "%s",message);
break;
}
}
if (result != 0)
{ SInt32 theID = GetCommandID();
StrPtrLen *commandPtr = GetCommand();
if (theID < 0)
{ if (commandPtr)
{ qtss_sprintf(fQueryMessageBuff,"reason=\"%s for command %s\"",evalMessage.Ptr,commandPtr->Ptr);
}
}
else
{ //qtss_printf("Set fQueryMessageBuff=%s\n",evalMessage.Ptr);
qtss_sprintf(fQueryMessageBuff,"reason=\"%s for command %s\"",evalMessage.Ptr, QueryURI::sCommandDefs[GetCommandID()]);
}
}
else
{ qtss_sprintf(fQueryMessageBuff,"reason=\"OK\"");
}
fQueryEvalMessage.Set(fQueryMessageBuff, strlen(fQueryMessageBuff));
//qtss_printf("fQueryMessageBuff=%s\n",fQueryMessageBuff);
}
fQueryEvalResult = result;
return result;
}
void QueryURI::ParseQueryString(StringParser *parserPtr,StrPtrLen *urlStreamPtr)
{
StrPtrLen queryStr;
StringParser tempQueryParse(parserPtr->GetStream());// point to local copy xx
char *stopCharPtr = NULL;
char *startCharPtr = NULL;
UInt32 len = 0;
tempQueryParse.ConsumeUntil(NULL, sWhiteQuoteOrEOL); // stop on whitespace '"'
tempQueryParse.ConsumeWhitespace();
tempQueryParse.ConsumeUntil(NULL, '?'); // stop at start of query
tempQueryParse.Expect('?');
startCharPtr = tempQueryParse.GetCurrentPosition();
//qtss_printf("QueryURI::ParseQueryString start Position = '%s'\n",startCharPtr);
while (tempQueryParse.GetDataRemaining() > 0)
{
tempQueryParse.ConsumeUntil(NULL, sWhiteQuoteOrEOL); // stop on whitespace '"'
stopCharPtr = tempQueryParse.GetCurrentPosition();
if (*stopCharPtr == '"') // if quote read to next quote
{
tempQueryParse.ConsumeLength(NULL, 1);
tempQueryParse.ConsumeUntil(NULL, '"');
tempQueryParse.ConsumeLength(NULL, 1);
//qtss_printf("QueryURI::ParseQueryString is quote GetCurrentPosition = '%s' len = %"_U32BITARG_"\n",stopCharPtr, strlen(stopCharPtr));
}
else
{
//qtss_printf("QueryURI::ParseQueryString white or EOL GetCurrentPosition = '%s' len = %"_U32BITARG_"\n",stopCharPtr, strlen(stopCharPtr));
if (*stopCharPtr == ' ')
{ tempQueryParse.ConsumeWhitespace();
continue;
}
break;
}
}
len = (UInt32) ((PointerSizedInt)stopCharPtr - (PointerSizedInt) startCharPtr);
if (len < QueryURI::eMaxBufferSize)
{ if (len > 0)
fHasQuery = true;
queryStr.Set(fQueryBuffer,len);
memcpy(fQueryBuffer, startCharPtr, len );
fURIFieldSPL[eQuery].Set(queryStr.Ptr,queryStr.Len);
fURIFieldsPtr[eQuery].fData = &fURIFieldSPL[eQuery];
}
//qtss_printf("Query String = '%s' Query len = %"_U32BITARG_" parseLen = %"_U32BITARG_"\n",queryStr.Ptr, queryStr.Len,len);
};
void QueryURI::ParseURLString(StringParser *parserPtr,StrPtrLen *urlStreamPtr)
{
parserPtr->ConsumeWhitespace();
parserPtr->ConsumeUntilWhitespace(urlStreamPtr);
fAdminFullURI.Set(fURIBuffer, urlStreamPtr->Len);
if (urlStreamPtr->Len < QueryURI::eMaxBufferSize)
memcpy(fURIBuffer,urlStreamPtr->Ptr,urlStreamPtr->Len); // make a local copy in fAdminFullURI
//qtss_printf("QueryURI::ParseURLString fURIBuffer =%s len = %"_U32BITARG_"\n", fURIBuffer,urlStreamPtr->Len);
StringParser tempURLParse(&fAdminFullURI);// point to local copy
tempURLParse.ConsumeUntil(&fURIFieldSPL[eURL],'?'); // pull out URL
fURIFieldsPtr[eURL].fData = &fURIFieldSPL[eURL];
//qtss_printf("QueryURI::ParseURLString fURIFieldsPtr[eURL]="); PRINT_STR(fURIFieldsPtr[eURL].fData);
};
void QueryURI::URLParse(StrPtrLen *inStream)
{
if (inStream != NULL)
{
char * decodedRequest = NEW char[inStream->Len + 1];
Assert(decodedRequest != NULL);
decodedRequest[inStream->Len] = 0;
OSCharArrayDeleter decodedRequestDeleter(decodedRequest);
StringParser tempParser(inStream);
StrPtrLen URLToParse;
SInt32 URLoffset = 0;
if (inStream->Len > 0)
{ // skip past the HTTP command for the StringTranslator::DecodeURL but keep it in our decoded Request buffer
tempParser.ConsumeWhitespace();
tempParser.ConsumeWord(NULL);
tempParser.ConsumeWhitespace();
URLToParse.Set(tempParser.GetCurrentPosition(),tempParser.GetDataRemaining()); // this should be a '/' and is required by the DecodeURL routine
URLoffset = tempParser.GetDataParsedLen();
memcpy(decodedRequest, inStream->Ptr, URLoffset);
}
SInt32 decodedLen = StringTranslator::DecodeURL(URLToParse.Ptr, URLToParse.Len, &decodedRequest[URLoffset], inStream->Len);
StrPtrLen decodedRequestStr(decodedRequest,decodedLen);
StringParser parser(&decodedRequestStr);
StrPtrLen startFields;
StrPtrLen adminURI;
StrPtrLen streamURL;
do // once
{
if (decodedRequestStr.Len < 1)
{
//qtss_printf("no string to parse \n");
break;
}
if (decodedRequestStr.Len > QueryURI::eMaxBufferSize -1)
{
//qtss_printf("URL string bigger than Buffer size=%"_U32BITARG_"\n",decodedRequestStr.Len);
break;
}
StrPtrLen httpRequest;
parser.ConsumeWord(&httpRequest);
static StrPtrLen sPost("POST");
static StrPtrLen sGet("GET");
if ( false == httpRequest.Equal(sPost) && false == httpRequest.Equal(sGet) ) //bail if not a GET or POST
{
//qtss_printf("not a POST or GET \n");
break;
}
ParseURLString(&parser,&streamURL);
ParseQueryString(&parser,&streamURL);
if (fURIFieldSPL[eURL].Len > 0)
{
StrPtrLen tempStr;
StringParser URIParser(fURIFieldsPtr[eURL].fData); // parser now pointing to internal buffer root of URL
if(!URIParser.Expect('/'))
{
//qtss_printf("no starting slash\n");
break;
}
URIParser.ConsumeWord(&tempStr);
if ( !(tempStr.Len != 0 && tempStr.Equal(StrPtrLen(fURIFieldsPtr[eModuleID].fFieldName,fURIFieldsPtr[eModuleID].fFieldLen) )) )//check "modules" request
{
//qtss_printf("no %s in URL\n",fURIFieldsPtr[eModuleID].fFieldName);
break;
}
fURIFieldSPL[eModuleID] = tempStr;
fURIFieldsPtr[eModuleID].fData = &fURIFieldSPL[eModuleID];
if(!URIParser.Expect('/'))
{
//qtss_printf("no trailing slash for modules\n");
break;
}
URIParser.ConsumeWord(&tempStr);
if ( !(tempStr.Len != 0 && tempStr.Equal(StrPtrLen(fURIFieldsPtr[eRootID].fFieldName,fURIFieldsPtr[eRootID].fFieldLen) )) )//check "modules" request
{
//qtss_printf("no %s in URL\n", fURIFieldsPtr[eRootID].fFieldName);
break;
}
fIsAdminQuery = true; // ok it is for us
fURIFieldSPL[eRootID] = tempStr;
fURIFieldsPtr[eRootID].fData = &fURIFieldSPL[eRootID];
}
if (fURIFieldSPL[eQuery].Len > 0) // has query fields (step past ?)
{
StringParser queryParser(fURIFieldsPtr[eQuery].fData);
StrPtrLen tempStr(fURIFieldSPL[eQuery].Ptr,fURIFieldSPL[eQuery].Len);
StrPtrLen tempData;
//qtss_printf("queryParser=");PRINT_STR(fURIFieldsPtr[eQuery].fData);
while (queryParser.GetDataRemaining() != 0)
{ tempData.Set(NULL,0);
if (queryParser.GetDataRemaining())queryParser.ConsumeWhitespace();
if (queryParser.GetDataRemaining())queryParser.ConsumeUntil(&tempStr,(UInt8*) sNotQueryData);
if (tempStr.Len == 0)
{
//qtss_printf("no query name\n");
if (queryParser.GetDataRemaining())queryParser.ConsumeLength(NULL, 1);
continue;
}
if (queryParser.GetDataRemaining())queryParser.ConsumeWhitespace();
if(!queryParser.Expect('='))
{
//qtss_printf("no '=' for query name ");PRINT_STR(&tempStr);
if (queryParser.GetDataRemaining())queryParser.ConsumeLength(NULL, 1);
continue;
}
if (queryParser.GetDataRemaining())queryParser.ConsumeWhitespace();
char testQuote = queryParser.PeekFast();
if (testQuote == '"')
{ if (queryParser.GetDataRemaining())queryParser.ConsumeLength(NULL, 1);
if (queryParser.GetDataRemaining())queryParser.ConsumeUntil(&tempData, '"');
if (queryParser.GetDataRemaining())queryParser.ConsumeLength(NULL, 1);
}
else
{
if (queryParser.GetDataRemaining())queryParser.ConsumeUntil(&tempData,(UInt8*) sNotQueryData);
}
if (tempData.Len == 0)
{
if (queryParser.GetDataRemaining())queryParser.Expect('+');
if (queryParser.GetDataRemaining())queryParser.ConsumeWhitespace();
//qtss_printf("no query data for ");PRINT_STR(&tempStr);
continue;
}
if (queryParser.GetDataRemaining())queryParser.ConsumeWhitespace();
if (queryParser.GetDataRemaining())queryParser.Expect('+');
if (queryParser.GetDataRemaining())queryParser.ConsumeWhitespace();
//qtss_printf("set data =%s\n", tempData.Ptr);
StrPtrLen definedID;
UInt32 fieldID;
for (short testField = 0; testField < eNumAttributes; testField++)
{
definedID.Set(fURIFieldsPtr[testField].fFieldName,fURIFieldsPtr[testField].fFieldLen);
fieldID = fURIFieldsPtr[testField].fID;
if ( definedID.EqualIgnoreCase(tempStr.Ptr, tempStr.Len) ) // test (fURIFieldsPtr[fieldID].fData == NULL) to make first time setting only
{ // set the field value always takes the last appearance of the name=value pair
if (fieldID >= eFilter1)
{ UInt32 emptyId = eFilter1 + fNumFilters;
if (tempData.Len > 0)
{ fNumFilters ++;
fURIFieldSPL[emptyId].Set(tempData.Ptr,tempData.Len);
fURIFieldsPtr[emptyId].fData = &fURIFieldSPL[emptyId];
}
}
else
{
fURIFieldSPL[fieldID].Set(tempData.Ptr,tempData.Len);
fURIFieldsPtr[testField].fData = &fURIFieldSPL[fieldID];
}
}
}
}
}
} while (false);
/*
for (int count = 0; fURIFieldsPtr[count].fID != -1 ; count++)
{ //qtss_printf("QueryURI::URLParse %s=",fURIFieldsPtr[count].fFieldName); PRINT_STR(fURIFieldsPtr[count].fData);
}
*/
}
}

View file

@ -0,0 +1,212 @@
/*
*
* @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: AdminElements.h
Contains: implements various Admin Elements class
*/
#ifndef _ADMINQUERY_H_
#define _ADMINQUERY_H_
#ifndef __Win32__
#include <unistd.h> /* for getopt() et al */
#endif
#include <time.h>
#include <stdio.h> /* for qtss_printf */
#include <stdlib.h> /* for getloadavg & other useful stuff */
#include "QTSSAdminModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "OSHashTable.h"
#include "OSMutex.h"
#include "StrPtrLen.h"
#include "OSRef.h"
/*
r = recurse -> walk downward in hierarchy
v = verbose -> return full path in name
a = access -> return read/write access
t = type -> return type of value ** perhaps better not to support
f = filter -> return filter
p = path -> return path
*/
class QueryURI
{
public:
enum{ eMaxAttributeSize = 60 , eMaxBufferSize =2048};
struct URIField
{
char fFieldName[eMaxAttributeSize + 1];
UInt32 fFieldLen;
SInt32 fID;
StrPtrLen* fData;
};
enum
{
eModuleID = 0,
eRootID = 1,
eURL = 2,
eQuery = 3,
eParameters = 4,
eSnapshot = 5,
eCommand = 6,
eValue = 7,
eType = 8,
eAccess = 9,
eName = 10,
eFilter1,
eFilter2,
eFilter3,
eFilter4,
eFilter5,
eFilter6,
eFilter7,
eFilter8,
eFilter9,
eFilter10,
eNumAttributes
};
enum //commands
{
kGETCommand = 0,
kSETCommand = 1,
kADDCommand = 2,
kDELCommand = 3,
kLastCommand = 4
};
enum
{
kRecurseParam = 1 << 0,
kVerboseParam = 1 << 1,
kAccessParam = 1 << 2,
kTypeParam = 1 << 3,
kFilterParam = 1 << 4,
kPathParam = 1 << 5,
kDebugParam = 1 << 6,
kIndexParam = 1 << 7
};
static UInt8 sNotQueryData[];
static UInt8 sWhiteQuoteOrEOL[];
static UInt8 sWhitespaceOrQuoteMask[];
static URIField sURIFields[];
static char *sCommandDefs[];
URIField *fURIFieldsPtr;
void URLParse(StrPtrLen *inStream);
void SetQueryData() { if (fIsAdminQuery) { SetSnapShot(); SetParamBits(0); SetCommand(); SetAccessFlags(); } }
StrPtrLen* GetModuleID() { return fURIFieldsPtr[eModuleID].fData; };
StrPtrLen* GetRootID() { return fURIFieldsPtr[eRootID].fData; };
StrPtrLen* GetURL() { return fURIFieldsPtr[eURL].fData; };
StrPtrLen* GetQuery() { return fURIFieldsPtr[eQuery].fData; };
StrPtrLen* GetParameters() { return fURIFieldsPtr[eParameters].fData; };
StrPtrLen* GetSnapshot() { return fURIFieldsPtr[eSnapshot].fData; };
StrPtrLen* GetCommand() { return fURIFieldsPtr[eCommand].fData; };
StrPtrLen* GetValue() { return fURIFieldsPtr[eValue].fData; };
StrPtrLen* GetType() { return fURIFieldsPtr[eType].fData; };
StrPtrLen* GetAccess() { return fURIFieldsPtr[eAccess].fData; };
StrPtrLen* GetName() { return fURIFieldsPtr[eName].fData; };
StrPtrLen* GetFilter(UInt32 index) { return fURIFieldsPtr[eFilter1 + index].fData; };
StrPtrLen* GetEvalMsg() { return &fQueryEvalMessage; };
UInt32 GetAccessFlags() { return fAccessFlags; };
UInt32 GetSnapshotID() { return fSnapshotID; };
UInt32 GetParamBits() { return fParamBits; };
Bool16 IsAdminQuery() { return fIsAdminQuery; };
Bool16 UseSnapShot() { return fUseSnapShot; };
Bool16 RecurseParam() { return (Bool16) ( (fParamBits & kRecurseParam) != 0); };
Bool16 VerboseParam() { return (Bool16) ( (fParamBits & kVerboseParam) != 0); };
Bool16 AccessParam() { return (Bool16) ( (fParamBits & kAccessParam) != 0); };
Bool16 TypeParam() { return (Bool16) ( (fParamBits & kTypeParam) != 0); };
Bool16 FilterParam() { return (Bool16) ( (fParamBits & kFilterParam) != 0); };
Bool16 PathParam() { return (Bool16) ( (fParamBits & kPathParam) != 0); };
Bool16 DebugParam() { return (Bool16) ( (fParamBits & kDebugParam) != 0); };
Bool16 IndexParam() { return (Bool16) ( (fParamBits & kIndexParam) != 0); };
void SetQueryHasResponse() { fQueryHasResponse =true; };
Bool16 QueryHasReponse() { if (fQueryEvalResult > 0) return true; else return fQueryHasResponse; };
UInt32 GetEvaluResult() { return fQueryEvalResult; };
StrPtrLen* NextSegment(StrPtrLen *currentPathPtr, StrPtrLen *outNextPtr);
void SetAccessFlags();
void SetParamBits(UInt32 forcebits);
void SetSnapShot();
SInt32 GetCommandID() { return fTheCommand;};
char fLastPath[1024];
QueryURI (StrPtrLen *inStream);
~QueryURI();
UInt32 EvalQuery(UInt32 *forceResultPtr, char *forceMessagePtr);
char fQueryMessageBuff[1024];
Bool16 fIsPref;
SInt16 fNumFilters;
Bool16 fHasQuery;
private:
char fURIBuffer[QueryURI::eMaxBufferSize];
char fQueryBuffer[QueryURI::eMaxBufferSize];
StrPtrLen fURIFieldSPL[QueryURI::eNumAttributes];
StrPtrLen fAdminFullURI;
UInt32 fParamBits;
UInt32 fSnapshotID;
UInt32 fAccessFlags;
Bool16 fIsAdminQuery;
Bool16 fUseSnapShot;
StrPtrLen fCurrentPath;
StrPtrLen fNext;
Bool16 fQueryHasResponse;
UInt32 fQueryEvalResult;
StrPtrLen fQueryEvalMessage;
SInt32 fTheCommand;
void SetCommand();
void ParseURLString(StringParser *parserPtr,StrPtrLen *urlPtr);
void ParseQueryString(StringParser *parserPtr,StrPtrLen *urlPtr);
UInt32 CheckInvalidIterator(char* evalMessageBuff);
UInt32 CheckInvalidArrayIterator(char* evalMessageBuff);
UInt32 CheckInvalidRecurseParam(char* evalMessageBuff);
};
#endif // _ADMINQUERY_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
/*
*
* @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: QTSSWebStatsModule.h
Contains: A module that uses the information available in the server
to present a web page containing that information. Uses the Filter
module feature of QTSS API.
*/
#ifndef __QTSSADMINMODULE_H__
#define __QTSSADMINMODULE_H__
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSAdminModule_Main(void* inPrivateArgs);
}
#endif // __QTSSADMINMODULE_H__

View file

@ -0,0 +1,646 @@
/*
*
* Copyright (c) 1999-2005 Apple Computer, Inc. All Rights Reserved.
*
* @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: DSAccessChecker.cpp
Contains: Class definition for access checking via Open Directory
Created By: Dan Sinema
Created: Jan 14, 2005
*/
/*
* Directory Service code added by Dan Sinema
*
* Jan 14, 2005 - Cleaned up code and added more comments.
* Nov 8, 2004 - Finsihed final code. Added group support.
*
*/
// ANSI / POSIX Headers
#include <grp.h>
#include <membership.h>
#include <pwd.h>
#include <signal.h>
#include <unistd.h>
// STL Headers
#include <cstdio>
#include <cstdlib>
#include <cstring>
// Project Headers
#include "SafeStdLib.h"
#include "StrPtrLen.h"
#include "StringParser.h"
#include "ResizeableStringFormatter.h"
#include "DSAccessChecker.h"
#include "DSDataList.h"
#include "QTAccessFile.h"
#define DEBUG_DSACCESS 0
#define debug_printf if (DEBUG_DSACCESS) ::qtss_printf
#include <AvailabilityMacros.h>
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
#if OSX_OD_API
#define OD_API 1
#define DS_API 0
#else
#define OD_API 0
#define DS_API 1
#endif
#else
#define OD_API 0
#define DS_API 1
#endif
// Framework Headers
#if DS_API
#include <DirectoryService/DirectoryService.h>
using namespace DirectoryServices;
#endif
#if OD_API
#include <OpenDirectory/OpenDirectory.h>
#endif
#if __LP64__
#define ds_API_PTR UInt32*
#else
#define ds_API_PTR long unsigned int*
#endif
#pragma mark DSAccessChecker class globals
const char* DSAccessChecker::kDefaultAccessFileName = "qtaccess";
#pragma mark DSAccessChecker class implementation
#if DS_API
// Find a list of records that match the given criteria.
static SInt32 _GetRecordList(
tDirReference inDSRef,
const char *inDomain,
const char *inRecName,
const char *inRecType,
tDataList *inAttrType,
tDirNodeReference *outNodeRef,
tDataBuffer **outDataBuff,
UInt32 *outRecCount )
{
SInt32 status = eDSNoErr;
tDataBuffer *pDataBuff = NULL;
tDirNodeReference nodeRef = 0;
tContextData context = NULL;
UInt32 nodeCount = 0;
tDataList *nodeName = NULL;
UInt32 recCount = 0;
*outNodeRef = 0;
*outDataBuff = NULL;
*outRecCount = 0;
pDataBuff = ::dsDataBufferAllocate( inDSRef, 4096 );
if (pDataBuff == NULL)
{
// We need the buffer for locating the node for which the user object resides
debug_printf("QTSSODAuthModule: Unable to allocate buffer.\n");
return eDSAllocationFailed;
}
// This is the default action with no domain: use the Search node.
status = ::dsFindDirNodes( inDSRef, pDataBuff, NULL, eDSSearchNodeName, (ds_API_PTR) &nodeCount, &context );
if ( context != NULL )
{
::dsReleaseContinueData( inDSRef, context );
context = NULL;
}
// Check for failure of the dsFindDirNodes
// Node count less than 1 means no node found...doh!
if ( nodeCount < 1 )
{
status = eDSNodeNotFound;
}
if ( status != eDSNoErr )
{
goto cleanupBadGetRecordList;
}
// Extract the name of the found node.
status = ::dsGetDirNodeName( inDSRef, pDataBuff, 1, &nodeName );
if (status == eDSNoErr)
{
// Open the node so we can do the DS magic
status = ::dsOpenDirNode( inDSRef, nodeName, &nodeRef );
::dsDataListDeallocate( inDSRef, nodeName );
std::free( nodeName );
nodeName = NULL;
}
if (status != eDSNoErr)
{
// Bail if we cannot open the node.
debug_printf("QTSSODAuthModule: Could not open node - error: %"_S32BITARG_"\n", status);
}
else
{
// Specify what we are looking for...
// pRecName: the passed name of the record
// pRecType: the passed name of the record type
// pAttrType: attributes to return to the caller
DSDataList recName( inDSRef, inRecName );
DSDataList recType( inDSRef, inRecType );
recCount = 1;
// Find the record that matchs the above criteria
status = ::dsGetRecordList( nodeRef, pDataBuff, recName, eDSExact, recType, inAttrType, 0, (ds_API_PTR)&recCount, &context );
if ( context != NULL )
{
::dsReleaseContinueData( inDSRef, context );
context = NULL;
}
if ( recCount == 0 )
{
status = eDSRecordNotFound;
debug_printf("QTSSODAuthModule: No records found.\n");
}
else if ( status != eDSNoErr )
{
debug_printf("QTSSODAuthModule: No records found - error: %"_S32BITARG_"\n", status);
}
}
if ( status == eDSNoErr )
{
*outNodeRef = nodeRef;
*outDataBuff = pDataBuff;
*outRecCount = recCount;
return eDSNoErr;
}
cleanupBadGetRecordList:
if ( nodeRef != 0 )
{
::dsCloseDirNode( nodeRef );
}
// This variable is guaranteed to be valid because the function would
// have returned if it was bad.
::dsDataBufferDeAllocate( inDSRef, pDataBuff );
return status;
}
static SInt32 _FindRecordNode(
tDirReference inDSRef,
const char *inDomain,
const char *inRecName,
const char *inRecType,
tDataList *outHomeNodeName )
{
tDataBuffer *pRecBuff = NULL;
tDirNodeReference nodeRef = 0;
SInt32 status = eDSNoErr;
UInt32 attrIndex = 0;
UInt32 recCount = 0;
tRecordEntry *pRecEntry = NULL;
tAttributeListRef attrListRef = 0;
if ( outHomeNodeName == NULL )
{
return eDSNullDataList;
}
std::memset( outHomeNodeName, 0, sizeof( *outHomeNodeName) );
// A Username and Password is needed, if either one is not present then bail!
if ( inRecName == NULL )
{
return eDSInvalidRecordName;
}
if ( inRecType == NULL )
{
return eDSInvalidRecordType;
}
status = ::_GetRecordList( inDSRef, inDomain, inRecName, inRecType,
DSDataList (inDSRef, kDSNAttrMetaNodeLocation),
&nodeRef, &pRecBuff, &recCount );
if ( status != eDSNoErr )
{
return status;
}
// Get the record entry out of the list, there should only be one record!
status = ::dsGetRecordEntry( nodeRef, pRecBuff, 1, &attrListRef, &pRecEntry );
if ( status != eDSNoErr )
{
// These variables are guaranteed to be valid because the function would
// have returned if they were bad.
::dsCloseDirNode( nodeRef );
::dsDataBufferDeAllocate( inDSRef, pRecBuff );
return status;
}
// Now loop through attributes of the entry...looking for kDSNAttrMetaNodeLocation and kDSNAttrRecordName
for ( attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++ )
{
tAttributeEntryPtr pAttrEntry = NULL;
tAttributeValueListRef valueRef = 0;
status = ::dsGetAttributeEntry( nodeRef, pRecBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry );
if ( ( status != eDSNoErr ) || ( pAttrEntry == NULL ) )
continue;
// Test for kDSNAttrMetaNodeLocation
if ( std::strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0 )
{
tAttributeValueEntry *pValueEntry = NULL;
// If it matches then get the value of the attribute
status = ::dsGetAttributeValue( nodeRef, pRecBuff, 1, valueRef, &pValueEntry );
if ( ( status == eDSNoErr ) && ( pValueEntry != NULL ) )
{
// Store the node location in outHomeNodeName
if ( outHomeNodeName->fDataNodeCount != 0 )
{
debug_printf("QTSSODAuthModule: Multiple user locations found!?\n");
}
else
{
status = ::dsBuildListFromPathAlloc( inDSRef, outHomeNodeName, pValueEntry->fAttributeValueData.fBufferData, "/" );
::dsDeallocAttributeValueEntry( inDSRef, pValueEntry );
}
}
}
::dsDeallocAttributeEntry( inDSRef, pAttrEntry );
::dsCloseAttributeValueList( valueRef );
}
// Cleanup dsGetRecordEntry() return values.
::dsCloseAttributeList( attrListRef );
::dsDeallocRecordEntry( inDSRef, pRecEntry );
::dsCloseDirNode( nodeRef );
::dsDataBufferDeAllocate( inDSRef, pRecBuff );
return status;
}
#endif
#pragma mark -
#pragma mark "Public Methods"
// Now the class proper.
DSAccessChecker::DSAccessChecker()
{
}
DSAccessChecker::~DSAccessChecker()
{
#if DEBUG
debug_printf("QTSSODAuthModule: Access checker object destroyed.\n");
#endif
}
#if 0 //OD_API notes
/*
This is Leopard or later code so some check before using OD based code is needed.
Implement this api for digest auth.
*/
#include <OpenDirectory/OpenDirectoryPriv.h>
/System/Library/PrivateFrameworks/OpenDirectory.framework/Frameworks/CFOpenDirectory.framework/CFOpenDirectory
CFErrorRef outError = NULL;
ODNodeRef cfNode = ODNodeCreateWithNodeType( kCFAllocatorDefault, kODSessionDefault, kODTypeAuthenticationSearchNode, NULL );
if (cfNode)
{
ODRecordRef cfUserRecord = ODNodeCopyRecord( kCFAllocatorDefault, cfNode, CFSTR("username"), NULL );
if (cfUserRecord != NULL)
{
CFArrayRef authItems = CFArrayCreate.... ( username, server challenge, client response, http method);
// for DIGEST_MD5
if (ODRecordVerifyPasswordExtended( cfUserRecord, CFSTR(kDSStdAuthDIGEST_MD5), authItems, NULL, NULL, &outError ))
{
}
// this for password
if (ODRecordVerifyPassword( cfUserRecord, CFSTR("password") ) )
{
}
CFRelease( cfUserRecord );
CFRelease( autItems );
}
CFRelease( cfNode );
}
// kDSStdAuthDIGEST_MD5
* user name in UTF8 encoding,
* server challenge in UTF8 encoding,
* client response data,
* HTTP method in UTF8 encoding
}
}
#endif
#if OD_API
Bool16 DSAccessChecker::CheckPassword(const char* inUsername, const char* inPassword)
{
Bool16 checkedResult = false;
CFErrorRef outError = NULL;
debug_printf("DSAccessChecker::CheckPassword userName=%s password=%s\n", inUsername,inPassword);
ODNodeRef cfNodeRef= ODNodeCreateWithNodeType( kCFAllocatorDefault, kODSessionDefault, kODTypeAuthenticationSearchNode, NULL );
//static ODRecordRef _ODNodeCopyRecord( ODNodeRef inNodeRef, CFStringRef inRecordType, CFStringRef inRecordName, CFArrayRef inAttributes, CFErrorRef *outError );
CFStringRef cfPassword = CFStringCreateWithCString(kCFAllocatorDefault, inPassword, kCFStringEncodingUTF8);
CFStringRef cfUsername = CFStringCreateWithCString(kCFAllocatorDefault, inUsername, kCFStringEncodingUTF8);
CFTypeRef vals[] = { CFSTR(kDSAttributesStandardAll) };
CFArrayRef reqAttrs = CFArrayCreate(NULL, vals,1, &kCFTypeArrayCallBacks);
ODRecordRef cfUserRecord = ODNodeCopyRecord(cfNodeRef, CFSTR(kDSStdRecordTypeUsers), cfUsername, reqAttrs, &outError );
if (cfNodeRef && cfUserRecord && cfPassword && cfUsername)
{
// this for password
if ( ODRecordVerifyPassword( cfUserRecord, cfPassword , NULL ) )
{ checkedResult = true;
debug_printf("DSAccessChecker::CheckPassword ODRecordVerifyPassword user is authenticated\n");
}
else
{ debug_printf("DSAccessChecker::CheckPassword ODRecordVerifyPassword user failed to authenticate\n");
}
}
if (reqAttrs) CFRelease( reqAttrs );
if (cfUserRecord) CFRelease( cfUserRecord );
if (cfPassword) CFRelease( cfPassword );
if (cfUsername) CFRelease( cfUsername );
if (cfNodeRef) CFRelease( cfNodeRef );
return checkedResult;
}
Bool16 DSAccessChecker::CheckDigest(const char* inUsername, const char* inServerChallenge, const char* inClientResponse, const char* inMethod)
{
Bool16 checkedResult = false;
CFErrorRef outError = NULL;
CFArrayRef outItems = NULL;
if (NULL == inUsername || NULL == inServerChallenge || NULL == inClientResponse )
return false;
ResizeableStringFormatter challengeString;
challengeString.Put((char*) inServerChallenge);
challengeString.PutTerminator();
char* challengeCString= challengeString.GetBufPtr();
ResizeableStringFormatter responseString;
responseString.Put( (char*)inClientResponse);
responseString.PutTerminator();
char* responseCString= responseString.GetBufPtr();
ODNodeRef cfNode = ODNodeCreateWithNodeType( kCFAllocatorDefault, kODSessionDefault, kODTypeAuthenticationSearchNode, NULL );
debug_printf("DSAccessChecker::CheckDigest \nuserName=[%s] \nchallenge=[%s] \nresponse=[%s] \nmethod=[%s]\n", inUsername,challengeCString, responseCString,inMethod);
CFTypeRef vals[] = { CFSTR(kDSAttributesStandardAll) };
CFArrayRef reqAttrs = CFArrayCreate(NULL, vals,1, &kCFTypeArrayCallBacks);
CFStringRef cfUsername = CFStringCreateWithCString(kCFAllocatorDefault, inUsername, kCFStringEncodingUTF8);
ODRecordRef cfUserRecord = ODNodeCopyRecord(cfNode, CFSTR(kDSStdRecordTypeUsers), cfUsername, reqAttrs, &outError );
CFRelease( cfUsername );
cfUsername = NULL;
enum { kNumAuthValues=4 };
CFStringRef cfStringArray[kNumAuthValues];
cfStringArray[0] = CFStringCreateWithCString(kCFAllocatorDefault, inUsername, kCFStringEncodingUTF8);
cfStringArray[1] = CFStringCreateWithCString(kCFAllocatorDefault, challengeCString, kCFStringEncodingUTF8);
cfStringArray[2] = CFStringCreateWithCString(kCFAllocatorDefault, responseCString, kCFStringEncodingUTF8);
cfStringArray[3] = CFStringCreateWithCString(kCFAllocatorDefault, inMethod, kCFStringEncodingUTF8);
CFArrayRef cfAuthItems = CFArrayCreate(kCFAllocatorDefault, (const void **) &cfStringArray,kNumAuthValues, &kCFTypeArrayCallBacks);
if (cfNode && cfUserRecord && cfAuthItems)
{
debug_printf("DSAccessChecker::CheckDigest call ODRecordVerifyPasswordExtended\n");
// for DIGEST_MD5
if (ODRecordVerifyPasswordExtended( cfUserRecord, CFSTR(kDSStdAuthDIGEST_MD5), cfAuthItems, &outItems, NULL, &outError ))
{
checkedResult = true;
debug_printf("DSAccessChecker::CheckDigest SUCCESS ODRecordVerifyPasswordExtended response=%d\n",outError);
}
else
{ debug_printf("DSAccessChecker::CheckDigest ODRecordVerifyPasswordExtended response=%d\n", outError ? CFErrorGetCode(outError) : -1);
}
}
for (int i = 0; i < kNumAuthValues; i++)
CFRelease(cfStringArray[i]);
if (reqAttrs) CFRelease( reqAttrs );
if (cfAuthItems) CFRelease( cfAuthItems );
if (cfUserRecord) CFRelease( cfUserRecord );
if (cfNode) CFRelease( cfNode );
if (outItems) CFRelease( outItems );
if (outError) CFRelease( outError );
return checkedResult;
}
#endif
#if DS_API
Bool16 DSAccessChecker::CheckPassword(const char* inUsername, const char* inPassword)
{
tDirReference dsRef = 0;
tDataList userNode = { 0, NULL };
tDirNodeReference nodeRef = 0;
SInt32 status = eDSNoErr;
// A Username and Password is needed, if either one is not present then bail!
if ( inUsername == NULL )
{
debug_printf("QTSSODAuthModule: Username required.\n");
return false;
}
if ( inPassword == NULL )
{
debug_printf("QTSSODAuthModule: Password required.\n");
return false;
}
status = ::dsOpenDirService( &dsRef );
if ( status != eDSNoErr )
{
// Some DS error, tell the admin what the error is and bail.
// Error can be found in DirectoryService man page.
debug_printf("QTSSODAuthModule: Could not open Directory Services - error: %"_S32BITARG_"", status);
return false;
}
status = _FindRecordNode( dsRef, NULL, inUsername, kDSStdRecordTypeUsers, &userNode );
if ( status != eDSNoErr )
{
debug_printf("QTSSODAuthModule: Could not find user node.\n");
return false;
}
// Now that we know the node location of the user object, lets open that node.
status = ::dsOpenDirNode( dsRef, &userNode, &nodeRef );
::dsDataListDeallocate( dsRef, &userNode );
if ( status == eDSNoErr )
{
UInt32 uiLen = std::strlen( inUsername );
tDataNode *pAuthType = ::dsDataNodeAllocateString( dsRef, kDSStdAuthNodeNativeClearTextOK );
tDataBuffer *pStepBuff = ::dsDataBufferAllocate( dsRef, 128 );
tDataBuffer *pAuthBuff = ::dsDataBufferAllocate( dsRef, ( sizeof( UInt32 ) + sizeof( UInt32 ) + uiLen + std::strlen( inPassword ) ) );
if ( ( pStepBuff != NULL ) && ( pAuthType != NULL ) && ( pAuthBuff != NULL ) )
{
UInt32 uiCurr = 0;
// Copy username (that is passed into this function) into buffer for dsDoDirNodeAuth()
std::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( UInt32 ) );
uiCurr += sizeof( UInt32 );
std::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inUsername, uiLen );
uiCurr += uiLen;
// Copy password into a buffer for dsDoDirNodeAuth()
uiLen = std::strlen( inPassword );
std::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( UInt32 ) );
uiCurr += sizeof( UInt32 );
std::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inPassword, uiLen );
uiCurr += uiLen;
pAuthBuff->fBufferLength = uiCurr;
// Perform the authentication
status = ::dsDoDirNodeAuth( nodeRef, pAuthType, 1, pAuthBuff, pStepBuff, NULL );
// Since the buffer held a name & password, clear it immediately.
std::memset( pAuthBuff, 0, pAuthBuff->fBufferSize );
}
// Free the auth buffer.
if ( pAuthBuff != NULL )
{
::dsDataBufferDeAllocate( dsRef, pAuthBuff );
}
// Free the ignored step buffer.
if ( pStepBuff != NULL )
{
::dsDataBufferDeAllocate( dsRef, pStepBuff );
}
// Free the auth string.
if ( pAuthType != NULL )
{
::dsDataNodeDeAllocate( dsRef, pAuthType );
}
::dsCloseDirNode( nodeRef );
}
::dsCloseDirService( dsRef );
if( status == eDSNoErr )
{
debug_printf("QTSSODAuthModule: Authentication is good.\n");
return true;
}
// For admins running QTSS in debug
debug_printf("QTSSODAuthModule: OD returned %"_S32BITARG_" status.\n", status);
debug_printf("QTSSODAuthModule: Authentication failed.\n");
// If the Authentication failed then return false, which boots the user...
return false;
}
Bool16 DSAccessChecker::CheckDigest(const char* inUsername, const char* inServerChallenge, const char* inClientResponse, const char* inMethod)
{
return false;
}
#endif
#pragma mark -
#pragma mark "Protected Methods"
Bool16 DSAccessChecker::CheckGroupMembership(const char* inUsername, const char* inGroupName)
{
// In Tiger, group membership is painfully simple: we ask memberd for it!
struct passwd *user = NULL;
struct group *group = NULL;
uuid_t userID;
uuid_t groupID;
int isMember = 0;
// Look up the user using the POSIX APIs: only care about the UID.
user = getpwnam(inUsername);
endpwent();
if ( user == NULL )
return false;
uuid_clear(userID);
if ( mbr_uid_to_uuid(user->pw_uid, userID) )
return false;
// Look up the group using the POSIX APIs: only care about the GID.
group = getgrnam(inGroupName);
endgrent();
if ( group == NULL )
return false;
uuid_clear(groupID);
if ( mbr_gid_to_uuid(group->gr_gid, groupID) )
return false;
// mbr_check_membership() returns 0 on success and error code on failure.
if ( mbr_check_membership(userID, groupID, &isMember) )
return false;
return (bool)isMember;
}

View file

@ -0,0 +1,70 @@
/*
*
* @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: DSAccessChecker.h
Contains: Class definition for access checking via Open Directory
Created By: Dan Sinema
Created: Jan 14, 2005
*/
#ifndef _QTSSACCESSCHECKER_H_
#define _QTSSACCESSCHECKER_H_
// STL Headers
#include <cstdio> // for struct FILE
#include <string>
#include "QTAccessFile.h"
class DSAccessChecker
{
/*
Access check logic:
If "modAccess_enabled" == "enabled,
*/
public:
static const char* kDefaultAccessFileName;
DSAccessChecker();
virtual ~DSAccessChecker();
Bool16 CheckPassword(const char* inUsername, const char* inPassword);
Bool16 CheckDigest(const char* inUsername, const char* inServerChallenge, const char* inClientResponse, const char* inMethod);
protected:
Bool16 CheckGroupMembership(const char* inUsername, const char* inGroupName);
};
#endif //_QTSSACCESSCHECKER_H_

View file

@ -0,0 +1,91 @@
/*
*
* @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: CDirService.h
Contains: Implements DSException.
Created By: chris jalbert
Created: 26 July 2000
*/
// This file is only meaningful if exceptions are enabled.
#if USE_EXCEPTIONS
// STL and Std C++ Library Headers
#include <cstdlib> // for abs()
#include <stdexcept> // for standard exceptions
// Project Headers
#include "CDirService.h"
using namespace DirectoryServices ;
using namespace std ;
// ----------------------------------------------------------------------------
// ¥ CDirService Class Globals
// These private typedefs, globals, and functions are not declared statically
// in the class definition because I want to hide the implementation details
// and reduce unrelated dependencies in the class header.
// ----------------------------------------------------------------------------
static const char * const _DSErr = "DirectoryService error: " ;
/*
* Convert an UInt32 to ASCII for printf purposes, returning
* a pointer to the first character of the string representation.
* Borrowed from vfprintf.c.
*/
static char *_ltoa (
SInt32 val,
char *endp)
{
register char *cp = endp ;
register SInt32 sval = std::abs (val) ;
// Terminate the string.
*--cp = '\0' ;
do {
*--cp = '0' + (sval % 10) ;
sval /= 10 ;
} while (sval != 0) ;
// Handle signed values.
if (val < 0)
*--cp = '-' ;
return cp ;
}
DSException::DSException (tDirStatus err)
: inherited (string (_DSErr) + _ltoa (err, &mErrStr[sizeof (mErrStr)])),
mErr (err)
{
}
#endif /* USE_EXCEPTIONS */

View file

@ -0,0 +1,87 @@
/*
*
* @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: CDirService.h
Contains: Defines DSException.
Created By: chris jalbert
Created: 26 July 2000
*/
#ifndef _CDirService_h
#define _CDirService_h
// Framework Headers
#include <DirectoryService/DirServicesTypes.h>
namespace DirectoryServices {
const tDirReference kDSDirRefNull = 0 ;
// This file is only meaningful if exceptions are enabled.
#if __EXCEPTIONS
// STL and Std C++ Library Headers
#include <stdexcept> // for standard exceptions
//-----------------------------------------------------------------------------
// ¥ DSException - exception class that wraps a tDirStatus result code.
//-----------------------------------------------------------------------------
class DSException : public std::runtime_error {
public:
typedef std::runtime_error inherited ;
DSException ( tDirStatus err ) ;
tDirStatus status ( void ) const { return mErr ; }
private:
tDirStatus mErr ;
char mErrStr [12] ;
} ;
#define throw_ds_error(err) throw DSException(err)
#else /* __EXCEPTIONS */
//-----------------------------------------------------------------------------
// ¥ DSException - with exceptions disabled, this is dummy code.
//-----------------------------------------------------------------------------
class DSException {
public:
DSException ( tDirStatus ) { }
} ;
// Define away the throw spec.
#define throw(spec)
#define throw_ds_error(err)
#endif /* __EXCEPTIONS */
} // namespace DirectoryServices
#endif /* _CDirService_h */

View file

@ -0,0 +1,94 @@
/*
*
* @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: DSBuffer.cpp
Contains: Class implementation for Open Directory buffers
(wraps tDataBufferPtr)
Created By: chris jalbert
Created: 28 November 2000
*/
// ANSI / POSIX headers
// STL and Std C++ Library Headers
#include <stdexcept> // for standard exceptions
// Framework Headers
#include <DirectoryService/DirServices.h>
// Project Headers
#include "DSBuffer.h"
using namespace DirectoryServices ;
// ----------------------------------------------------------------------------
// ¥ DSBuffer Class Globals
// These private typedefs, globals, and functions are not declared statically
// in the class definition because I want to hide the implementation details
// and reduce unrelated dependencies in the class header.
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ¥ DSBuffer Protected Instance Methods
// ----------------------------------------------------------------------------
#pragma mark **** DSBuffer Protected Instance Methods ****
// ----------------------------------------------------------------------------
// ¥ÊGrow
// All memory management (even in the c'tors) are handled in this method.
// An inNewSize value of 0 implies the use of the default buffer size!
// To leave the buffer alone, call with an argument value of 1.
// ----------------------------------------------------------------------------
tDataBufferPtr DSBuffer::grow ( size_t inNewSize )
{
if (!inNewSize)
inNewSize = kDefaultSize ;
if (mBuffer && (inNewSize <= mBuffer->fBufferSize))
return mBuffer ;
register size_t ulTemp = 16 ;
if (inNewSize == kDefaultSize)
ulTemp = inNewSize ;
else
for ( ; ulTemp < inNewSize ; ulTemp <<= 1) ;
register tDataBufferPtr bufNew = ::dsDataBufferAllocate (mDirRef, ulTemp) ;
if (!bufNew)
throw_ds_error (eDSAllocationFailed) ;
if (mBuffer && (ulTemp = mBuffer->fBufferLength))
std::memcpy (bufNew->fBufferData, mBuffer->fBufferData, ulTemp) ;
else
ulTemp = 0 ;
bufNew->fBufferLength = ulTemp ;
::dsDataBufferDeAllocate (mDirRef, mBuffer) ;
return (mBuffer = bufNew) ;
}

View file

@ -0,0 +1,125 @@
/*
*
* @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: DSBuffer.h
Contains: Class definition for Open Directory buffers
(wraps tDataBufferPtr)
Created By: chris jalbert
Created: 26 July 2000
*/
#ifndef _DSBuffer_h
#define _DSBuffer_h
// Framework Headers
#include <DirectoryService/DirServicesTypes.h>
#include <DirectoryService/DirServicesUtils.h>
// Project Headers
#include "CDirService.h"
namespace DirectoryServices {
//-----------------------------------------------------------------------------
// ¥ DSBuffer - a wrapper for tDataBufferPtr.
// This should be considered a private class, primarily used by DSNode.
// All methods except Grow() are inlined for performance reasons (sorry).
//-----------------------------------------------------------------------------
class DSBuffer
{
public:
/**** Typedefs, enums, and constants. ****/
enum { kDefaultSize = 128 } ;
public:
/**** Instance methods. ****/
// ctor and dtor.
DSBuffer ( tDirReference inDirRef = 0,
size_t inBufferSize = kDefaultSize ) throw (DSException)
: mDirRef (inDirRef), mBuffer (0)
{ if (!grow (inBufferSize)) throw_ds_error (eDSAllocationFailed) ; }
~DSBuffer ( void ) throw ()
{ if (mBuffer) ::dsDataBufferDeAllocate (mDirRef, mBuffer) ; }
// Inline accessors.
size_t capacity ( void ) const throw ()
{ return (size_t) mBuffer->fBufferSize ; }
size_t size ( void ) const throw ()
{ return (size_t) mBuffer->fBufferSize ; }
size_t length ( void ) const throw ()
{ return (size_t) mBuffer->fBufferLength ; }
const char *c_str ( void ) const throw ()
{ return (const char *) mBuffer->fBufferData ; }
const void *data ( void ) const throw ()
{ return (const void *) mBuffer->fBufferData ; }
// Inline setters.
void clear ( void ) throw ()
{ mBuffer->fBufferLength = 0 ; }
void resize ( size_t inLength ) throw (DSException)
{ if (inLength > mBuffer->fBufferSize)
throw_ds_error (eDSBufferTooSmall) ;
mBuffer->fBufferLength = inLength ; }
void set ( const char *inString ) throw (DSException)
{ clear () ; append (inString) ; }
void set ( const void *inData, size_t inLength ) throw (DSException)
{ clear () ; append (inData, inLength) ; }
void append ( const char *inString ) throw (DSException)
{ append ((const void *) inString, 1 + strlen (inString)) ; }
void append ( const void *inData, size_t inLength ) throw (DSException)
{ grow (mBuffer->fBufferLength + inLength) ;
char *cpBuf = mBuffer->fBufferData + mBuffer->fBufferLength ;
std::memcpy (cpBuf, inData, inLength) ;
mBuffer->fBufferLength += inLength ; }
// Casting operators.
tDataBufferPtr operator->() throw ()
{ return mBuffer ; }
operator tDataBufferPtr() const throw ()
{ return mBuffer ; }
operator const char*() const throw ()
{ return this->c_str () ; }
operator const void*() const throw ()
{ return this->data () ; }
protected:
/**** Instance methods accessible only to class and subclasses. ****/
tDataBufferPtr grow ( size_t inNewSize ) throw (DSException) ;
/**** Instance data. ****/
tDirReference mDirRef ;
tDataBufferPtr mBuffer ;
} ;
} // namespace DirectoryServices
#endif /* _DSBuffer_h */

View file

@ -0,0 +1,140 @@
/*
*
* @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: DSDataList.h
Contains: Class definition for Open Directory data list
(wraps tDataListPtr)
Created By: chris jalbert
Created: 26 July 2000
*/
#ifndef _DSDataList_h
#define _DSDataList_h
// ANSI / POSIX Headers
#include <stdarg.h> // for varargs stuff
#include <string.h> // for memset()
// STL and Std C++ Library Headers
#include <memory> // for auto_ptr<>
// Framework Headers
#include <DirectoryService/DirServicesTypes.h>
#include <DirectoryService/DirServicesUtils.h>
// Project Headers
#include "DSDataNode.h"
namespace DirectoryServices {
//-----------------------------------------------------------------------------
// ¥ DSDataList - simple wrapper for tDataBufferPtr.
// This should be considered a private class, primarily used by DSNode.
// All methods are inlined for performance reasons (sorry).
// Logically, a tDataList is a collection of tDataNode's, so this
// implementation offers GetCount() and operator[](u_long) methods.
//-----------------------------------------------------------------------------
class DSDataList
{
public:
/**** Instance methods. ****/
// ctor and dtor.
/* Not used anywhere and conflicts with next ctor, which is more useful.
DSDataList ( tDirReference inDirRef,
const char *inString, ... ) throw (DSException)
: mDirRef (inDirRef)
{ va_list args ; va_start (args, inString) ;
std::memset (&mList, 0, sizeof (mList)) ;
tDirStatus nError = ::dsBuildListFromStringsAllocV (
inDirRef, &mList, inString, args) ;
va_end (args) ;
if (nError) throw_ds_error (nError) ; }
*/
DSDataList ( tDirReference inDirRef,
const char *inPath ) throw (DSException)
: mDirRef (inDirRef)
{ std::memset (&mList, 0, sizeof (mList)) ;
tDirStatus nError = ::dsBuildListFromPathAlloc (inDirRef, &mList, inPath, "/") ;
if (nError) throw_ds_error (nError) ; }
DSDataList ( const DSDataList& inOrg ) throw (DSException)
: mDirRef (inOrg.mDirRef)
{ tDataList *dlp = ::dsDataListCopyList (inOrg.mDirRef, &inOrg.mList) ;
if (!dlp) throw_ds_error (eDSAllocationFailed) ;
mList = *dlp ; std::free (dlp) ; }
// The following constructor changes the ownership of the list buffer!
DSDataList ( tDirReference inDirRef,
tDataListPtr inList = 0 ) throw (DSException)
: mDirRef (inDirRef)
{ if (inList) {
mList = *inList ; std::memset (inList, 0, sizeof (mList)) ;
} else std::memset (&mList, 0, sizeof (mList)) ; }
~DSDataList ( void ) throw ()
{ ::dsDataListDeallocate (mDirRef, &mList) ; }
// Inline accessors.
UInt32 count ( void ) const throw ()
{ return ::dsDataListGetNodeCount (&mList) ; }
size_t length ( void ) const throw ()
{ return (size_t) ::dsGetDataLength (&mList) ; }
// GetPath()'s return value will be freed when it goes out of scope.
// If it is important, COPY IT to another auto_ptr (which will
// properly invalidate the original).
std::auto_ptr<const char> path ( const char *inSep = "/" ) const
{ return std::auto_ptr<const char> (::dsGetPathFromList (mDirRef,
&mList, inSep)) ; }
// Casting operators.
operator tDataListPtr() throw ()
{ return &mList ; }
operator const tDataList*() const throw ()
{ return &mList ; }
DSDataNode* operator[] ( UInt32 inIndex ) const throw (DSException)
{ tDataNodePtr dnTemp ;
tDirStatus nError = ::dsDataListGetNodeAlloc (
mDirRef, &mList, inIndex, &dnTemp) ;
if (nError) throw_ds_error (nError) ;
return new DSDataNode (mDirRef, dnTemp) ; }
// Setters.
void append ( const char *inString ) throw (DSException)
{ tDirStatus nError = ::dsAppendStringToListAlloc (
mDirRef, &mList, inString) ;
if (nError) throw_ds_error (nError) ; }
protected:
/**** Instance data. ****/
tDirReference mDirRef ;
tDataList mList ;
} ;
} // namespace DirectoryServices
#endif /* _DSDataList_h */

View file

@ -0,0 +1,118 @@
/*
*
* @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: DSDataNode.h
Contains: Class definition for Open Directory data node
(wraps tDataNodePtr)
Created By: chris jalbert
Created: 26 July 2000
*/
#ifndef _DSDataNode_h
#define _DSDataNode_h
// Framework Headers
#include <DirectoryService/DirServicesTypes.h>
#include <DirectoryService/DirServicesUtils.h>
// Project Headers
#include "CDirService.h"
namespace DirectoryServices {
//-----------------------------------------------------------------------------
// ¥ DSDataNode - simple wrapper for tDataBufferPtr.
// This should be considered a private class, primarily used by DSNode.
// All methods are inlined for performance reasons (sorry).
// tDataNode's are identical to tDataBuffer's in implementation, however,
// nodes are treated as opaque objects with DS accessor functions. As a
// result, DSDataNode is not a subclass of DSBuffer.
//-----------------------------------------------------------------------------
class DSDataNode
{
public:
/**** Instance methods. ****/
// ctor and dtor.
DSDataNode ( tDirReference inDirRef,
size_t inBufSize,
size_t inBufUsed,
const void *inData ) throw (DSException)
: mDirRef (inDirRef),
mNode (::dsDataNodeAllocateBlock (inDirRef,
(UInt32) inBufSize, (UInt32) inBufUsed,
(void *) inData))
{ if (!mNode) throw_ds_error (eDSAllocationFailed) ; }
DSDataNode ( tDirReference inDirRef,
const char *inString ) throw (DSException)
: mDirRef (inDirRef),
mNode (::dsDataNodeAllocateString (inDirRef, inString))
{ if (!mNode) throw_ds_error (eDSAllocationFailed) ; }
// Used by DSDataList
DSDataNode ( tDirReference inDirRef,
tDataNode *inNode ) throw (DSException)
: mDirRef (inDirRef), mNode (inNode)
{ if (!mNode) throw_ds_error (eDSAllocationFailed) ; }
// Something of a "copy" constructor
DSDataNode ( tDirReference inDirRef,
const tDataNode *inNode ) throw (DSException)
: mDirRef (inDirRef),
mNode (::dsDataNodeAllocateBlock (inDirRef,
inNode->fBufferSize, inNode->fBufferLength,
(void *) inNode->fBufferData))
{ if (!mNode) throw_ds_error (eDSAllocationFailed) ; }
~DSDataNode ( void ) throw ()
{ if (mNode) ::dsDataNodeDeAllocate (mDirRef, mNode) ; }
// Inline accessors.
size_t capacity ( void ) const throw ()
{ return (size_t) ::dsDataNodeGetSize (mNode) ; }
size_t size ( void ) const throw ()
{ return (size_t) ::dsDataNodeGetSize (mNode) ; }
size_t length ( void ) const throw ()
{ return (size_t) ::dsDataNodeGetLength (mNode) ; }
void resize ( size_t inLength ) throw (DSException)
{ tDirStatus nError = ::dsDataNodeSetLength (mNode, inLength) ;
if (nError) throw_ds_error (nError) ; }
// Casting operators.
operator tDataNodePtr() const throw ()
{ return mNode ; }
protected:
/**** Instance data. ****/
tDirReference mDirRef ;
tDataNodePtr mNode ;
} ;
} // namespace DirectoryServices
#endif /* _DSDataNode_h */

View file

@ -0,0 +1,528 @@
/*
*
* @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: QTSSODSAuthModule.cpp
Contains: Implementation of QTSSDSAuthModule, a modified version of the AuthenticateRequestModule
is sample code.
*/
#include "QTSSDSAuthModule.h"
#include "../../defaultPaths.h"
#include "DSAccessChecker.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "OSArrayObjectDeleter.h"
#include "SafeStdLib.h"
#include "QTSSMemoryDeleter.h"
#include "QTSS_Private.h"
#include "OS.h"
//#define SACL 1
#if OSX_SACL
extern "C"
{
#include <membershipPriv.h>
}
#include <membership.h>
#include <errno.h>
#endif
// ATTRIBUTES
// STATIC DATA
const UInt32 kBuffLen = 512;
#define MODPREFIX_ "modDSAuth_"
#define AUTHDEBUG 0
#define debug_printf if (AUTHDEBUG) qtss_printf
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_PrefsObject sServerPrefs = NULL;
static OSMutex* sAuthMutex = NULL;
static Bool16 sDefaultAuthenticationEnabled = true;
static Bool16 sAuthenticationEnabled = true;
static char* sDefaultAccessFileName = "qtaccess";
static char* sAccessFileName = NULL;
static Bool16 sAllowGuestDefaultEnabled = true;
static Bool16 sDefaultGuestEnabled = true;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSDSAuthModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register();
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error RereadPrefs();
static QTSS_Error AuthenticateRTSPRequest(QTSS_RTSPAuth_Params* inParams);
static QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams);
static Bool16 AuthenticateRequest(QTSS_StandardRTSP_Params* inParams, const char* pathBuff, const char* movieRootDir, StrPtrLen* ioRealmName, Bool16* foundUserPtr);
static int check_sacl(const char *inUser);
#define kSACLNotAuthorized 0
#define kSACLAuthorized 1
#define kSACLUnknownUser 2
#define kSACLAnyUser 3
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSDSAuthModule_Main(void* inPrivateArgs)
{
printf("QTSSDSAuthModule_Main\n");
#if OSX_SACL
printf("QTSSDSAuthModule_Main OSX_SACL\n");
#endif
#if OSX_OD_API
printf("QTSSDSAuthModule_Main OSX_OD_API\n");
#endif
return _stublibrary_main(inPrivateArgs, QTSSDSAuthModuleDispatch);
}
QTSS_Error QTSSDSAuthModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register();
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPAuthenticate_Role:
if (sAuthenticationEnabled)
return AuthenticateRTSPRequest(&inParams->rtspAthnParams);
case QTSS_RTSPAuthorize_Role:
if (sAuthenticationEnabled)
return Authorize(&inParams->rtspRequestParams);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
QTSS_Error Register()
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthenticate_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
sServerPrefs = inParams->inPrefs;
sAuthMutex = new OSMutex();
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
char* GetCheckedFileName()
{
char *result = NULL;
static char *badChars = "/'\"";
char theBadCharMessage[] = "' '";
char *theBadChar = NULL;
result = QTSSModuleUtils::GetStringAttribute(sPrefs, MODPREFIX_"dsaccessfilename", sDefaultAccessFileName);
StrPtrLen searchStr(result);
theBadChar = strpbrk(searchStr.Ptr, badChars);
if ( theBadChar!= NULL)
{
theBadCharMessage[1] = theBadChar[0];
QTSSModuleUtils::LogErrorStr(qtssWarningVerbosity,MODPREFIX_"found invalid DS access file name in prefs");
delete[] result;
result = new char[::strlen(sDefaultAccessFileName) + 2];
::strcpy(result, sDefaultAccessFileName);
}
return result;
}
QTSS_Error RereadPrefs()
{
OSMutexLocker locker(sAuthMutex);
QTSSModuleUtils::GetAttribute(sPrefs, MODPREFIX_"enabled", qtssAttrDataTypeBool16,
&sAuthenticationEnabled, &sDefaultAuthenticationEnabled, sizeof(sAuthenticationEnabled));
QTSSModuleUtils::GetAttribute(sServerPrefs,"enable_allow_guest_default", qtssAttrDataTypeBool16,
&sAllowGuestDefaultEnabled,(void *)&sDefaultGuestEnabled, sizeof(sAllowGuestDefaultEnabled));
delete [] sAccessFileName;
sAccessFileName = GetCheckedFileName();
return QTSS_NoErr;
}
Bool16 AuthenticateRequest(QTSS_StandardRTSP_Params* inParams,
const char* pathBuff,
const char* movieRootDir,
StrPtrLen* ioRealmName,
Bool16* foundUserPtr)
{
if (foundUserPtr)
*foundUserPtr = false;
if (ioRealmName) //Set Value to Empty for now use whatever is set by access file or the default
{
ioRealmName->Ptr[0] = '\0';
ioRealmName->Len = 0;
}
QTSS_Error theErr = QTSS_NoErr;
char passwordBuff[kBuffLen];
StrPtrLen passwordStr(passwordBuff, kBuffLen -1);
char nameBuff[kBuffLen];
StrPtrLen nameStr(nameBuff, kBuffLen -1);
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserName,0, (void *) nameStr.Ptr, &nameStr.Len);
if ( (QTSS_NoErr != theErr) || (nameStr.Len >= kBuffLen) )
{
debug_printf("QTSSDSAuthModule:AuthenticateRequest() Username Error - %"_S32BITARG_"\n", theErr);
return false;
}
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserPassword,0, (void *) passwordStr.Ptr, &passwordStr.Len);
if ( (QTSS_NoErr != theErr) || (passwordStr.Len >= kBuffLen) )
{
debug_printf("QTSSDSAuthModule:AuthenticateRequest() Password Error - %"_S32BITARG_"\n", theErr);
return false;
}
nameBuff[nameStr.Len] = '\0';
passwordBuff[passwordStr.Len] = '\0';
//
// Use the name and password to check access
DSAccessChecker accessChecker;
if ( !accessChecker.CheckPassword( nameBuff, passwordBuff) )
{
return false;
}
if (foundUserPtr)
*foundUserPtr = true;
return true;
}
QTSS_Error AuthenticateRTSPRequest(QTSS_RTSPAuth_Params* inParams)
{
OSMutexLocker locker(sAuthMutex);
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_AuthScheme authScheme = qtssAuthNone;
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest start\n");
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) )
{
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest inParams NULL\n");
return QTSS_RequestFailed;
}
// Get the user profile object from the request object
QTSS_UserProfileObject theUserProfile = NULL;
UInt32 len = sizeof(QTSS_UserProfileObject);
QTSS_Error theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqUserProfile, 0, (void*)&theUserProfile, &len);
Assert(len == sizeof(QTSS_UserProfileObject));
if (theErr != QTSS_NoErr)
{
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - username error is %"_S32BITARG_"\n", theErr);
return theErr;
}
char* nameBuff = NULL;
theErr = QTSS_GetValueAsString(theUserProfile, qtssUserName, 0, &nameBuff);
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - username is %s\n", nameBuff);
OSCharArrayDeleter usernameBufDeleter(nameBuff);
if (theErr != QTSS_NoErr)
{
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - theUserProfile nameBuff error is %"_S32BITARG_"\n", theErr);
}
len = sizeof(authScheme);
theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&authScheme, &len);
if (theErr != QTSS_NoErr)
return theErr;
DSAccessChecker accessChecker;
Bool16 allowed = true;
Bool16 foundUser = true;
Bool16 authHandled = true;
if ( authScheme == qtssAuthDigest)
{
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - authScheme = qtssAuthDigest\n");
char* challengeBuff = NULL;
(void) QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqDigestChallenge, 0, &challengeBuff);
OSCharArrayDeleter challengeDeleter(challengeBuff);
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - Server Challenge =%s\n",challengeBuff);
char* responseBuff = NULL;
(void) QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqDigestResponse, 0, &responseBuff);
OSCharArrayDeleter responseDeleter(responseBuff);
char* methodBuff = NULL;
(void) QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqMethodStr, 0, &methodBuff);
OSCharArrayDeleter methodDeleter(methodBuff);
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - Server Method =%s\n",methodBuff);
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - username is %s challenge=%s response=%s method=%s\n", nameBuff, challengeBuff, responseBuff, methodBuff);
if ( false == accessChecker.CheckDigest(nameBuff, challengeBuff, responseBuff, methodBuff) )
{ debug_printf("QTSSDSAuthModule CheckDigest returned false\n");
}
else
{ debug_printf("QTSSDSAuthModule CheckDigest returned true\n");
(void) QTSSModuleUtils::AuthorizeRequest(theRTSPRequest,&allowed,&foundUser,&authHandled);
}
}
if ( authScheme == qtssAuthBasic)
{
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - authScheme = qtssAuthBasic\n");
char passwordBuff[kBuffLen];
StrPtrLen passwordStr(passwordBuff, kBuffLen -1);
char nameBuff[kBuffLen];
StrPtrLen nameStr(nameBuff, kBuffLen -1);
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserName,0, (void *) nameStr.Ptr, &nameStr.Len);
if ( (QTSS_NoErr != theErr) || (nameStr.Len >= kBuffLen) )
{
debug_printf("QTSSDSAuthModule:AuthenticateRequest() Username Error - %"_S32BITARG_"\n", theErr);
return false;
}
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserPassword,0, (void *) passwordStr.Ptr, &passwordStr.Len);
if ( (QTSS_NoErr != theErr) || (passwordStr.Len >= kBuffLen) )
{
debug_printf("QTSSDSAuthModule:AuthenticateRequest() Password Error - %"_S32BITARG_"\n", theErr);
}
nameBuff[nameStr.Len] = '\0';
passwordBuff[passwordStr.Len] = '\0';
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - username is %s\n", nameBuff);
debug_printf("QTSSDSAuthModule:AuthenticateRTSPRequest - password is %s\n", passwordBuff);
if ( !accessChecker.CheckPassword(nameBuff, passwordBuff) )
{ debug_printf("QTSSDSAuthModule CheckPassword returned false\n");
}
else
{
debug_printf("QTSSDSAuthModule CheckPassword returned true\n");
(void) QTSSModuleUtils::AuthorizeRequest(theRTSPRequest,&allowed,&foundUser,&authHandled);
}
}
return QTSS_NoErr;
}
int check_sacl(const char *inUser)
{
#if OSX_SACL
int mbrErr = ENOENT;
int isMember = 0;
uuid_t user_uuid;
uuid_t uu;
mbrErr = mbr_uid_to_uuid(geteuid(), uu);
if (0 == mbrErr)
{
mbrErr = mbr_check_service_membership(uu, "qtss", &isMember);
if (ENOENT == mbrErr) //no acl exists so allow any user.
return kSACLAnyUser;
}
if( (mbrErr = mbr_user_name_to_uuid(inUser, user_uuid)) != 0)
{
return kSACLUnknownUser;
}
if((mbrErr = mbr_check_service_membership(user_uuid, "qtss", &isMember)) != 0)
{
if(mbrErr == ENOENT){ // no ACL exists
return kSACLAuthorized;
} else {
return kSACLNotAuthorized;
}
}
if(isMember == kSACLAuthorized)
{
return kSACLAuthorized;
}
return kSACLNotAuthorized;
#else
return kSACLAuthorized;
#endif
}
QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams)
{
OSMutexLocker locker(sAuthMutex);
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) )
{
debug_printf("QTSSDSAuthModule - Authorize inParams: Error");
return QTSS_RequestFailed;
}
//get the local file path
char* pathBuffStr = NULL;
QTSS_Error theErr = QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqLocalPath, 0, &pathBuffStr);
QTSSCharArrayDeleter pathBuffDeleter(pathBuffStr);
if (theErr != QTSS_NoErr)
{
debug_printf("QTSSDSAuthModule - Authorize [QTSS_GetValueAsString]: Error %"_S32BITARG_"", theErr);
return QTSS_RequestFailed;
}
//get the root movie directory
char* movieRootDirStr = NULL;
theErr = QTSS_GetValueAsString(theRTSPRequest,qtssRTSPReqRootDir, 0, &movieRootDirStr);
OSCharArrayDeleter movieRootDeleter(movieRootDirStr);
if (theErr != QTSS_NoErr)
{
debug_printf("QTSSDSAuthModule - Authorize[QTSS_GetValueAsString]: Error %"_S32BITARG_"", theErr);
return false;
}
//check if this user is allowed to see this movie
DSAccessFile accessFile;
Bool16 allowNoAccessFiles = sAllowGuestDefaultEnabled; //no access files allowed means allowing guest access (unknown users)
Bool16 allowAnyUser = false;
QTSS_ActionFlags noAction = ~qtssActionFlagsRead; //only handle read
QTSS_ActionFlags authorizeAction = QTSSModuleUtils::GetRequestActions(theRTSPRequest);
Bool16 authorized =false;
Bool16 saclUser = false;
char *name = NULL;
(void) QTSS_GetValueAsString (theRTSPRequest,qtssRTSPReqUserName,0, &name);
OSCharArrayDeleter nameDeleter(name);
if (sAllowGuestDefaultEnabled) // if guest access is on, sacls are ignored.
{
authorized = true;
}
else
{ int result = check_sacl(name);
switch (result)
{
case kSACLAuthorized: authorized = true;
break;
case kSACLUnknownUser: authorized = false; //set this to true to allow file based and other non-directory service users access, when SACLs are enabled in the system for QTSS.
break;
case kSACLNotAuthorized: authorized = false;
break;
case kSACLAnyUser: authorized = true;
break;
default: authorized = false;
}
debug_printf("QTSSDSAuthModule:Authorize sacl_check result=%d for %s authorized = %d\n",result, name, authorized);
if (false == authorized)
saclUser = true;
}
Bool16 foundUser = false;
Bool16 passwordOK = false; //::AuthenticateRequest(inParams, pathBuffStr, movieRootDirStr, &sRealmNameStr, &foundUser);
if (authorized) //have to be authorized by sacls or guest first before qtaccess file checks can allow or disallow.
{
theErr = accessFile.AuthorizeRequest(inParams,allowNoAccessFiles, noAction, authorizeAction,&authorized, &allowAnyUser);
debug_printf("QTSSDSAuthModule:Authorize AuthorizeRequest() returned authorized=%d allowAnyUser=%d\n", authorized, allowAnyUser);
}
debug_printf("QTSSDSAuthModule:Authorize AuthenticateRequest() returned passwordOK=%d foundUser=%d authorized=%d allowAnyUser=%d\n", passwordOK ,foundUser, authorized,allowAnyUser);
Bool16 allowRequest = authorized;
Bool16 authHandled = true;
if(!(authorizeAction & qtssActionFlagsRead)) //not for us
{
debug_printf("QTSSDSAuthModule:Authorize(qtssActionFlagsRead) not handled do nothing.\n");
}
else if (allowRequest)
{
debug_printf("QTSSDSAuthModule:Authorize() succeeded.\n");
theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &foundUser, &authHandled);
debug_printf("QTSSDSAuthModule:Authorize allowRequest=%d founduser=%d authHandled=%d\n", allowRequest, foundUser, authHandled);
}
else //request denied
{
debug_printf("QTSSDSAuthModule:Authorize() failed.\n");
foundUser = saclUser;
authHandled = true;
theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &foundUser, &authHandled);
debug_printf("QTSSDSAuthModule:Authorize allowRequest=%d founduser=%d authHandled=%d saclUser=%d\n", allowRequest, foundUser, authHandled,saclUser);
}
return theErr;
}

View file

@ -0,0 +1,46 @@
/*
*
* @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: QTSSODAuthModule.h
Contains: This is a modified version of the QTSSAccessModule also released with
QTSS 2.0. It has been modified to shrink the linespacing so that
the code can fit on slides. Also, this module issues redirects to
an error movie.
*/
#ifndef _QTSSDSAUTHMODULE_H__
#define _QTSSDSAUTHMODULE_H__
#include "QTSS.h"
extern "C"
{
QTSS_Error QTSSDSAuthModule_Main(void* inPrivateArgs);
}
#endif

View file

@ -0,0 +1,207 @@
/*
*
* @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: QTSSDemoAuthorizationModule.cpp
Contains: Implementation of QTSSDemoAuthorizationModule, which demonstrates an authorization method using
client IP addresses for access control. Connections from IP addresses not included in the "IPAccessList"
preference will be immediately sent an RTSP "401 Unauthorized" error.
Example of preferences allowing only connections from loopback (or any address in the 127.0.0.X network):
<MODULE NAME="QTSSDemoAuthorizationModule" >
<PREF NAME="enabled" TYPE="Bool16" >true</PREF>
<PREF NAME="IPAccessList" >127.0.0.*</PREF>
</MODULE>
*/
#include "QTSSDemoAuthorizationModule.h"
#include "../../defaultPaths.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "OSArrayObjectDeleter.h"
#include "SafeStdLib.h"
#include "QTSSMemoryDeleter.h"
#include "StringParser.h"
#include "OSMemory.h"
// STATIC DATA
static QTSS_ServerObject sServer = NULL;
static QTSS_ModuleObject sModule = NULL;
static QTSS_ModulePrefsObject sModulePrefs = NULL;
const UInt32 kBuffLen = 512;
// Module description and version
static char* sDescription = "Demonstrates an authorization method using client IP addresses for access control";
static UInt32 sVersion = 0x00010000;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSDemoAuthorizationModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error RereadPrefs();
static QTSS_Error Authenticate();
static QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams);
static Bool16 AcceptSession(QTSS_RTSPSessionObject inRTSPSession);
// Module preferences and their defaults
static Bool16 sEnabled = false;
static Bool16 kDefaultEnabled = false;
static char* sIPAccessList = NULL;
static QTSS_AttributeID sIPAccessListID = qtssIllegalAttrID;
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSDemoAuthorizationModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSDemoAuthorizationModuleDispatch);
}
QTSS_Error QTSSDemoAuthorizationModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPAuthenticate_Role:
return Authenticate();
case QTSS_RTSPAuthorize_Role:
return Authorize(&inParams->rtspRequestParams);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthenticate_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
// Tell the server our name
static char* sModuleName = "QTSSDemoAuthorizationModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
// Get the server object
sServer = inParams->inServer;
// Get our prefs object
sModule = inParams->inModule;
sModulePrefs = QTSSModuleUtils::GetModulePrefsObject(sModule);
// Set our version and description
(void)QTSS_SetValue(sModule, qtssModDesc, 0, sDescription, ::strlen(sDescription));
(void)QTSS_SetValue(sModule, qtssModVersion, 0, &sVersion, sizeof(sVersion));
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
QTSSModuleUtils::GetAttribute(sModulePrefs, "enabled", qtssAttrDataTypeBool16,
&sEnabled, &kDefaultEnabled, sizeof(sEnabled));
delete [] sIPAccessList;
sIPAccessList = QTSSModuleUtils::GetStringAttribute(sModulePrefs, "IPAccessList", "127.0.0.*");
sIPAccessListID = QTSSModuleUtils::GetAttrID(sModulePrefs, "IPAccessList");
return QTSS_NoErr;
}
// This is not necessary, but could be used to perform Authentication Role actions
QTSS_Error Authenticate()
{
return QTSS_NoErr;
}
QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams)
{
QTSS_Error theErr = QTSS_NoErr;
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_RTSPSessionObject theRTSPSession = inParams->inRTSPSession;
QTSS_ActionFlags noAction = ~qtssActionFlagsRead; //only handle read
QTSS_ActionFlags authorizeAction = QTSSModuleUtils::GetRequestActions(theRTSPRequest);
Bool16 allowRequest = false;
if ((authorizeAction & noAction) != 0)
return QTSS_NoErr;
allowRequest = AcceptSession(theRTSPSession);
if( (theErr != QTSS_NoErr) || (allowRequest == false) ) //handle it
{
//Access denied
Bool16 allowed = false;
(void) QTSSModuleUtils::SendErrorResponse(theRTSPRequest, qtssClientUnAuthorized, 0);
(void) QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowed, &allowed, &allowed);
} else {
//Access granted
Bool16 allowed = true;
(void) QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowed, &allowed, &allowed);
}
return QTSS_NoErr;
}
Bool16 AcceptSession(QTSS_RTSPSessionObject inRTSPSession)
{
char remoteAddress[20] = {0};
StrPtrLen theClientIPAddressStr(remoteAddress,sizeof(remoteAddress));
QTSS_Error err = QTSS_GetValue(inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void*)theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len);
if (err != QTSS_NoErr) return false;
if (QTSSModuleUtils::AddressInList(sModulePrefs, sIPAccessListID, &theClientIPAddressStr))
return true;
return false;
}

View file

@ -0,0 +1,47 @@
/*
*
* @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: QTSSDemoAuthorizationModule.h
Contains: Header file for QTSSDemoAuthorizationModule.
*/
#ifndef __QTSS_DEMOAUTHORIZATION_MODULE_H__
#define __QTSS_DEMOAUTHORIZATION_MODULE_H__
#include "QTSS.h"
extern "C"
{
QTSS_Error QTSSDemoAuthorizationModule_Main(void* inPrivateArgs);
}
#endif // __QTSS_DEMOAUTHORIZATION_MODULE_H__

View file

@ -0,0 +1,227 @@
/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 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: QTSSDemoRedirectModule.cpp
Contains: Implementation of QTSSDemoRedirectModule
*/
#include "QTSSDemoRedirectModule.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "SafeStdLib.h"
#include "QTSSMemoryDeleter.h"
// STATIC DATA
static QTSS_ServerObject sServer = NULL;
static QTSS_ModuleObject sModule = NULL;
static QTSS_ModulePrefsObject sModulePrefs = NULL;
static StrPtrLen sRedirect("RTSP/1.0 302 Found\r\nServer: QTSS/6.0\r\nCSeq: 1\r\nConnection: Close");
static StrPtrLen sLocation("\r\nLocation: ");
static StrPtrLen sRedirectEnd("\r\n\r\n");
static const UInt32 kBuffSize = 512;
// Module description and version
static char* sDescription = "Demonstrates a way to redirect file requests based on client bandwidth headers";
static UInt32 sVersion = 0x00010000;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSDemoRedirectModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error RereadPrefs();
static QTSS_Error Authenticate(QTSS_StandardRTSP_Params* inParams);
// Module preferences and their defaults
static Bool16 sEnabled = false;
static Bool16 kDefaultEnabled = true;
static char* sRedirectUrl = NULL;
static char* sRedirectUrlDefault = "sample";
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSDemoRedirectModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSDemoRedirectModuleDispatch);
}
QTSS_Error QTSSDemoRedirectModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPAuthenticate_Role:
return Authenticate(&inParams->rtspRequestParams);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthenticate_Role);
// Tell the server our name
static char* sModuleName = "QTSSDemoRedirectModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
// Get the server object
sServer = inParams->inServer;
// Get our prefs object
sModule = inParams->inModule;
sModulePrefs = QTSSModuleUtils::GetModulePrefsObject(sModule);
// Set our version and description
(void)QTSS_SetValue(sModule, qtssModDesc, 0, sDescription, ::strlen(sDescription));
(void)QTSS_SetValue(sModule, qtssModVersion, 0, &sVersion, sizeof(sVersion));
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
QTSSModuleUtils::GetAttribute(sModulePrefs, "enabled", qtssAttrDataTypeBool16,
&sEnabled, &kDefaultEnabled, sizeof(sEnabled));
sRedirectUrl = QTSSModuleUtils::GetStringAttribute(sModulePrefs, "url_to_redirect", sRedirectUrlDefault);
return QTSS_NoErr;
}
QTSS_Error Authenticate(QTSS_StandardRTSP_Params* inParams)
{
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) ) {
return QTSS_RequestFailed;
}
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_Error theErr = QTSS_NoErr;
char *movieUrlTrunc = NULL;
theErr = QTSS_GetValueAsString(theRTSPRequest,qtssRTSPReqTruncAbsoluteURL, 0, &movieUrlTrunc);
QTSSCharArrayDeleter movieUrlTruncDeleter(movieUrlTrunc);
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
char *movieUrlFilename = NULL;
theErr = QTSS_GetValueAsString(theRTSPRequest,qtssRTSPReqFileName, 0, &movieUrlFilename);
QTSSCharArrayDeleter movieUrlFilenameDeleter(movieUrlFilename);
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
// Return unless the requested filename matches the redirect URL
if (::strncmp(movieUrlFilename, sRedirectUrl, (::strlen(movieUrlFilename) <= ::strlen(sRedirectUrl))
? ::strlen(sRedirectUrl) : ::strlen(movieUrlFilename)))
return QTSS_NoErr;
// Get the client's bandwidth header
UInt32 bandwidthBits = 0;
UInt32 len = sizeof(bandwidthBits);
(void)QTSS_GetValue(theRTSPRequest, qtssRTSPReqBandwidthBits, 0, (void *)&bandwidthBits, &len);
// Prepare a filename extension to use for redirection.
// These numbers match the "Streaming Speed" options in the QuickTime Player 7 settings.
// The extension will be appended to the requested URL.
char theNewFilename[kBuffSize];
::memset(theNewFilename, 0, kBuffSize);
if (bandwidthBits <= 0) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_50kbit.3gp", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 28000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_50kbit.3gp", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 56000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_50kbit.3gp", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 112000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_100kbit.mov", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 256000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_100kbit.mov", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 384000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_300kbit.mov", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 512000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_300kbit.mov", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 768000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_300kbit.mov", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 1000000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_h264_1mbit.mp4", movieUrlTrunc, movieUrlFilename);
} else if (bandwidthBits <= 1500000) {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_h264_1mbit.mp4", movieUrlTrunc, movieUrlFilename);
} else {
qtss_snprintf(theNewFilename, sizeof(theNewFilename), "%s/%s_h264_1mbit.mp4", movieUrlTrunc, movieUrlFilename);
}
// In order to send the redirect, we need to get a QTSS_StreamRef
// so we can send data to the client. Get the QTSS_StreamRef out of the request.
QTSS_StreamRef* theStreamRef = NULL;
UInt32 strRefLen = 0;
theErr = QTSS_GetValuePtr(theRTSPRequest, qtssRTSPReqStreamRef, 0,
(void**)&theStreamRef, &strRefLen);
if (( QTSS_NoErr != theErr ) || ( sizeof(QTSS_StreamRef) != strRefLen) ) {
return QTSS_RequestFailed;
}
// Send the redirect
UInt32 theLenWritten = 0;
(void)QTSS_Write(*theStreamRef, sRedirect.Ptr, sRedirect.Len, &theLenWritten, 0);
(void)QTSS_Write(*theStreamRef, sLocation.Ptr, sLocation.Len, &theLenWritten, 0);
(void)QTSS_Write(*theStreamRef, theNewFilename, ::strlen(theNewFilename), &theLenWritten, 0);
(void)QTSS_Write(*theStreamRef, sRedirectEnd.Ptr, sRedirectEnd.Len, &theLenWritten, 0);
(void)QTSS_Flush(*theStreamRef);
return QTSS_NoErr;
}

View file

@ -0,0 +1,47 @@
/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 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: QTSSDemoRedirectModule.h
Contains: Header file for QTSSDemoRedirectModule.
*/
#ifndef __QTSS_DEMOREDIRECT_MODULE_H__
#define __QTSS_DEMOREDIRECT_MODULE_H__
#include "QTSS.h"
extern "C"
{
QTSS_Error QTSSDemoRedirectModule_Main(void* inPrivateArgs);
}
#endif // __QTSS_DEMOREDIRECT_MODULE_H__

View file

@ -0,0 +1,417 @@
/*
*
* @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: QTSSDemoSMILModule.cpp
Contains:
*/
/*
<smil>
<head>
<layout>
<root-layout width="240" height="160" background-color="black" />
<region id="region_1" background-color="black" left="0" top="0" width="240" height="160" />
</layout>
</head>
<body>
<seq>
<video src="Blue%20Indigo/George%D5s%20Blues" alt="Cool Jazz" region="region_1" />
<video src="rtsp://17.333.33.333/catwalk.mov"" alt="Streaming Jazz" region="region_1" begin="2s" />
</seq>
</body>
</smil>
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include <dirent.h>
#include <string.h>
#include <time.h>
#include "QTSSDemoSMILModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "OSFileSource.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "ev.h"
#include "QTFile.h"
#include "QTTrack.h"
#include "QTHintTrack.h"
#include "QTSSModuleUtils.h"
#include "OSMutex.h"
#define HTTP_FILE_ASYNC 1
#define HTTP_FILE_DEBUGGING 1
// STATIC DATA
// For processing the requests
static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: QTSS/2.0\r\nContent-Type: video/quicktime\r\n\r\n";
static QTSS_PrefsObject sPrefs = NULL;
static StrPtrLen sPathSeparator("/");
static StrPtrLen sRTSPUrlPrefix("rtsp://");
static StrPtrLen sRefMovieBufPrefix("rtsptext\r");
static StrPtrLen sRespHeaderPrefix("HTTP/1.0 200 OK\r\nServer: QTSS/2.0\r\nConnection: Close");
static StrPtrLen sContentLengthHeaderTag("\r\nContent-Length: ");
static StrPtrLen sContentTypeHeaderTag("\r\nContent-Type: ");
static StrPtrLen sConnectionKeepAliveTag("Keep-Alive");
static StrPtrLen sQuickTimeMimeType("video/quicktime");
static StrPtrLen sUnknownMimeType("application/unknown");
static StrPtrLen sGifMimeType("image/gif");
static StrPtrLen sSdpMimeType("application/sdp");
static StrPtrLen sSmilMimeType("application/smil");
static StrPtrLen sQTSuffix("qt");
static StrPtrLen sMovSuffix("mov");
static StrPtrLen sGifSuffix("gif");
static StrPtrLen sSdpSuffix("sdp");
static StrPtrLen sSmiSuffix("smi");
static StrPtrLen sSmilSuffix("smil");
// FUNCTIONS
static QTSS_Error QTSSDemoSMILDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error FilterRequest(QTSS_Filter_Params* inParams);
// For processing the requests
//Demo stuff
struct HitCount {
char url[512];
int hitcount;
};
static HitCount gHitcountArray[100] = {};
//protos
void CountHit(char* url);
void GenerateHotHitSMIL(char* buffer);
int HitCountCompare(const void * hitCount1, const void *hitCount2);
QTSS_Error CountRequest( QTSS_RTSPRequestObject inRTSPRequest, QTSS_ClientSessionObject inClientSession,
QTSS_RTSPSessionObject inRTSPSession, QTSS_CliSesClosingReason *inCloseReasonPtr );
void InitHitCountFromFile();
//funcs
void InitHitCountFromFile()
{
FILE* hitfile = fopen("hitcount.txt", "r");
if (hitfile == NULL) return;
int c = 0;
int i = 0;
while ( (c = fscanf(hitfile, "%s %d", gHitcountArray[i].url, &gHitcountArray[i].hitcount)) == 2 )
{
qtss_printf("%s %d\n", gHitcountArray[i].url, gHitcountArray[i].hitcount);
i++;
}
fclose(hitfile);
}
void WriteHitCountToFile()
{
FILE* hitfile = fopen("hitcount.txt", "w");
int i=0;
for (i=0;gHitcountArray[i].url[0] && i<512; i++)
{
qtss_fprintf(hitfile, "%s %d\n", gHitcountArray[i].url, gHitcountArray[i].hitcount);
}
fclose(hitfile);
}
void CountHit(char* url)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("Counting Hit for \"%s\"\n", url);
#endif
if ( url == NULL )
return;
int i=0;
for (i=0;gHitcountArray[i].url[0] && i<512; i++)
{
if ( strcmp(url, gHitcountArray[i].url ) == 0)
{
gHitcountArray[i].hitcount++;
qsort(&gHitcountArray, i+1, sizeof(HitCount), &HitCountCompare);
return;
}
}
::strcpy(gHitcountArray[i].url, url);
gHitcountArray[i].hitcount++;
qsort(&gHitcountArray, i+1, sizeof(HitCount), &HitCountCompare);
}
int HitCountCompare(const void * hitCount1, const void *hitCount2)
{
return ((const HitCount*)hitCount2)->hitcount - ((const HitCount*)hitCount1)->hitcount;
}
QTSS_Error ClientSessionClosing(QTSS_ClientSessionClosing_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("ClientSessionClosing\n");
#endif
return CountRequest(NULL, inParams->inClientSession, NULL, &inParams->inReason);
}
QTSS_Error CountRequest( QTSS_RTSPRequestObject inRTSPRequest, QTSS_ClientSessionObject inClientSession,
QTSS_RTSPSessionObject inRTSPSession, QTSS_CliSesClosingReason *inCloseReasonPtr )
{
char urlBuf[256] = { 0 };
StrPtrLen url(urlBuf, 256 -1);
(void)QTSS_GetValue(inClientSession, qtssCliSesFullURL, 0, url.Ptr, &url.Len);
CountHit( url.Ptr );
WriteHitCountToFile();
return QTSS_NoErr;
}
void GenerateHotHitSMIL(char* buffer)
{
char smilTemplate[8192] = {};
char* templateCursor = smilTemplate;
char* bufferCursor = buffer;
FILE* smilTemplateFile = fopen("template.smil", "r");
if (smilTemplateFile != NULL)
{
int len = fread(smilTemplate, sizeof(char), sizeof(smilTemplate), smilTemplateFile);
smilTemplate[len] = '\0';
fclose(smilTemplateFile);
}
else
{
strcpy(smilTemplate, "<smil>\n"
" <head>\n"
" <layout>\n"
" </layout>\n"
" </head>\n"
" <body>\n"
" <seq>\n"
" <video src=\"%s\" />\n"
" </seq>\n"
" </body>\n"
"</smil>\n");
}
int hitNum = 0;
char* p = NULL;
while ( (p = strstr(templateCursor, "%s")) != 0 )
{
char saveCh = p[2];
p[2] = '\0';
int len = qtss_sprintf(bufferCursor, templateCursor, gHitcountArray[hitNum++].url);
p[2] = saveCh;
bufferCursor += len;
templateCursor = &p[2];
}
strcat(bufferCursor, templateCursor);
#if HTTP_FILE_DEBUGGING
qtss_printf("smil generated:\n%s\n", buffer);
#endif
}
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSDemoSMILModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSDemoSMILDispatch);
}
QTSS_Error QTSSDemoSMILDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("QTSSDemoSMILDispatch\n");
#endif
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RTSPFilter_Role:
return FilterRequest(&inParams->rtspFilterParams);
case QTSS_ClientSessionClosing_Role:
return ClientSessionClosing(&inParams->clientSessionClosingParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("Register\n");
#endif
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
// Tell the server our name!
static char* sModuleName = "QTSSDemoSMILModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("Initialize\n");
#endif
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
// Get prefs object
sPrefs = inParams->inPrefs;
InitHitCountFromFile();
return QTSS_NoErr;
}
QTSS_Error FilterRequest(QTSS_Filter_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("FilterRequest\n");
#endif
static Bool16 sFalse = false;
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
// Initial state.
StrPtrLen theFullRequest;
StrPtrLen reqMethod;
StrPtrLen reqStr;
StrPtrLen httpVersion;
(void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len);
StringParser fullRequest(&theFullRequest);
// Parsing the HTTP request
fullRequest.ConsumeWord(&reqMethod);
if ( !(reqMethod.Equal(StrPtrLen("GET")) || reqMethod.Equal(StrPtrLen("HEAD"))) )
// It's not a "Get" or a "Head" request
return QTSS_NoErr;
fullRequest.ConsumeWhitespace();
if ( !fullRequest.Expect('/') )
// Improperly formed request
return QTSS_NoErr;
fullRequest.ConsumeUntil(&reqStr, StringParser::sEOLWhitespaceMask);
if( reqStr.Len == 0 )
//if a file or directory name is not given, return
return QTSS_NoErr;
if ( !reqStr.Equal(StrPtrLen("Popular.smil")) )
return QTSS_NoErr;
// If it's a "Head" request send the Head response header back and just return
if ( reqMethod.Equal(StrPtrLen("HEAD")) )
{
QTSS_Write(theRequest, sResponseHeader, ::strlen(sResponseHeader), NULL, 0);
return QTSS_NoErr;
}
// Create a buffer to store data.
char theFileBuffer[8192];
char contentLength[256];
// Before sending any response, set keep alive to off for this connection
// Regardless of what the client sends, the server always closes the connection after sending the file
(void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
#if HTTP_FILE_DEBUGGING
qtss_printf("Creating a smil file\n");
#endif
// Create a ref movie buffer for the single file. It is of the form:
// rtsptext\r
// rtsp://servername/filepath
char smilFileBuf[8192] = {0};
GenerateHotHitSMIL(smilFileBuf);
qtss_sprintf(contentLength, "%"_U32BITARG_"", (UInt32) ::strlen(smilFileBuf));
// Allocate memory for theFileBuffer
// Write the HTTP header prefix into the buffer
::strcpy(theFileBuffer, sRespHeaderPrefix.Ptr);
::strcat(theFileBuffer, sContentLengthHeaderTag.Ptr);
// Write the remaining part of the HTTP header into the file buffer
::strcat(theFileBuffer, contentLength);
::strcat(theFileBuffer, sContentTypeHeaderTag.Ptr);
::strcat(theFileBuffer, sSmilMimeType.Ptr);
::strcat(theFileBuffer, "\r\n\r\n");
// Write the smil file created above to the file buffer
::strcat(theFileBuffer, smilFileBuf);
// Write the contents of the file buffer to the request stream and return
QTSS_Write(theRequest, theFileBuffer, strlen(theFileBuffer), NULL, 0);
#if HTTP_FILE_DEBUGGING
qtss_printf("Wrote the smil file to the request stream. Successful!\n");
#endif
return QTSS_NoErr;
}

View file

@ -0,0 +1,41 @@
/*
*
* @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: QTSSDemoSMILModule.h
Contains:
*/
#ifndef __QTSSDEMOSMILMODULE_H__
#define __QTSSDEMOSMILMODULE_H__
#include "QTSS.h"
extern "C"
{
QTSS_Error QTSSDemoSMILModule_Main(void* inPrivateArgs);
}
#endif // __QTSSDEMOSMILMODULE_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
/*
*
* @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: QTSSFileModule.h
Contains: Content source module that uses the QTFileLib to serve Hinted QuickTime
files to clients.
*/
#ifndef _RTPFILEMODULE_H_
#define _RTPFILEMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSFileModule_Main(void* inPrivateArgs);
}
#endif //_RTPFILEMODULE_H_

View file

@ -0,0 +1,738 @@
/*
*
* @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: QTSSFilePrivsModule.cpp
Contains: Implementation of QTSSFilePrivsModule.
*/
#include "QTSSFilePrivsModule.h"
#include "OSArrayObjectDeleter.h"
#include "QTSS_Private.h"
#include "StrPtrLen.h"
#include "OSMemory.h"
#include "MyAssert.h"
#include "StringFormatter.h"
#include "StrPtrLen.h"
#include "StringParser.h"
#include "QTSSModuleUtils.h"
#include "base64.h"
#include "OS.h"
#ifndef __MW_
#include <sys/errno.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif
// STATIC DATA
static StrPtrLen sDefaultRealm("WWW Streaming Server"); // testing only
static StrPtrLen sSDPSuffix(".sdp");
static char* sRootUserPtr = "root";
const UInt32 kMaxPathLen = 512;
static OSMutex* sUserMutex = NULL;
// FUNCTION PROTOTYPES
QTSS_Error QTSSFilePrivsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
QTSS_Error Register(QTSS_Register_Params* inParams);
QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
QTSS_Error Shutdown();
QTSS_Error RereadPrefs();
QTSS_Error AuthenticateRTSPRequest(QTSS_StandardRTSP_Params* inParams);
Bool16 QTSSAuthorize(QTSS_StandardRTSP_Params* inParams, const char* pathBuff);
Bool16 CheckWorldAccess(const char* pathBuff);
Bool16 CheckPassword(QTSS_StandardRTSP_Params* inParams);
Bool16 FileExists(char* pathBuff);
UInt32 GetPathParentDestructive(const StrPtrLen *thePathPtr, StrPtrLen *resultPathPtr, UInt32 maxLen);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSFilePrivsModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSFilePrivsModuleDispatch);
}
QTSS_Error QTSSFilePrivsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
break;
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
break;
case QTSS_RereadPrefs_Role:
return RereadPrefs();
break;
case QTSS_RTSPAuthorize_Role:
return AuthenticateRTSPRequest(&inParams->rtspRequestParams);
break;
case QTSS_Shutdown_Role:
return Shutdown();
break;
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
// Tell the server our name!
static char* sModuleName = "QTSSFilePrivsModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sUserMutex = NEW OSMutex();
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
return QTSS_NoErr;
}
UInt32 GetPathParentDestructive(const StrPtrLen *thePathPtr, StrPtrLen *resultPathPtr, UInt32 maxLen)
{
if (resultPathPtr && thePathPtr)
{
StrPtrLen thePath = *thePathPtr;
while ( (thePath.Len > 0) && (thePath.Ptr[thePath.Len -1] == '/') )
{
thePath.Len--;
}
while ( (thePath.Len > 0) && (thePath.Ptr[thePath.Len -1] != '/') )
{
thePath.Len--;
}
if (thePath.Len < maxLen)
{
memcpy (resultPathPtr->Ptr,thePath.Ptr, thePath.Len);
resultPathPtr->Len = thePath.Len;
resultPathPtr->Ptr[thePath.Len] = 0;
//qtss_printf("new dir =%s \n",resultPathPtr->Ptr);
}
}
return resultPathPtr->Len;
}
Bool16 IsUserMember(uid_t userID, gid_t groupID)
{
struct passwd* user = getpwuid(userID);
struct group* group = getgrgid(groupID);
if ((user == NULL) || (group == NULL))
return false;
if (user->pw_gid == groupID) return true;
int i = 0;
while (group->gr_mem[i] != NULL)
{
if (!strcmp(user->pw_name, group->gr_mem[i]))
return true;
i++;
}
return false;
}
Bool16 CheckFileAccess(struct passwd *passwdStructPtr, StrPtrLen */*nameStrPtr*/, char* pathBuff)
{
Bool16 result = false;
/*
if the user is the owner then
if owner access then succeed
else fail
if user is in group then
if group access then succeed
else fail
if guest is on then
succed
else fail
*/
do // once only check for the current entity
{
struct stat statData;
int statResult = stat(pathBuff,&statData);
if (0 != statResult)
{
//qtss_printf("no access to file =%s\n",pathBuff);
result = true; // let access error be handled by server
break;
}
if ( statData.st_uid == (UInt16) passwdStructPtr->pw_uid) // check if owner
{
if ( (statData.st_mode & 0400 ) != 0 ) // has owner read access
{ //qtss_printf("owner access to file =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("no owner access to file =%s\n",pathBuff);
result = false;
break;
}
}
if (statData.st_gid == (UInt16) passwdStructPtr->pw_gid) // check if user's default group owns
{
if ( (statData.st_mode & 0040) != 0 ) // has group read access
{ //qtss_printf("user default group has access to file =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user default group has no access to file =%s\n",pathBuff);
result = false;
break;
}
}
if (IsUserMember(passwdStructPtr->pw_uid, statData.st_gid)) // check if user in group
{
if ( (statData.st_mode & 0040) != 0 ) // has group read access
{ //qtss_printf("user member group has access to file =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user member group has no access to file =%s\n",pathBuff);
result = false;
break;
}
}
if ( (statData.st_mode & 0004) != 0 ) // has world read access
{
//qtss_printf("world has access to file =%s\n",pathBuff);
result = true;
break;
}
//qtss_printf("world has no read access to file =%s\n",pathBuff);
result = false;
} while (false);
if (!result)
{
//qtss_printf("CheckFileAccess failed for %s on file %s\n",nameStrPtr->Ptr, pathBuff);
}
else
{
//qtss_printf("success on file %s for %s\n",pathBuff, nameStrPtr->Ptr);
}
return result;
}
Bool16 CheckDirAccess(struct passwd *passwdStructPtr, StrPtrLen */*nameStrPtr*/, char* pathBuff)
{
Bool16 result = true;
char searchBuffer[kMaxPathLen] = {};
StrPtrLen searchPath(searchBuffer, kMaxPathLen -1);
StrPtrLen thePath(pathBuff);
/*
for each directory in path until fail
if the user is the owner then
if owner access then succeed-continue
else fail - stop
if user is in group then
if group access then succeed-continue
else fail - stop
if guest access then
succeed - continue
else fail - stop
*/
if (thePath.Len <= searchPath.Len)
{
memcpy(searchBuffer,thePath.Ptr, thePath.Len);
while ( (true == result) && (0 != GetPathParentDestructive(&searchPath, &searchPath, kMaxPathLen)) ) // loop until fail or have checked full directory path and file
{
result = false;
do // once only check for the current entity
{
struct stat statData;
int statResult = stat(searchBuffer,&statData);
if (0 != statResult)
{
//qtss_printf("no access to path =%s\n",searchBuffer);
result = true; // let the error be handled in the server
break; // allow
}
if ( statData.st_uid == (UInt16) passwdStructPtr->pw_uid) // check if owner
{
if ( (statData.st_mode & 0100 ) != 0 ) // has owner search access
{ //qtss_printf("owner search access to directory =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("no owner search access to directory =%s\n",pathBuff);
result = false;
break;
}
}
if (statData.st_gid == (UInt16) passwdStructPtr->pw_gid) // check if user's default group owns
{
if ( (statData.st_mode & 0010) != 0 ) // has group search access
{ //qtss_printf("user default group has search access to directory =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user default group has no search access to directory =%s\n",pathBuff);
result = false;
break;
}
}
if (IsUserMember(passwdStructPtr->pw_uid, statData.st_gid)) // check if user in group
{
if ( (statData.st_mode & 0010) != 0 ) // has group search access
{ //qtss_printf("user member group has search access to directory =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user member group has no search access to directory =%s\n",pathBuff);
result = false;
break;
}
}
if ( (statData.st_mode & 0001) != 0 ) // has world search access
{
//qtss_printf("world has search access to directory =%s\n",pathBuff);
result = true;
break;
}
//qtss_printf("world has no search access to directory =%s\n",pathBuff);
result = false;
} while (false);
}
}
if (!result)
{
//qtss_printf("CheckDirAccess failed for %s on file %s\n",nameStrPtr->Ptr, searchBuffer);
}
else
{
//qtss_printf("success on file %s for %s\n",searchBuffer, nameStrPtr->Ptr);
}
return result;
}
Bool16 FileExists(char* pathBuff)
{
struct stat statData;
Bool16 result = true;
if (0 != stat(pathBuff,&statData) ) // something wrong
{
if ( OSThread::GetErrno() == ENOENT ) // doesn't exist
result = false;
}
return result;
}
Bool16 CheckWorldFileAccess(char* pathBuff)
{
Bool16 result = false;
struct stat statData;
do // once only check for the current entity
{
//qtss_printf("stat on %s \n",pathBuff);
if (0 != stat(pathBuff,&statData))
{
//qtss_printf("no access to path =%s\n",pathBuff);
result = true; // let the server deal with this one
break;
}
//qtss_printf("statData.st_mode = %x \n",statData.st_mode);
if (0 == (statData.st_mode & 0004) ) // world read access
{
//qtss_printf("no world access to path =%s\n",pathBuff);
break;
}
result = true;
} while (false);
return result;
}
Bool16 CheckWorldDirAccess(char* pathBuff)
{
Bool16 result = true;
struct stat statData;
char searchBuffer[kMaxPathLen] = {};
StrPtrLen searchPath(searchBuffer, kMaxPathLen -1);
StrPtrLen thePath(pathBuff);
if (thePath.Len <= searchPath.Len)
{
memcpy(searchBuffer,thePath.Ptr, thePath.Len);
while ( (true == result) && (0 != GetPathParentDestructive(&searchPath, &searchPath, kMaxPathLen)) ) // loop until fail or have checked full directory path and file
{
result = false;
do // once only check for the current entity
{
//qtss_printf("stat on %s \n",searchBuffer);
if (0 != stat(searchBuffer,&statData))
{
//qtss_printf("no world access to path =%s\n",searchBuffer);
result = true; // let the server deal with this one
break;
}
//qtss_printf("statData.st_mode = %x \n",statData.st_mode);
if (0 == (statData.st_mode & 0001) )
{
//qtss_printf("no world access to path =%s\n",searchBuffer);
result = false;
break;
}
result = true;
} while (false);
} ;
}
return result;
}
Bool16 QTSSAuthorize(QTSS_StandardRTSP_Params* inParams, char* pathBuff)
{
QTSS_Error theErr = QTSS_NoErr;
Bool16 result = false;
const int kBuffLen = 256;
char passwordBuff[kBuffLen] = {};
char nameBuff[kBuffLen] = {};
StrPtrLen nameStr(nameBuff, kBuffLen -1);
StrPtrLen passwordStr(passwordBuff, kBuffLen -1);
Bool16 noUserName = false;
Bool16 noPassword = false;
Bool16 isSpecialGuest = false;
do
{
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserName,0, (void *) nameStr.Ptr, &nameStr.Len);
//qtss_printf("GetValue qtssRTSPReqUserName err =%"_S32BITARG_" \n",theErr);
if ( (theErr != QTSS_NoErr) || (nameStr.Len == 0) || (nameStr.Ptr == NULL) || (*nameStr.Ptr == '\0'))
{
//qtss_printf ("no user name\n");
noUserName = true;
}
//qtss_printf("RTSPRequest dictionary name =%s len = %"_S32BITARG_"\n",nameStr.Ptr, nameStr.Len);
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserPassword,0, (void *) passwordStr.Ptr, &passwordStr.Len);
//qtss_printf("GetValue qtssRTSPReqUserName err =%"_S32BITARG_" \n",theErr);
if ( (theErr != QTSS_NoErr) || (passwordStr.Len == 0) || (passwordStr.Ptr == NULL) || (*passwordStr.Ptr == '\0'))
{
//qtss_printf ("no Password\n");
noPassword = true;
}
//qtss_printf("RTSPRequest dictionary password =%s len = %"_S32BITARG_" \n",passwordStr.Ptr, passwordStr.Len);
if (noUserName && noPassword) isSpecialGuest = true;
if (isSpecialGuest) // no name and no password means guest
{
//qtss_printf ("User is guest check for access\n");
result = CheckWorldDirAccess(pathBuff);
if (true == result)
result = CheckWorldFileAccess(pathBuff);
break; // no further processing on guest
}
if (0 == strcasecmp(nameStr.Ptr, sRootUserPtr) )
{ //qtss_printf("user is root no root access to file =%s\n",pathBuff); // must log
result = false; // don't allow
break;
}
struct passwd *passwdStructPtr = getpwnam(nameStr.Ptr);
if (NULL == passwdStructPtr)
{
//qtss_printf("failed to find name =%s\n",passwordStr.Ptr);
break;
}
char *theCryptedPassword = crypt(passwordStr.Ptr, passwdStructPtr->pw_passwd);
if ( 0 != strcmp(passwdStructPtr->pw_passwd, theCryptedPassword ) )
{
//qtss_printf("failed to match name to password =%s\n",passwordStr.Ptr);
break;
}
result = CheckDirAccess(passwdStructPtr, &nameStr, pathBuff);
if (!result) break;
result = CheckFileAccess(passwdStructPtr, &nameStr, pathBuff);
if (!result) break;
//qtss_printf("QTSSAuthorize: user %s is authorized for %s\n",nameStr.Ptr,pathBuff);
} while (false);
if (!result)
{ //qtss_printf("QTSSAuthorize: user %s is un authorized for %s\n",nameStr.Ptr,pathBuff);
}
return result;
}
Bool16 FileOrSDPFileExists(char *pathBuff, UInt32 *pathLen, const UInt32 maxLen, QTSS_Error *theErrPtr)
{
Bool16 result = false;
do
{
if (FileExists(pathBuff))
{
//qtss_printf("file exists =%s\n",pathBuff);
result = true;
break; // file is there
}
if ( (*pathLen + sSDPSuffix.Len + 1) >= maxLen)
{
//qtss_printf("buffer too small for path\n");
if (theErrPtr) *theErrPtr = ENAMETOOLONG; // don't allow request to succed we can't authorize
break;
}
strcat(pathBuff, sSDPSuffix.Ptr);
//qtss_printf("sdp path =%s\n",pathBuff);
if (FileExists(pathBuff))
{
//qtss_printf("sdp file exists =%s\n",pathBuff);
result = true;
break; // sdp file is there
}
} while (false);
return result;
}
QTSS_Error AuthenticateRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
QTSS_Error theErr = QTSS_NoErr;
OSMutexLocker locker(sUserMutex);
/*
Notes:
pathBuff is cleared so Getting the path should leave a zero terminated string.
Order is important.
1) Get the path
2) See if the path exists or an sdp path based on the path exists
3) if world access on the path is available, allow the request
4) if non-world access on the path or file check the name and password on the path and file
*/
do
{
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) )
{
theErr = QTSS_RequestFailed;
break;
}
UInt32 pathLen = kMaxPathLen -1;
char pathBuff[kMaxPathLen] = {};
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
theErr = QTSS_GetValue (theRTSPRequest,qtssRTSPReqLocalPath,0, (void *) pathBuff, &pathLen);
if ( (theErr != QTSS_NoErr) || (0 == pathLen) )
{
//qtss_printf("path not found in request\n");
break; // Bail on the request. The Server will handle the error
}
Bool16 fileOk = FileOrSDPFileExists(pathBuff, &pathLen, kMaxPathLen, &theErr);
if (!fileOk)
{
//qtss_printf("file not found\n");
break; // Do nothing. The Server will handle the error
}
Bool16 allowRequest = QTSSAuthorize(inParams, pathBuff);
if (allowRequest)
{
//qtss_printf("user is authorized\n");
break; // Do nothing. Allow the request (the default behavior)
}
// We are not allowing the request so pass false back to the server.
theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqUserAllowed, 0, &allowRequest, sizeof(allowRequest));
if (theErr != QTSS_NoErr) break;
#if 0 // test setting a specific realm this is not used in this module
theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, sDefaultRealm.Ptr, sDefaultRealm.Len);
if (theErr != QTSS_NoErr) break;
#endif
} while (false);
if (theErr)
{
Assert(0);
theErr = QTSS_RequestFailed;
}
return theErr;
}

View file

@ -0,0 +1,46 @@
/*
*
* @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: QTSSFILEPRIVSMODULE.h
Contains: Module that handles and file system authorization
*/
#ifndef _QTSSFILEPRIVSMODULE_H_
#define _QTSSFILEPRIVSMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSFilePrivsModule_Main(void* inPrivateArgs);
}
#endif //_QTSSFILEPRIVSMODULE_H_

View file

@ -0,0 +1,458 @@
/*
*
* @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: QTSSFlowControlModule.cpp
Contains: Implements object defined in .h file
*/
#include <stdio.h>
#include "QTSSFlowControlModule.h"
#include "OSHeaders.h"
#include "QTSSModuleUtils.h"
#include "MyAssert.h"
//Turns on printfs that are useful for debugging
#define FLOW_CONTROL_DEBUGGING 0
// ATTRIBUTES IDs
static QTSS_AttributeID sNumLossesAboveTolAttr = qtssIllegalAttrID;
static QTSS_AttributeID sNumLossesBelowTolAttr = qtssIllegalAttrID;
static QTSS_AttributeID sNumWorsesAttr = qtssIllegalAttrID;
// STATIC VARIABLES
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_PrefsObject sServerPrefs = NULL;
static QTSS_ServerObject sServer = NULL;
// Default values for preferences
static UInt32 sDefaultLossThinTolerance = 30;
static UInt32 sDefaultNumLossesToThin = 3;
static UInt32 sDefaultLossThickTolerance = 5;
static UInt32 sDefaultLossesToThick = 6;
static UInt32 sDefaultWorsesToThin = 2;
static Bool16 sDefaultModuleEnabled = true;
// Current values for preferences
static UInt32 sLossThinTolerance = 30;
static UInt32 sNumLossesToThin = 3;
static UInt32 sLossThickTolerance = 5;
static UInt32 sLossesToThick = 6;
static UInt32 sWorsesToThin = 2;
static Bool16 sModuleEnabled = true;
// Server preference we respect
static Bool16 sDisableThinning = false;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSFlowControlModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error RereadPrefs();
static QTSS_Error ProcessRTCPPacket(QTSS_RTCPProcess_Params* inParams);
static void InitializeDictionaryItems(QTSS_RTPStreamObject inStream);
QTSS_Error QTSSFlowControlModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSFlowControlModuleDispatch);
}
QTSS_Error QTSSFlowControlModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParamBlock->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParamBlock->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTCPProcess_Role:
return ProcessRTCPPacket(&inParamBlock->rtcpProcessParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RTCPProcess_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
// Add other attributes
static char* sNumLossesAboveToleranceName = "QTSSFlowControlModuleLossAboveTol";
static char* sNumLossesBelowToleranceName = "QTSSFlowControlModuleLossBelowTol";
static char* sNumGettingWorsesName = "QTSSFlowControlModuleGettingWorses";
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sNumLossesAboveToleranceName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sNumLossesAboveToleranceName, &sNumLossesAboveTolAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sNumLossesBelowToleranceName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sNumLossesBelowToleranceName, &sNumLossesBelowTolAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sNumGettingWorsesName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sNumGettingWorsesName, &sNumWorsesAttr);
// Tell the server our name!
static char* sModuleName = "QTSSFlowControlModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sServer = inParams->inServer;
sServerPrefs = inParams->inPrefs;
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
return RereadPrefs();
}
QTSS_Error RereadPrefs()
{
//
// Use the standard GetPref routine to retrieve the correct values for our preferences
QTSSModuleUtils::GetAttribute(sPrefs, "loss_thin_tolerance", qtssAttrDataTypeUInt32,
&sLossThinTolerance, &sDefaultLossThinTolerance, sizeof(sLossThinTolerance));
QTSSModuleUtils::GetAttribute(sPrefs, "num_losses_to_thin", qtssAttrDataTypeUInt32,
&sNumLossesToThin, &sDefaultNumLossesToThin, sizeof(sNumLossesToThin));
QTSSModuleUtils::GetAttribute(sPrefs, "loss_thick_tolerance", qtssAttrDataTypeUInt32,
&sLossThickTolerance, &sDefaultLossThickTolerance, sizeof(sLossThickTolerance));
QTSSModuleUtils::GetAttribute(sPrefs, "num_losses_to_thick", qtssAttrDataTypeUInt32,
&sLossesToThick, &sDefaultLossesToThick, sizeof(sLossesToThick));
QTSSModuleUtils::GetAttribute(sPrefs, "num_worses_to_thin", qtssAttrDataTypeUInt32,
&sWorsesToThin, &sDefaultWorsesToThin, sizeof(sWorsesToThin));
QTSSModuleUtils::GetAttribute(sPrefs, "flow_control_udp_thinning_module_enabled", qtssAttrDataTypeBool16,
&sModuleEnabled, &sDefaultModuleEnabled, sizeof(sDefaultModuleEnabled));
UInt32 len = sizeof(sDisableThinning);
(void) QTSS_GetValue(sServerPrefs, qtssPrefsDisableThinning, 0, (void*)&sDisableThinning, &len);
return QTSS_NoErr;
}
Bool16 Is3GPPSession(QTSS_RTCPProcess_Params *inParams)
{
Bool16 is3GPP = false;
UInt32 theLen = sizeof(is3GPP);
(void)QTSS_GetValue(inParams->inClientSession, qtssCliSessIs3GPPSession, 0, (void*)&is3GPP, &theLen);
return is3GPP;
}
QTSS_Error ProcessRTCPPacket(QTSS_RTCPProcess_Params* inParams)
{
if (!sModuleEnabled || sDisableThinning || Is3GPPSession(inParams) )
{
//qtss_printf("QTSSFlowControlModule.cpp:ProcessRTCPPacket processing disabled sModuleEnabled=%d sDisableThinning=%d Is3GPPSession(inParams)=%d\n", sModuleEnabled, sDisableThinning,Is3GPPSession(inParams));
return QTSS_NoErr;
}
#if FLOW_CONTROL_DEBUGGING
QTSS_RTPPayloadType* thePayloadType = 0;
UInt32 thePayloadLen = 0;
(void)QTSS_GetValuePtr(inParams->inRTPStream, qtssRTPStrPayloadType, 0, (void**)&thePayloadType, &thePayloadLen);
if ((*thePayloadType != 0) && (*thePayloadType == qtssVideoPayloadType))
qtss_printf("Video track reporting:\n");
else if ((*thePayloadType != 0) && (*thePayloadType == qtssAudioPayloadType))
qtss_printf("Audio track reporting:\n");
else
qtss_printf("Unknown track reporting\n");
#endif
//
// Find out if this is a qtssRTPTransportTypeUDP. This is the only type of
// transport we should monitor
QTSS_RTPTransportType theTransportType = qtssRTPTransportTypeUDP;
UInt32 theLen = sizeof(theTransportType);
QTSS_Error theErr = QTSS_GetValue(inParams->inRTPStream, qtssRTPStrTransportType, 0, (void*)&theTransportType, &theLen);
Assert(theErr == QTSS_NoErr);
if (theTransportType != qtssRTPTransportTypeUDP)
return QTSS_NoErr;
//ALGORITHM FOR DETERMINING WHEN TO MAKE QUALITY ADJUSTMENTS IN THE STREAM:
//This routine makes quality adjustment determinations for the server. It is designed
//to be flexible: you may swap this algorithm out for another implemented in another module,
//and this algorithm uses settings that are adjustable at runtime.
//It uses the % loss statistic in the RTCP packets, as well as the "getting better" &
//"getting worse" fields.
//Less bandwidth will be served if the loss % of N number of RTCP packets is above M, where
//N and M are runtime settings.
//Less bandwidth will be served if "getting worse" is reported N number of times.
//More bandwidth will be served if the loss % of N number of RTCPs is below M.
//N will scale up over time.
//More bandwidth will be served if the client reports "getting better"
//If the initial values of our dictionary items aren't yet in, put them in.
InitializeDictionaryItems(inParams->inRTPStream);
QTSS_RTPStreamObject theStream = inParams->inRTPStream;
Bool16 ratchetMore = false;
Bool16 ratchetLess = false;
Bool16 clearPercentLossThinCount = true;
Bool16 clearPercentLossThickCount = true;
UInt32* uint32Ptr = NULL;
UInt16* uint16Ptr = NULL;
theLen = 0;
UInt32 theNumLossesAboveTol = 0;
UInt32 theNumLossesBelowTol = 0;
UInt32 theNumWorses = 0;
// Get our current counts for this stream. If any of these aren't present, something is seriously wrong
// with this dictionary, so we should probably just abort
(void)QTSS_GetValuePtr(theStream, sNumLossesAboveTolAttr, 0, (void**)&uint32Ptr, &theLen);
if ((uint32Ptr != NULL) && (theLen == sizeof(UInt32)))
theNumLossesAboveTol = *uint32Ptr;
(void)QTSS_GetValuePtr(theStream, sNumLossesBelowTolAttr, 0, (void**)&uint32Ptr, &theLen);
if ((uint32Ptr != NULL) && (theLen == sizeof(UInt32)))
theNumLossesBelowTol = *uint32Ptr;
(void)QTSS_GetValuePtr(theStream, sNumWorsesAttr, 0, (void**)&uint32Ptr, &theLen);
if ((uint32Ptr != NULL) && (theLen == sizeof(UInt32)))
theNumWorses = *uint32Ptr;
//First take any action necessitated by the loss percent
(void)QTSS_GetValuePtr(inParams->inRTPStream, qtssRTPStrPercentPacketsLost, 0, (void**)&uint16Ptr, &theLen);
if ((uint16Ptr != NULL) && (theLen == sizeof(UInt16)))
{
UInt16 thePercentLoss = *uint16Ptr;
thePercentLoss /= 256; //Hmmm... looks like the client reports loss percent in multiples of 256
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Percent loss: %d\n", thePercentLoss);
#endif
//check for a thinning condition
if (thePercentLoss > sLossThinTolerance)
{
theNumLossesAboveTol++;//we must count this loss
//We only adjust after a certain number of these in a row. Check to see if we've
//satisfied the thinning condition, and adjust the count
if (theNumLossesAboveTol >= sNumLossesToThin)
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Percent loss too high: ratcheting less\n");
#endif
ratchetLess = true;
}
else
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Percent loss too high: Incrementing percent loss count to %"_U32BITARG_"\n", theNumLossesAboveTol);
#endif
(void)QTSS_SetValue(theStream, sNumLossesAboveTolAttr, 0, &theNumLossesAboveTol, sizeof(theNumLossesAboveTol));
clearPercentLossThinCount = false;
}
}
//check for a thickening condition
else if (thePercentLoss < sLossThickTolerance)
{
theNumLossesBelowTol++;//we must count this loss
if (theNumLossesBelowTol >= sLossesToThick)
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Percent is low: ratcheting more\n");
#endif
ratchetMore = true;
}
else
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Percent is low: Incrementing percent loss count to %"_U32BITARG_"\n", theNumLossesBelowTol);
#endif
(void)QTSS_SetValue(theStream, sNumLossesBelowTolAttr, 0, &theNumLossesBelowTol, sizeof(theNumLossesBelowTol));
clearPercentLossThickCount = false;
}
}
}
//Now take a look at the getting worse heuristic
(void)QTSS_GetValuePtr(inParams->inRTPStream, qtssRTPStrGettingWorse, 0, (void**)&uint16Ptr, &theLen);
if ((uint16Ptr != NULL) && (theLen == sizeof(UInt16)))
{
UInt16 isGettingWorse = *uint16Ptr;
if (isGettingWorse != 0)
{
theNumWorses++;//we must count this getting worse
//If we've gotten N number of getting worses, then thin. Otherwise, just
//increment our count of getting worses
if (theNumWorses >= sWorsesToThin)
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Client reporting getting worse. Ratcheting less\n");
#endif
ratchetLess = true;
}
else
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Client reporting getting worse. Incrementing num worses count to %"_U32BITARG_"\n", theNumWorses);
#endif
(void)QTSS_SetValue(theStream, sNumWorsesAttr, 0, &theNumWorses, sizeof(theNumWorses));
}
}
}
//Finally, if we get a getting better, automatically ratchet up
(void)QTSS_GetValuePtr(inParams->inRTPStream, qtssRTPStrGettingBetter, 0, (void**)&uint16Ptr, &theLen);
if ((uint16Ptr != NULL) && (theLen == sizeof(UInt16)) && (*uint16Ptr > 0))
ratchetMore = true;
//For clearing out counts below
UInt32 zero = 0;
//Based on the ratchetMore / ratchetLess variables, adjust the stream
if (ratchetMore || ratchetLess)
{
UInt32 curQuality = 0;
(void)QTSS_GetValuePtr(theStream, qtssRTPStrQualityLevel, 0, (void**)&uint32Ptr, &theLen);
if ((uint32Ptr != NULL) && (theLen == sizeof(UInt32)))
curQuality = *uint32Ptr;
UInt32 numQualityLevels = 0;
(void)QTSS_GetValuePtr(theStream, qtssRTPStrNumQualityLevels, 0, (void**)&uint32Ptr, &theLen);
if ((uint32Ptr != NULL) && (theLen == sizeof(UInt32)))
numQualityLevels = *uint32Ptr;
if ((ratchetLess) && (curQuality < numQualityLevels))
{
curQuality++;
if (curQuality > 1) // v3.0.1=v2.0.1 make level 2 means key frames in the file or max if reflected.
curQuality = numQualityLevels;
(void)QTSS_SetValue(theStream, qtssRTPStrQualityLevel, 0, &curQuality, sizeof(curQuality));
}
else if ((ratchetMore) && (curQuality > 0))
{
curQuality--;
if (curQuality > 1) // v3.0.1=v2.0.1 make level 2 means key frames in the file or max if reflected.
curQuality = 1;
(void)QTSS_SetValue(theStream, qtssRTPStrQualityLevel, 0, &curQuality, sizeof(curQuality));
}
Bool16 *startedThinningPtr = NULL;
SInt32 numThinned = 0;
(void)QTSS_GetValuePtr(inParams->inClientSession, qtssCliSesStartedThinning, 0, (void**)&startedThinningPtr, &theLen);
if (false == *startedThinningPtr)
{
(void) QTSS_LockObject(sServer);
*startedThinningPtr = true;
(void)QTSS_GetValue(sServer, qtssSvrNumThinned, 0, (void*)&numThinned, &theLen);
numThinned++;
(void)QTSS_SetValue(sServer, qtssSvrNumThinned, 0, &numThinned, sizeof(numThinned));
(void) QTSS_UnlockObject(sServer);
}
else if (curQuality == 0)
{
(void) QTSS_LockObject(sServer);
*startedThinningPtr = false;
(void)QTSS_GetValue(theStream, qtssSvrNumThinned, 0, (void*)&numThinned, &theLen);
numThinned--;
(void)QTSS_SetValue(sServer, qtssSvrNumThinned, 0, &numThinned, sizeof(numThinned));
(void) QTSS_UnlockObject(sServer);
}
//When adjusting the quality, ALWAYS clear out ALL our counts of EVERYTHING. Note
//that this is the ONLY way that the fNumGettingWorses count gets cleared
(void)QTSS_SetValue(theStream, sNumWorsesAttr, 0, &zero, sizeof(zero));
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Clearing num worses count\n");
#endif
clearPercentLossThinCount = true;
clearPercentLossThickCount = true;
}
//clear thick / thin counts if we are supposed to.
if (clearPercentLossThinCount)
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Clearing num losses above tolerance count\n");
#endif
(void)QTSS_SetValue(theStream, sNumLossesAboveTolAttr, 0, &zero, sizeof(zero));
}
if (clearPercentLossThickCount)
{
#if FLOW_CONTROL_DEBUGGING
qtss_printf("Clearing num losses below tolerance count\n");
#endif
(void)QTSS_SetValue(theStream, sNumLossesBelowTolAttr, 0, &zero, sizeof(zero));
}
return QTSS_NoErr;
}
void InitializeDictionaryItems(QTSS_RTPStreamObject inStream)
{
UInt32* theValue = NULL;
UInt32 theValueLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inStream, sNumLossesAboveTolAttr, 0, (void**)&theValue, &theValueLen);
if (theErr != QTSS_NoErr)
{
// The dictionary parameters haven't been initialized yet. Just set them all to 0.
(void)QTSS_SetValue(inStream, sNumLossesAboveTolAttr, 0, &theValueLen, sizeof(theValueLen));
(void)QTSS_SetValue(inStream, sNumLossesBelowTolAttr, 0, &theValueLen, sizeof(theValueLen));
(void)QTSS_SetValue(inStream, sNumWorsesAttr, 0, &theValueLen, sizeof(theValueLen));
}
}

View file

@ -0,0 +1,46 @@
/*
*
* @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: QTSSFlowControlModule.h
Contains: Uses information in RTCP packers to throttle back the server
when it's pumping out too much data to a given client
*/
#ifndef _QTSSFLOWCONTROLMODULE_H_
#define _QTSSFLOWCONTROLMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSFlowControlModule_Main(void* inPrivateArgs);
}
#endif //_QTSSFLOWCONTROLMODULE_H_

View file

@ -0,0 +1,123 @@
/*
*
* @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: DirectoryInfo.cpp
Contains: Implementation of class defined in .h file
*/
#include "DirectoryInfo.h"
Bool16 SessionListElement::Equal(QTSS_ClientSessionObject* inSessionPtr)
{
UInt32 *theSessionID = 0;
UInt32 *inSessionID = 0;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(fSession, qtssCliSesCounterID, 0, (void **)&theSessionID, &theLen);
Assert(theLen != 0);
(void)QTSS_GetValuePtr(*inSessionPtr, qtssCliSesCounterID, 0, (void **)&inSessionID, &theLen);
Assert(theLen != 0);
if (*theSessionID == *inSessionID)
return true;
return false;
}
UInt32 SessionListElement::CurrentBitRate()
{
UInt32 *theBitRate = 0;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(fSession, qtssCliSesCurrentBitRate, 0, (void **)&theBitRate, &theLen);
Assert(theLen != 0);
return *theBitRate;
}
static bool IsSession(PLDoubleLinkedListNode<SessionListElement>* node, void* userData)
{
/*
used by ForEachUntil to find a SessionListElement with a given Session
userData is a pointer to the QTSS_ClientSessionObject we want to find
*/
return node->fElement->Equal((QTSS_ClientSessionObject *)userData);
}
static void AddCurrentBitRate(PLDoubleLinkedListNode<SessionListElement>* node, void* userData)
{
*(UInt64 *)userData += node->fElement->CurrentBitRate();
}
DirectoryInfo::~DirectoryInfo()
{
fMutex.Lock();
fClientSessionList->ClearList();
fNumSessions = 0;
fHomeDir.Delete();
fMutex.Unlock();
}
void DirectoryInfo::AddSession(QTSS_ClientSessionObject *sessionPtr)
{
fMutex.Lock();
SessionListElement *theElement = NEW SessionListElement(sessionPtr);
PLDoubleLinkedListNode<SessionListElement> *sessionNode = new PLDoubleLinkedListNode<SessionListElement> (theElement);
fClientSessionList->AddNode(sessionNode);
fNumSessions++;
fMutex.Unlock();
}
void DirectoryInfo::RemoveSession(QTSS_ClientSessionObject *sessionPtr)
{
fMutex.Lock();
PLDoubleLinkedListNode<SessionListElement> *node = NULL;
node = fClientSessionList->ForEachUntil(IsSession, (void *)sessionPtr);
if (node != NULL)
{
fClientSessionList->RemoveNode(node);
fNumSessions--;
}
fMutex.Unlock();
}
UInt64 DirectoryInfo::CurrentTotalBandwidthInKbps()
{
fMutex.Lock();
UInt64 totalBandwidth = 0;
fClientSessionList->ForEach(AddCurrentBitRate, &totalBandwidth);
fMutex.Unlock();
return (totalBandwidth/1024);
}

View file

@ -0,0 +1,79 @@
/*
*
* @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: DirectoryInfo.h
Contains: Stores an array of client sessions, # of client sessions,
and the home directory
*/
#ifndef _DIRECTORYINFO_H_
#define _DIRECTORYINFO_H_
#include "QTSS.h"
#include "StrPtrLen.h"
#include "OSRef.h"
#include "OSMemory.h"
#include "PLDoubleLinkedList.h"
class SessionListElement {
public:
SessionListElement(QTSS_ClientSessionObject *inSessionPtr) { fSession = *inSessionPtr; }
virtual ~SessionListElement() { fSession = NULL; }
Bool16 Equal(QTSS_ClientSessionObject* inSessionPtr);
UInt32 CurrentBitRate();
private:
QTSS_ClientSessionObject fSession;
};
class DirectoryInfo
{
public:
DirectoryInfo(StrPtrLen *inHomeDir):fNumSessions(0), fHomeDir(inHomeDir->GetAsCString())
{
fClientSessionList = NEW PLDoubleLinkedList<SessionListElement>;
fRef.Set(fHomeDir, (void *)this);
}
~DirectoryInfo();
OSRef* GetRef() { return &fRef; }
void AddSession(QTSS_ClientSessionObject *sessionPtr);
void RemoveSession(QTSS_ClientSessionObject *sessionPtr);
UInt64 CurrentTotalBandwidthInKbps();
UInt32 NumSessions() { return fNumSessions; }
private:
OSRef fRef;
OSMutex fMutex;
PLDoubleLinkedList<SessionListElement> *fClientSessionList;
UInt32 fNumSessions;
StrPtrLen fHomeDir;
};
#endif // _DIRECTORYINFO_H_

View file

@ -0,0 +1,416 @@
/*
*
* @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: QTSSHomeDirectoryModule.cpp
Contains: Module that expands ~ in request URLs to home directories
*/
#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include "OSMemory.h"
#include "StringParser.h"
#include "ResizeableStringFormatter.h"
#include "QTSSModuleUtils.h"
#include "OSArrayObjectDeleter.h"
#include "DirectoryInfo.h"
#include "QTSSMemoryDeleter.h"
#include "QTSSHomeDirectoryModule.h"
#define HOME_DIRECTORY_MODULE_DEBUGGING 0
// STATIC DATA
static QTSS_ServerObject sServer = NULL;
static QTSS_ModuleObject sModule = NULL;
static QTSS_ModulePrefsObject sPrefs = NULL;
// Attributes
static char* sIsFirstRequestName = "QTSSHomeDirectoryModuleIsFirstRequest";
static QTSS_AttributeID sIsFirstRequestAttr = qtssIllegalAttrID;
static char* sRequestHomeDirAttrName = "QTSSHomeDirectoryModuleHomeDir";
static QTSS_AttributeID sRequestHomeDirAttr = qtssIllegalAttrID;
static char* sSessionHomeDirAttrName = "QTSSHomeDirectoryModuleHomeDir";
static QTSS_AttributeID sSessionHomeDirAttr = qtssIllegalAttrID;
// Module description and version
static char* sDescription = "Provides support for streaming from users' home directories";
static UInt32 sVersion = 0x00010000;
// Module preferences and their defaults
static Bool16 sEnabled = false;
static Bool16 kDefaultEnabled = false;
static char* sMoviesDirectory = NULL; // Streaming dir in user's home dir
static char* kDefaultMoviesDirectory = "/Sites/Streaming/";
static UInt32 sMaxNumConnsPerHomeDir = 0; //Max conns. allowed per home directory
static UInt32 kDefaultMaxNumConnsPerHomeDir = 0; // 0 => no limit enforced
static UInt32 sMaxBWInKbpsPerHomeDir = 0; // Max BW allowed per home directory
static UInt32 kDefaultMaxBWInKbpsPerHomeDir = 0; // 0 => no limit enforced
enum { kDefaultHomeDirSize = 64 };
static Bool16 sTrue = true;
static OSRefTable* sDirectoryInfoMap = NULL;
// FUNCTIONS
static QTSS_Error QTSSHomeDirectoryDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error RereadPrefs();
static QTSS_Error RewriteRequestFilePathAndRootDir(QTSS_StandardRTSP_Params* inParams);
static void RewriteRootDir(QTSS_StandardRTSP_Params* inParams, StrPtrLen* inHomeDir);
static QTSS_Error AuthorizeRequest(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error RemoveClientSessionFromMap(QTSS_ClientSessionClosing_Params* inParams);
// Helper functions
static Bool16 ExpandPath(const StrPtrLen *inPathPtr, StrPtrLen *outPathPtr);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSHomeDirectoryModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSHomeDirectoryDispatch);
}
QTSS_Error QTSSHomeDirectoryDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPRoute_Role:
{
if (!sEnabled)
break;
return RewriteRequestFilePathAndRootDir(&inParams->rtspRouteParams);
}
case QTSS_RTSPAuthorize_Role:
{
if (!sEnabled)
break;
return AuthorizeRequest(&inParams->rtspAuthParams);
}
case QTSS_ClientSessionClosing_Role:
{
if (!sEnabled)
break;
return RemoveClientSessionFromMap(&inParams->clientSessionClosingParams);
}
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPRoute_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
// Add an RTSP session attribute to track if the request is the first request of the session
// so that the bandwidth/# of connections quota check can be done only if it is the first request
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sIsFirstRequestName, NULL, qtssAttrDataTypeBool16);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sIsFirstRequestName, &sIsFirstRequestAttr);
// Add an RTSP request object attribute for tracking the home directory it is being served from
(void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sRequestHomeDirAttrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sRequestHomeDirAttrName, &sRequestHomeDirAttr);
// Add an RTP session object attribute for tracking the home directory it is being served from
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sSessionHomeDirAttrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sSessionHomeDirAttrName, &sSessionHomeDirAttr);
// Tell the server our name!
static char* sModuleName = "QTSSHomeDirectoryModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
// Get the server object
sServer = inParams->inServer;
// Get our prefs object
sModule = inParams->inModule;
sPrefs = QTSSModuleUtils::GetModulePrefsObject(sModule);
// Set our version and description
(void)QTSS_SetValue(sModule, qtssModDesc, 0, sDescription, ::strlen(sDescription));
(void)QTSS_SetValue(sModule, qtssModVersion, 0, &sVersion, sizeof(sVersion));
RereadPrefs();
sDirectoryInfoMap = NEW OSRefTable();
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
QTSSModuleUtils::GetAttribute(sPrefs, "enabled", qtssAttrDataTypeBool16,
&sEnabled, &kDefaultEnabled, sizeof(sEnabled));
delete [] sMoviesDirectory;
sMoviesDirectory = QTSSModuleUtils::GetStringAttribute(sPrefs, "movies_directory", kDefaultMoviesDirectory);
QTSSModuleUtils::GetAttribute(sPrefs, "max_num_conns_per_home_directory", qtssAttrDataTypeUInt32,
&sMaxNumConnsPerHomeDir, &kDefaultMaxNumConnsPerHomeDir, sizeof(sMaxNumConnsPerHomeDir));
QTSSModuleUtils::GetAttribute(sPrefs, "max_bandwidth_kbps_per_home_directory", qtssAttrDataTypeUInt32,
&sMaxBWInKbpsPerHomeDir, &kDefaultMaxBWInKbpsPerHomeDir, sizeof(sMaxBWInKbpsPerHomeDir));
return QTSS_NoErr;
}
QTSS_Error RewriteRequestFilePathAndRootDir(QTSS_StandardRTSP_Params* inParams)
{
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
char* requestPathStr;
(void)QTSS_GetValueAsString(theRequest, qtssRTSPReqFilePath, 0, &requestPathStr);
QTSSCharArrayDeleter requestPathStrDeleter(requestPathStr);
StrPtrLen theRequestPath(requestPathStr);
StringParser theRequestPathParser(&theRequestPath);
// request path begins with a '/' for ex. /mysample.mov or /~joe/mysample.mov
theRequestPathParser.Expect('/');
if (theRequestPathParser.PeekFast() == '~')
{
theRequestPathParser.Expect('~');
StrPtrLen theFirstPath;
theRequestPathParser.ConsumeUntil(&theFirstPath, '/');
if (theFirstPath.Len != 0) // if the URI is /~/mysample.mov --> do nothing
{
StrPtrLen theHomeDir;
// ExpandPath allocates memory, so delete it before exiting
if (ExpandPath(&theFirstPath, &theHomeDir))
{
// Rewrite the qtssRTSPReqRootDir attribute
RewriteRootDir(inParams, &theHomeDir);
StrPtrLen theStrippedRequestPath;
theRequestPathParser.ConsumeLength(&theStrippedRequestPath, theRequestPathParser.GetDataRemaining());
(void) QTSS_SetValue(theRequest, qtssRTSPReqFilePath, 0, theStrippedRequestPath.Ptr, theStrippedRequestPath.Len);
}
theHomeDir.Delete();
}
}
return QTSS_NoErr;
}
void RewriteRootDir(QTSS_StandardRTSP_Params* inParams, StrPtrLen* inHomeDir)
{
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
QTSS_ClientSessionObject theSession = inParams->inClientSession;
QTSS_Error theErr = QTSS_NoErr;
Assert(inHomeDir->Len != 0);
if (inHomeDir->Len == 0)
return;
char homeDirFormatterBuf[kDefaultHomeDirSize] = "";
ResizeableStringFormatter theHomeDirFormatter(homeDirFormatterBuf, kDefaultHomeDirSize);
theHomeDirFormatter.Put(inHomeDir->Ptr, inHomeDir->Len);
// Append a path separator if the movies directory doesn't begin with it
if ((::strlen(sMoviesDirectory) != 0) && (sMoviesDirectory[0] != '/'))
theHomeDirFormatter.PutChar('/');
// Append the movies_directory
theHomeDirFormatter.Put(sMoviesDirectory);
StrPtrLen theRootDir(theHomeDirFormatter.GetBufPtr(), theHomeDirFormatter.GetBytesWritten());
// Set qtssRTSPReqRootDir
(void)QTSS_SetValue(theRequest, qtssRTSPReqRootDir, 0, theRootDir.Ptr, theRootDir.Len);
// Set the client session's home dir attribute
(void)QTSS_SetValue(theSession, sSessionHomeDirAttr, 0, theRootDir.Ptr, theRootDir.Len);
// If the request is a PLAY, then add this to the session list for the home directory
QTSS_RTSPMethod *theMethod = NULL;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(theRequest, qtssRTSPReqMethod, 0, (void **)&theMethod, &theLen);
if (*theMethod == qtssPlayMethod)
{
OSRef* theDirectoryInfoRef = sDirectoryInfoMap->Resolve(&theRootDir);
if (theDirectoryInfoRef == NULL)
{
DirectoryInfo* newDirInfo = NEW DirectoryInfo(&theRootDir);
theErr = sDirectoryInfoMap->Register(newDirInfo->GetRef());
Assert(theErr == QTSS_NoErr);
theDirectoryInfoRef = sDirectoryInfoMap->Resolve(&theRootDir);
Assert (theDirectoryInfoRef == newDirInfo->GetRef());
}
DirectoryInfo* theDirInfo = (DirectoryInfo *)theDirectoryInfoRef->GetObject();
theDirInfo->AddSession(&theSession);
sDirectoryInfoMap->Release(theDirectoryInfoRef);
}
#if HOME_DIRECTORY_MODULE_DEBUGGING
char* newRequestPath = NULL;
theErr = QTSS_GetValueAsString (theRequest, qtssRTSPReqFilePath, 0, &newRequestPath);
qtss_printf("QTSSHomeDirectoryModule::RewriteRootDir qtssRTSPReqFilePath = %s\n", newRequestPath);
QTSS_Delete(newRequestPath);
char* newRequestRootDir = NULL;
theErr = QTSS_GetValueAsString (theRequest, qtssRTSPReqRootDir, 0, &newRequestRootDir);
qtss_printf("QTSSHomeDirectoryModule::RewriteRootDir qtssRTSPReqRootDir = %s\n", newRequestRootDir);
QTSS_Delete(newRequestRootDir);
#endif
}
QTSS_Error AuthorizeRequest(QTSS_StandardRTSP_Params* inParams)
{
// Only do anything if this is the first request
Bool16 *isFirstRequest = NULL;
UInt32 theLen = sizeof(isFirstRequest);
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sIsFirstRequestAttr, 0, (void**)&isFirstRequest, &theLen);
if (isFirstRequest != NULL)
return QTSS_NoErr;
OSRef *theDirectoryInfoRef = NULL;
DirectoryInfo *theDirInfo = NULL;
StrPtrLen theHomeDir;
// if no limits are imposed, do nothing
if ((sMaxNumConnsPerHomeDir == 0) && (sMaxBWInKbpsPerHomeDir == 0))
goto end_authorize;
// Get this client session's home dir
(void)QTSS_GetValuePtr(inParams->inClientSession, sSessionHomeDirAttr, 0, (void**)&theHomeDir.Ptr, &theHomeDir.Len);
if ((theHomeDir.Ptr == NULL) || (theHomeDir.Len == 0)) // If it's NULL it's not served out of the home dir
goto end_authorize;
// Get the sessions for this home dir
theDirectoryInfoRef = sDirectoryInfoMap->Resolve(&theHomeDir);
if (theDirectoryInfoRef == NULL) // No sessions exist yet for this homeDir
goto end_authorize;
theDirInfo = (DirectoryInfo *)(theDirectoryInfoRef->GetObject());
if ((sMaxNumConnsPerHomeDir != 0) && ((theDirInfo->NumSessions()) >= sMaxNumConnsPerHomeDir))
QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotEnoughBandwidth, qtssMsgTooManyClients);
else if((sMaxBWInKbpsPerHomeDir != 0) && ((theDirInfo->CurrentTotalBandwidthInKbps()) >= sMaxBWInKbpsPerHomeDir))
QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotEnoughBandwidth, qtssMsgTooMuchThruput);
sDirectoryInfoMap->Release(theDirectoryInfoRef);
end_authorize:
// Mark the request so we'll know subsequent ones aren't the first.
(void)QTSS_SetValue(inParams->inRTSPSession, sIsFirstRequestAttr, 0, &sTrue, sizeof(sTrue));
return QTSS_NoErr;
}
QTSS_Error RemoveClientSessionFromMap(QTSS_ClientSessionClosing_Params* inParams)
{
QTSS_ClientSessionObject theSession = inParams->inClientSession;
StrPtrLen theHomeDir;
(void)QTSS_GetValuePtr (theSession, sSessionHomeDirAttr, 0, (void**)&theHomeDir.Ptr, &theHomeDir.Len);
if ((theHomeDir.Ptr != NULL) && (theHomeDir.Len != 0))
{
OSRef* theDirectoryInfoRef = sDirectoryInfoMap->Resolve(&theHomeDir);
if (theDirectoryInfoRef != NULL)
{
DirectoryInfo *theDirInfo = (DirectoryInfo *)theDirectoryInfoRef->GetObject();
theDirInfo->RemoveSession(&theSession);
sDirectoryInfoMap->Release(theDirectoryInfoRef);
(void)sDirectoryInfoMap->TryUnRegister(theDirectoryInfoRef);
}
}
return QTSS_NoErr;
}
// Expand path expands the ~ or ~username to the home directory of the user
// It allocates memory for the outPathPtr->Ptr, so the caller should delete it
// inPathPtr has the tilde stripped off, so inPathPtr->Len == 0 is valid
Bool16 ExpandPath(const StrPtrLen *inPathPtr, StrPtrLen *outPathPtr)
{
Assert(inPathPtr != NULL);
Assert(outPathPtr != NULL);
char *newPath = NULL;
if (inPathPtr->Len != 0)
{
char* inPathStr = inPathPtr->GetAsCString();
struct passwd *passwdStruct = getpwnam(inPathStr);
delete [] inPathStr;
if (passwdStruct != NULL)
newPath = passwdStruct->pw_dir;
}
/*
if (inPathPtr->Len == 0)
{
newPath = getenv("HOME");
if (newPath == NULL)
{
struct passwd *passwdStruct = getpwuid(getuid());
if (passwdStruct != NULL)
newPath = passwdStruct->pw_dir;
}
}
*/
if (newPath == NULL)
return false;
UInt32 newPathLen = ::strlen(newPath);
char* newPathStr = NEW char[newPathLen + 1];
::strcpy(newPathStr, newPath);
outPathPtr->Ptr = newPathStr;
outPathPtr->Len = newPathLen;
return true;
}

View file

@ -0,0 +1,41 @@
/*
*
* @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: QTSSHomeDirectoryModule.h
Contains: Module that expands ~ in request URLs to home directories
*/
#ifndef __QTSS_HOMEDIRECTORY_MODULE_H__
#define __QTSS_HOMEDIRECTORY_MODULE_H__
#include "QTSS.h"
extern "C"
{
QTSS_Error QTSSHomeDirectoryModule_Main(void* inPrivateArgs);
}
#endif // __QTSS_HOMEDIRECTORY_MODULE_H__

View file

@ -0,0 +1,178 @@
#!/bin/sh
#
#
OPTION=""
PLAT=`uname`
if [ ${1} ]; then
OPTION=${1}
fi
if [ "$OPTION" = "-h" ]; then
OPTION=""
fi
if [ ${OPTION} ]; then
OK=YES
else
echo ""
echo "usage: createuserstreamingdir user"
echo ""
echo "This tool will create the directory ~user/Sites/Streaming/."
echo "The created directory gives the QuickTimeStreamingServer access to user managed content."
echo ""
exit 0
fi
echo ""
CALLER=`whoami`
if [ "$1" = "$CALLER" ] ; then
OK=YES
else
if [ `id -u` != 0 ]
then
echo "You must be root, ${1}, or use the sudo command to proceed. "
echo "Cannot continue."
echo ""
echo "usage: createuserstreamingdir user"
echo ""
exit 1
fi
fi
#
# Home dir
#
NEWPATH=~"${1}"
HOMEDIR=`eval "echo $NEWPATH"`
echo "examining the home directory for ${NEWPATH}"
echo "home directory path = ${HOMEDIR}"
if [ -e "${HOMEDIR}" ] ; then
OK=YES
else
echo "The path \"${HOMEDIR}\" is not found."
echo "Cannot continue."
echo ""
echo "usage: createuserstreamingdir user"
echo ""
exit 1
fi
if [ -d "${HOMEDIR}" ] ; then
OK=YES
else
echo "${HOMEDIR} is not a directory."
echo "Cannot continue."
echo ""
exit 1
fi
if [ "Darwin" != "$PLAT" ]; then
chmod 755 "${HOMEDIR}"
echo "Set privileges for ${HOMEDIR} to 755 "
fi
#
# /Sites
#
if [ -e "${HOMEDIR}/Sites" ]; then
OK=YES
else
if [ -w "${HOMEDIR}/" ]; then
mkdir "${HOMEDIR}/Sites"
chmod 755 "${HOMEDIR}/Sites"
chown ${1} "${HOMEDIR}/Sites"
fi
fi
if [ -e "${HOMEDIR}/Sites" ]; then
OK=YES
else
echo "You do not have privileges to create ${HOMEDIR}/Sites."
echo "Cannot continue."
echo ""
exit 1
fi
if [ -d "${HOMEDIR}/Sites" ]; then
OK=YES
else
echo "${HOMEDIR}/Sites is not a directory."
echo "Cannot continue."
echo ""
exit 1
fi
#
# /Sites/Streaming
#
if [ -e "${HOMEDIR}/Sites/Streaming" ]; then
OK=YES
else
if [ -w "${HOMEDIR}/Sites" ]; then
mkdir -m 755 "${HOMEDIR}/Sites/Streaming"
chown ${1}:qtss "${HOMEDIR}/Sites/Streaming"
fi
fi
if [ -e "${HOMEDIR}/Sites/Streaming" ]; then
OK=YES
else
echo "You do not have privileges to create ${HOMEDIR}/Sites/Streaming."
echo "Cannot continue."
echo ""
exit 1
fi
if [ -d "${HOMEDIR}/Sites/Streaming" ]; then
OK=YES
else
echo "${HOMEDIR}/Sites/Streaming is not a directory."
echo "Cannot continue."
echo ""
exit 1
fi
#
# Test access
#
if [ -w "${HOMEDIR}/Sites/Streaming/" ]; then
OK=YES
else
echo "You do not have privileges to modify ${HOMEDIR}/Sites/Streaming."
echo "Cannot continue."
echo ""
exit 1
fi
chown ${1}:qtss "${HOMEDIR}/Sites/Streaming" > /dev/null 2>&1
if [ $? = 0 ]; then
OK=YES
else
echo "You are not the owner."
echo "You may need to run this tool again as root or use the sudo command."
echo ""
exit 1
fi
chmod 755 "${HOMEDIR}/Sites/Streaming" > /dev/null 2>&1
if [ $? = 0 ]; then
OK=YES
else
echo "The permissions are not correct."
echo "You may need to run this tool again as root or use the sudo command."
echo ""
exit 1
fi
echo "${HOMEDIR}/Sites/Streaming is ready for streaming."
echo ""
exit 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
/*
*
* @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: QTSSHttpFileModule.h
Contains: A module for HTTP file transfer of files and for
on-the-fly ref movie creation.
Uses the Filter module feature of QTSS API.
*/
#ifndef __QTSSHTTPFILEMODULE_H__
#define __QTSSHTTPFILEMODULE_H__
#include "QTSS.h"
enum {
transferHttpFile = 1,
transferRefMovieFolder = 2,
transferRefMovieFile = 3
};
typedef UInt32 TransferType;
extern "C"
{
QTSS_Error QTSSHttpFileModule_Main(void* inPrivateArgs);
}
#endif // __QTSSHTTPFILEMODULE_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,508 @@
/*
*
* @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: QTSSMP3StreamingModule.h
Contains: Handle ShoutCast/IceCast-style MP3 streaming.
Written by: Steve Ussery
*/
#ifndef __QTSSMP3STREAMINGMODULE_H__
#define __QTSSMP3STREAMINGMODULE_H__
#include "QTSS.h"
#include "OSQueue.h"
#include "OSMutex.h"
#include "OSHashTable.h"
#include "OSBufferPool.h"
extern "C"
{
EXPORT QTSS_Error QTSSMP3StreamingModule_Main(void* inPrivateArgs);
}
#define kURLBufferSize 1024
#define kSongNameBufferSize 1024
#define kHeaderBufferSize 1024
#define kRequestBufferSize 512
#define kHostNameBufferSize 256
#define kUserAgentBufferSize 256
#define kClientMetaInt 24576
// The client poll interval in milliseconds
#define kClientPollInterval 253
// FORWARD CLASS DEFINES
class MP3ClientSessionRef;
class MP3ClientSession;
class MP3ClientQueue;
class MP3BroadcasterSession;
class MP3BroadcasterQueue;
class MP3SessionRef;
class MP3SessionRefKey;
class MP3SessionTable;
enum {
kMP3UndefinedSessionType = 0,
kMP3BroadcasterSessionType,
kMP3ClientSessionType
};
//
// MP3Session -- This is a base class to hold all the MP3 Session state info.
// We will subclass it for MP3 Broadcaster sessions and MP3 Client sessions.
// (See the MP3BroadcasterSession & MP3ClientSession classes defined below.)
//
class MP3Session
{
public:
MP3Session();
MP3Session(QTSS_RTSPSessionObject sess, QTSS_StreamRef stream);
virtual ~MP3Session();
// The IsA() method is a mechanism for subclasses of this class to
// identify their type. It is kMP3UndefinedSessionType for this
// base class.
virtual UInt8 IsA() const;
// Session data field accessor methods
QTSS_RTSPSessionObject GetSession() { return fSession; }
void SetSessionID(UInt32 sessID) { fSessID = sessID; }
UInt32 GetSessionID() { return fSessID; }
QTSS_StreamRef GetStreamRef() { return fStream; }
UInt16 GetState() { return fState; }
void SetState(UInt16 state) { fState = state; }
UInt16 GetResult() { return fResult; }
void SetResult(UInt16 result) { fResult = result; }
static SInt32 GetTotalNumMP3Sessions() { return sTotalNumMP3Sessions; }
protected:
void SetSession(QTSS_RTSPSessionObject session) { fSession = session; }
void SetStreamRef(QTSS_StreamRef stream) { fStream = stream; }
private:
static SInt32 sTotalNumMP3Sessions;
UInt16 fState;
UInt16 fResult;
QTSS_StreamRef fStream;
QTSS_RTSPSessionObject fSession;
UInt32 fSessID;
};
//
// MP3BroadcasterSession -- This is a class to hold all the MP3 Broadcaster
// session-related state info. There is a global queue of these managed by the
// server. Each instance of this class must have a unique mountpoint name
// string which clients will use to identify the broadcast stream they are
// "tuning" into.
//
class MP3BroadcasterSession : public MP3Session
{
public:
// MP3BroadcasterSession states
enum {
kBroadcasterInitState = 0,
kBroadcasterValidatePasswordState,
kBroadcasterGetHeaderState,
kBroadcasterRecvDataState,
kBroadcasterShutDownState
};
MP3BroadcasterSession(QTSS_RTSPSessionObject sess, QTSS_StreamRef stream);
virtual ~MP3BroadcasterSession();
// The IsA() method is a mechanism for subclasses of the MP3Session class
// to identify their type. It is kMP3BroadcasterSessionType for this
// subclass.
virtual UInt8 IsA() const;
// Session state control methods
QTSS_Error ExecuteState();
void InitBroadcastSessionState();
void AcceptBroadcastSessionState();
void AcceptPasswordState();
void AcceptDataStreamState();
void ShutDownState();
// MP3 Client handling methods
QTSS_Error AddClient(QTSS_RTSPSessionObject sess, QTSS_StreamRef stream);
QTSS_Error RemoveClient(QTSS_RTSPSessionObject sess);
Bool16 IsMyClient(QTSS_RTSPSessionObject sess);
// Session data field accessor methods
void SetMountpoint(char* mp);
void SetMountpoint(StrPtrLen& mp);
Bool16 MountpointEqual(char* mp);
Bool16 MountpointEqual(StrPtrLen& mp);
char* GetMountpoint() { return fMountpoint; }
char* GetHeader() { return fHeader; }
void SetSongName(char* sn);
char* GetSongName() { return fSongName; }
// Session data handlers
QTSS_Error SendOKResponse();
QTSS_Error GetBroadcastHeaders();
QTSS_Error GetBroadcastData();
QTSS_Error SendClientsData();
void PreflightClients();
protected:
void TerminateHeaders();
private:
MP3BroadcasterSession();
MP3ClientQueue* fMP3ClientQueue;
UInt32 fDataBufferLen;
UInt32 fDataBufferSize;
char fMountpoint[kURLBufferSize];
char fHeader[kHeaderBufferSize];
char fSongName[kSongNameBufferSize];
char* fBuffer;
OSMutex fSongNameMutex;
Bool16 fNewSongName;
};
//
// MP3ClientSession -- This is a class to hold all the MP3 client
// session-related state info. These class instances are always owned by a
// instance of the MP3BroadcasterSession class which has a queue of these.
//
class MP3ClientSession : public MP3Session
{
public:
// MP3ClientSession states
enum {
kClientInitState = 0,
kClientSendResponse,
kClientSendDataState,
kClientShutDownState
};
MP3ClientSession(QTSS_RTSPSessionObject sess,
QTSS_StreamRef stream,
MP3BroadcasterSession* owner);
virtual ~MP3ClientSession();
// The IsA() method is a mechanism for subclasses of the MP3Session class
// to identify their type. It is kMP3ClientSessionType for this subclass.
virtual UInt8 IsA() const;
// Session stream handlers
QTSS_Error SendResponse();
QTSS_Error SendMP3Data(char* buffer, UInt32 bufferlen);
QTSS_Error RetrySendData();
QTSS_Error SendMetaData();
// Session data field accessor methods
void SetHeader(char* header);
char* GetHeader() { return fHeader; }
char* GetHostName() { return fHostName; }
char* GetUserAgent() { return fUserAgent; }
void SetSongName(char* sn);
char* GetSongName() { return fSongName; }
MP3BroadcasterSession* GetOwner() { return fOwner; }
void SetOwner(MP3BroadcasterSession* owner) { fOwner = owner; }
Bool16 WasBlocked() { return fWasBlocked; }
Bool16 WantsContentLength() { return fNeedsContentLength; }
void SetRequest(char* req) { ::strcpy(fRequestBuffer, req); }
char* GetRequest() { return fRequestBuffer; }
UInt32 GetTotalCount() { return fBytesSent; }
SInt64 GetConnectTime() { return fConnectTime; }
private:
MP3ClientSession();
void UpdateBitRateInternal(const SInt64& curTime);
void ParseRequestParams(QTSS_StreamRef stream);
UInt32 fBytesSent;
UInt32 fCurrentBitRate;
UInt32 fLastBitRateBytes;
SInt64 fLastBitRateUpdateTime;
SInt64 fConnectTime;
UInt32 fSendCount;
UInt32 fRetryCount;
MP3BroadcasterSession* fOwner;
char fHeader[kHeaderBufferSize];
char fHostName[kHostNameBufferSize];
char fUserAgent[kUserAgentBufferSize];
char fSongName[kSongNameBufferSize];
OSMutex fSongNameMutex;
Bool16 fNewSongName;
Bool16 fWasBlocked;
SInt64 fBlockTime;
Bool16 fNeedsContentLength;
Bool16 fWantsMetaData;
char fRequestBuffer[kRequestBufferSize];
QTSS_Object fQTSSObject;
};
//
// MP3SessionRef -- This class is just a wrapper class for handling the
// mapping of RTSP Session refs to the corresponding MP3 Session class refs.
// It will be the an element of our hash table in the MP3SessionTable class.
//
class MP3SessionRef
{
public:
MP3SessionRef(MP3Session* mp3Session);
~MP3SessionRef();
private:
MP3Session* GetMP3Session() { return fMP3Session; }
QTSS_RTSPSessionObject GetSession() { return fMP3Session->GetSession(); }
MP3SessionRef* fNextHashEntry;
PointerSizedInt fHashValue;
MP3Session* fMP3Session;
friend class MP3SessionRefKey;
friend class OSHashTable<MP3SessionRef, MP3SessionRefKey>;
friend class OSHashTableIter<MP3SessionRef, MP3SessionRefKey>;
friend class MP3SessionTable;
};
//
// MP3SessionRefKey -- This class is used to generate hash keys for looking
// up values in our MP3SessionTable.
//
class MP3SessionRefKey
{
public:
MP3SessionRefKey(MP3SessionRef* mp3SessRef);
MP3SessionRefKey(QTSS_RTSPSessionObject rtspSessRef);
~MP3SessionRefKey();
private:
PointerSizedInt GetHashKey() { return fHashValue; }
friend int operator ==(const MP3SessionRefKey &key1, const MP3SessionRefKey &key2)
{
return (key1.fHashValue == key2.fHashValue);
}
MP3SessionRef* fKeyValue;
PointerSizedInt fHashValue;
MP3Session* fMP3Session;
friend class OSHashTable<MP3SessionRef, MP3SessionRefKey>;
};
typedef OSHashTable<MP3SessionRef, MP3SessionRefKey> MP3SessRefHashTable;
typedef OSHashTableIter<MP3SessionRef, MP3SessionRefKey> MP3SessRefHashTableIter;
//
// MP3SessionTable -- This class provides a way to map RTSP Session references
// into the corresponding MP3Session class instances if any.
//
class MP3SessionTable
{
public:
enum
{
kDefaultTableSize = 19
};
// tableSize is the number of hash buckets not the number of entries.
MP3SessionTable(UInt32 tableSize = kDefaultTableSize);
~MP3SessionTable();
UInt32 GetNumSessionsInTable() { return (UInt32) fTable.GetNumEntries(); }
// Attempt to add a new MP3Session ref to the table's map.
// returns true on success or false if it fails.
Bool16 RegisterSession(MP3Session* session);
// Given an QTSS_RTSPSessionObject resolve it into a MP3Session class
// reference. Returns NULL if there's none in out map.
MP3Session* Resolve(QTSS_RTSPSessionObject rtspSession);
// Attempt to remove a MP3Session ref from the table's map.
// returns true on success or false if it fails.
Bool16 UnRegisterSession(MP3Session* session);
private:
MP3SessRefHashTable fTable;
OSMutex fMutex;
};
//
// MP3ClientQueue -- This is a class manages a queue of MP3Client objects.
// Every MP3BroadcasterSession class instance contains one of these objects.
//
class MP3ClientQueue
{
public:
MP3ClientQueue();
~MP3ClientQueue();
UInt32 GetNumClients() { return fQueue.GetLength(); }
QTSS_Error AddClient(QTSS_RTSPSessionObject clientsess, QTSS_StreamRef clientstream,
MP3BroadcasterSession* owner);
QTSS_Error RemoveClient(QTSS_RTSPSessionObject clientsess);
Bool16 InQueue(QTSS_RTSPSessionObject clientsess);
QTSS_Error SendToAllClients(char* buffer, UInt32 bufferlen);
void PreflightClients(char* songname);
private:
void TerminateClients();
OSMutex fMutex;
OSQueue fQueue;
};
//
// MP3BroadcasterQueue -- This is a class manages a queue of MP3Broadcaster objects.
// There is only one global instance of this class owned by the server.
//
class MP3BroadcasterQueue
{
public:
MP3BroadcasterQueue();
~MP3BroadcasterQueue();
UInt32 GetNumBroadcasters() { return fQueue.GetLength(); }
QTSS_Error CreateBroadcaster(QTSS_RTSPSessionObject bcastsess, QTSS_StreamRef bcaststream, StrPtrLen& mountpt);
QTSS_Error RemoveBroadcaster(QTSS_RTSPSessionObject bcastsess);
QTSS_Error RemoveClient(QTSS_RTSPSessionObject clientsess);
Bool16 InQueue(QTSS_RTSPSessionObject bcastsess);
Bool16 IsActiveClient(QTSS_RTSPSessionObject clientsess);
MP3BroadcasterSession* FindByMountPoint(char* mountpoint);
MP3BroadcasterSession* FindByMountPoint(StrPtrLen& mountpoint);
MP3BroadcasterSession* FindBySession(QTSS_RTSPSessionObject bcastsess);
void TerminateAllBroadcastSessions();
private:
OSMutex fMutex;
OSQueue fQueue;
};
#endif // __QTSSMP3STREAMINGMODULE_H__

View file

@ -0,0 +1,247 @@
/*
*
* @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: QTSSPosixFileSysModule.cpp
Contains: Implements web stats module
Written By: Denis Serenyi
*/
#include "QTSSPosixFileSysModule.h"
#include "QTSSModuleUtils.h"
#include "OSMemory.h"
#include "OSFileSource.h"
#include "Socket.h"
// ATTRIBUTES
static QTSS_AttributeID sOSFileSourceAttr = qtssIllegalAttrID;
static QTSS_AttributeID sEventContextAttr = qtssIllegalAttrID;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSPosixFileSysModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error OpenFile(QTSS_OpenFile_Params* inParams);
static QTSS_Error AdviseFile(QTSS_AdviseFile_Params* inParams);
static QTSS_Error ReadFile(QTSS_ReadFile_Params* inParams);
static QTSS_Error CloseFile(QTSS_CloseFile_Params* inParams);
static QTSS_Error RequestEventFile(QTSS_RequestEventFile_Params* inParams);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSPosixFileSysModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSPosixFileSysModuleDispatch);
}
QTSS_Error QTSSPosixFileSysModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_OpenFile_Role:
return OpenFile(&inParams->openFileParams);
case QTSS_AdviseFile_Role:
return AdviseFile(&inParams->adviseFileParams);
case QTSS_ReadFile_Role:
return ReadFile(&inParams->readFileParams);
case QTSS_CloseFile_Role:
return CloseFile(&inParams->closeFileParams);
case QTSS_RequestEventFile_Role:
return RequestEventFile(&inParams->reqEventFileParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
// Only open needs to be registered for, the rest happen naturally
(void)QTSS_AddRole(QTSS_OpenFile_Role);
// Add an attribute to the file object type to store a pointer to our OSFileSource
static char* sOSFileSourceName = "QTSSPosixFileSysModuleOSFileSource";
(void)QTSS_AddStaticAttribute(qtssFileObjectType, sOSFileSourceName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssFileObjectType, sOSFileSourceName, &sOSFileSourceAttr);
static char* sEventContextName = "QTSSPosixFileSysModuleEventContext";
(void)QTSS_AddStaticAttribute(qtssFileObjectType, sEventContextName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssFileObjectType, sEventContextName, &sEventContextAttr);
// Tell the server our name!
static char* sModuleName = "QTSSPosixFileSysModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
return QTSS_NoErr;
}
QTSS_Error OpenFile(QTSS_OpenFile_Params* inParams)
{
OSFileSource* theFileSource = NEW OSFileSource(inParams->inPath);
UInt64 theLength = theFileSource->GetLength();
//
// OSFileSource returns mod date as a time_t.
// This is the same as a QTSS_TimeVal, except the latter is in msec
QTSS_TimeVal theModDate = (QTSS_TimeVal)theFileSource->GetModDate();
theModDate *= 1000;
//
// Check to see if the file actually exists
if (theLength == 0)
{
delete theFileSource;
return QTSS_FileNotFound;
}
//
// Add this new file source object to the file object
QTSS_Error theErr = QTSS_SetValue(inParams->inFileObject, sOSFileSourceAttr, 0, &theFileSource, sizeof(theFileSource));
if (theErr != QTSS_NoErr)
{
delete theFileSource;
return QTSS_RequestFailed;
}
//
// If caller wants async I/O, at this point we should set up the EventContext
if (inParams->inFlags & qtssOpenFileAsync)
{
EventContext* theEventContext = NEW EventContext(EventContext::kInvalidFileDesc, Socket::GetEventThread());
theEventContext->InitNonBlocking(theFileSource->GetFD());
theErr = QTSS_SetValue(inParams->inFileObject, sEventContextAttr, 0, &theEventContext, sizeof(theEventContext));
if (theErr != QTSS_NoErr)
{
delete theFileSource;
delete theEventContext;
return QTSS_RequestFailed;
}
}
//
// Set up the other attribute values in the file object
(void)QTSS_SetValue(inParams->inFileObject, qtssFlObjLength, 0, &theLength, sizeof(theLength));
(void)QTSS_SetValue(inParams->inFileObject, qtssFlObjModDate, 0, &theModDate, sizeof(theModDate));
return QTSS_NoErr;
}
QTSS_Error AdviseFile(QTSS_AdviseFile_Params* inParams)
{
OSFileSource** theFile = NULL;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(inParams->inFileObject, sOSFileSourceAttr, 0, (void**)&theFile, &theLen);
Assert(theLen == sizeof(OSFileSource*));
(*theFile)->Advise(inParams->inPosition, inParams->inSize);
return QTSS_NoErr;
}
QTSS_Error ReadFile(QTSS_ReadFile_Params* inParams)
{
OSFileSource** theFile = NULL;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(inParams->inFileObject, sOSFileSourceAttr, 0, (void**)&theFile, &theLen);
Assert(theLen == sizeof(OSFileSource*));
OS_Error osErr = (*theFile)->Read(inParams->inFilePosition, inParams->ioBuffer, inParams->inBufLen, inParams->outLenRead);
if (osErr == EAGAIN)
return QTSS_WouldBlock;
else if (osErr != OS_NoErr)
return QTSS_RequestFailed;
else
return QTSS_NoErr;
}
QTSS_Error CloseFile(QTSS_CloseFile_Params* inParams)
{
OSFileSource** theFile = NULL;
EventContext** theContext = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inFileObject, sOSFileSourceAttr, 0, (void**)&theFile, &theLen);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_GetValuePtr(inParams->inFileObject, sEventContextAttr, 0, (void**)&theContext, &theLen);
if (theErr == QTSS_NoErr)
{
//
// If this file was opened up in async mode, there is an EventContext associated with it.
// We should first make sure that the OSFileSource destructor doesn't close the FD
// (the EventContext will do it), then close the EventContext
(*theFile)->ResetFD();
delete *theContext;
}
delete *theFile;
return QTSS_NoErr;
}
QTSS_Error RequestEventFile(QTSS_RequestEventFile_Params* inParams)
{
QTSS_ModuleState* theState = (QTSS_ModuleState*)OSThread::GetMainThreadData();
if (OSThread::GetCurrent() != NULL)
theState = (QTSS_ModuleState*)OSThread::GetCurrent()->GetThreadData();
Assert(theState->curTask != NULL);
EventContext** theContext = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inFileObject, sEventContextAttr, 0, (void**)&theContext, &theLen);
if (theErr == QTSS_NoErr)
{
//
// This call only works if the file is async!
(*theContext)->SetTask(theState->curTask);
(*theContext)->RequestEvent();
return QTSS_NoErr;
}
return QTSS_RequestFailed;
}

View file

@ -0,0 +1,50 @@
/*
*
* @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: QTSSPosixFileSysModule.h
Contains:
Written By: Denis Serenyi
*/
#ifndef __QTSSPOSIXFILESYSMODULE_H__
#define __QTSSPOSIXFILESYSMODULE_H__
#include "QTSS.h"
#include "QTSS_Private.h" // This module MUST be compiled directly into the server!
// This is because it uses the server's private internal event
// mechanism for doing async file I/O
extern "C"
{
EXPORT QTSS_Error QTSSPosixFileSysModule_Main(void* inPrivateArgs);
}
#endif // __QTSSPOSIXFILESYSMODULE_H__

View file

@ -0,0 +1,939 @@
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @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: QTSSProxyModule.cpp
Contains: Implementation of QTSSProxyModule class.
This module is intended to be used in a stripped down version of QTSS/DSS
in order to handle firewall proxy behaviour for the RTP protocol.
*/
#ifndef __Win32__
//
// For gethostbyname
#include <netdb.h>
#endif
#include "QTSSProxyModule.h"
#include "QTSSModuleUtils.h"
#include "UDPSocketPool.h"
#include "OSArrayObjectDeleter.h"
#include "OSMemory.h"
#include "RTSPClient.h"
#include "ClientSocket.h"
#include "SocketUtils.h"
class ProxyTask : public Task
{
public:
//
// Task that just polls on all the sockets in the pool, sending data on all available sockets
ProxyTask() : Task() {this->SetTaskName("ProxyTask"); this->Signal(Task::kStartEvent); }
virtual ~ProxyTask() {}
private:
virtual SInt64 Run();
enum
{
kProxyTaskPollIntervalMsec = 10
};
};
class ProxySocketPool : public UDPSocketPool
{
public:
//
// Global pool of UDP sockets used by the Proxy for receiving
// UDP data from the upstream servers.
ProxySocketPool() {}
virtual ~ProxySocketPool() {}
virtual UDPSocketPair* ConstructUDPSocketPair();
virtual void DestructUDPSocketPair(UDPSocketPair *inPair);
virtual void SetUDPSocketOptions(UDPSocketPair* inPair);
};
class ProxyDemuxerTask : public UDPDemuxerTask
{
public:
ProxyDemuxerTask(QTSS_RTPStreamObject inStream, UDPSocketPair* inSockets)
: UDPDemuxerTask(),
fStream(inStream),
fQueueElem(this),
fSockets(inSockets) {}
virtual ~ProxyDemuxerTask() {}
//
// ACCESSORS
QTSS_RTPStreamObject GetStream() { return fStream; }
OSQueueElem* GetQueueElem() { return &fQueueElem; }
UDPSocketPair* GetSockets() { return fSockets; }
UInt16 GetOriginServerPort() { return fOriginServerPort; }
//
// SetOriginServerPort -
// The origin server tells us what ports to send to on the SETUP
// response. This object gets created on the SETUP request, so we
// don't know the origin server ports when it is constructed. Once
// we know what they are, call this method to let the object know what the ports are
void SetOriginServerPort(UInt16 inPort) { fOriginServerPort = inPort; }
private:
QTSS_RTPStreamObject fStream;
OSQueueElem fQueueElem;
UDPSocketPair* fSockets;
UInt16 fOriginServerPort;
};
class ProxyClientInfo
{
public:
//
// This class contains all the proxy specific resources used by a single client of the
// proxy. It is a UDP demuxer because it needs to get signalled when there is incoming
// UDP data for this client
ProxyClientInfo() : fStream(NULL),
fClientSocket(Socket::kNonBlockingSocketType),
fClient(&fClientSocket, false),
fLastDemuxerTask(NULL) {}
~ProxyClientInfo();
//
// There are generally several streams for a single client. Each stream must
// have a different source port from the origin server. These functions
// accomplish this mapping.
ProxyDemuxerTask* AddStream(QTSS_RTPStreamObject inStream);
ProxyDemuxerTask* GetDemuxerTaskForStream(QTSS_RTPStreamObject inStream);
ProxyDemuxerTask* GetLastDemuxerTask() { return fLastDemuxerTask; }
//
// An RTSPClient object that handles the details of communicating with
// the upstream server over RTSP
RTSPClient* GetRTSPClient() { return &fClient; }
//
// A QTSS_SocketStream for doing async I/O in QTSS API with the RTSP socket
QTSS_SocketStream GetSocketStream() { return fStream; }
void SetSocketStream(QTSS_SocketStream inStream) { fStream = inStream; }
private:
//
// All the resources needed by 1 client of the proxy stored here, so this all can be
// stuffed into a ClientSession
QTSS_SocketStream fStream;
TCPClientSocket fClientSocket;
RTSPClient fClient;
ProxyDemuxerTask* fLastDemuxerTask;
OSQueue fDemuxerTaskQueue;
OSRef fRef;
};
// ATTRIBUTES
static QTSS_AttributeID sProxyClientInfoAttr = qtssIllegalAttrID;
static QTSS_AttributeID sPortNumberTooBigErr = qtssIllegalAttrID;
static QTSS_AttributeID sRemoteHostRefusedConnectionErr = qtssIllegalAttrID;
static QTSS_AttributeID sNoTransportHeaderInSetupErr = qtssIllegalAttrID;
// STATIC DATA
static QTSS_PrefsObject sServerPrefs = NULL;
static ProxySocketPool* sSocketPool = NULL;
static ProxyTask* sProxyTask = NULL;
static OSRefTable* sSessionMap = NULL;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSProxyModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams);
static QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error ProcessIncomingRTCPPacket(QTSS_RTCPProcess_Params* inParams);
static QTSS_Error HandleRTSPClientErr(QTSS_RTSPRequestObject inRequest, ProxyClientInfo* inClient, QTSS_Error inErr);
static void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask);
static QTSS_Error DoRequestPreProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest, QTSS_ClientSessionObject inSession);
static QTSS_Error DoRequestPostProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inClientSession);
static UInt32 ReplaceSessionAndTransportHeaders(StrPtrLen* inResponse, iovec* outNewResponse, StrPtrLen* inNewSessionID,
StrPtrLen* inNewTransportHeader, UInt32* outNewResponseLen);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSProxyModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSProxyModuleDispatch);
}
QTSS_Error QTSSProxyModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RTSPRequest_Role:
return ProcessRTSPRequest(&inParams->rtspRequestParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession(&inParams->clientSessionClosingParams);
case QTSS_RTCPProcess_Role:
return ProcessIncomingRTCPPacket(&inParams->rtcpProcessParams);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_Shutdown_Role);
(void)QTSS_AddRole(QTSS_RTSPRequest_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
(void)QTSS_AddRole(QTSS_RTCPProcess_Role);
// Tell the server our name!
static char* sModuleName = "QTSSProxyModule";
::strcpy(inParams->outModuleName, sModuleName);
static char* sProxyClientInfoName = "QTSSProxyModuleProxyClientInfo";
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sProxyClientInfoName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sProxyClientInfoName, &sProxyClientInfoAttr);
static char* sPortNumberToBigErrName = "QTSSProxyModulePortNumberTooBig";
static char* sRemoteHostRefusedConnectionErrName = "QTSSProxyModuleRemoteHostRefusedConnection";
static char* sNoTransportHeaderInSetupErrName = "QTSSProxyModuleNoTransportHeaderInSetup";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sPortNumberToBigErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sPortNumberToBigErrName, &sPortNumberTooBigErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionErrName, &sRemoteHostRefusedConnectionErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sNoTransportHeaderInSetupErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sNoTransportHeaderInSetupErrName, &sNoTransportHeaderInSetupErr);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
//
// Setup global data structures
sServerPrefs = inParams->inPrefs;
sSocketPool = NEW ProxySocketPool();
sProxyTask = NEW ProxyTask();
sSessionMap = NEW OSRefTable();
// Report to the server that this module handles DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN
static QTSS_RTSPMethod sSupportedMethods[] = { qtssDescribeMethod, qtssSetupMethod, qtssTeardownMethod, qtssPlayMethod, qtssPauseMethod };
QTSSModuleUtils::SetupSupportedMethods(inParams->inServer, sSupportedMethods, 5);
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
{
ProxyClientInfo* theClient = NULL;
UInt32 theLen = sizeof(theClient);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sProxyClientInfoAttr, 0,
&theClient, &theLen);
if (theErr != QTSS_NoErr)
return theErr;
delete theClient;
//
// NULL out the attribute so in case there is a race condition no one will
// get this dangling pointer
(void)QTSS_SetValue(inParams->inClientSession, sProxyClientInfoAttr, 0, NULL, 0);
return QTSS_NoErr;
}
QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
ProxyClientInfo* theClient = NULL;
UInt32 theLen = sizeof(theClient);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sProxyClientInfoAttr, 0,
&theClient, &theLen);
//
// If there is no client yet, this is the first request made on this session.
// Create an RTSPClient
if (theErr != QTSS_NoErr)
{
theClient = NEW ProxyClientInfo();
//
// Parse out the DNS name of the origin server and the port
StrPtrLen theDNSNameAndPort;
theErr = QTSS_GetValuePtr(inParams->inRTSPHeaders, qtssHostHeader, 0, (void**)&theDNSNameAndPort.Ptr, &theDNSNameAndPort.Len);
Assert(theErr == QTSS_NoErr);
StringParser extractPortNumber(&theDNSNameAndPort);
StrPtrLen theDNSNamePtr;
extractPortNumber.GetThru(&theDNSNamePtr, ':');
UInt32 thePort = extractPortNumber.ConsumeInteger(NULL);
//
// For now, if there was no port specified, use 554
if (thePort == 0)
thePort = 554;
//
// Make sure the port is in the range of a 2-byte integer
if (thePort > 65536)
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,
sPortNumberTooBigErr);
//
// gethostbyname takes a NULL terminated C-string
OSCharArrayDeleter theDNSName(theDNSNamePtr.GetAsCString());
//
// Do a DNS lookup on the host to find its IP address
struct hostent* theHostent = ::gethostbyname(theDNSName);
UInt32 theIPAddr = 0;
if (theHostent != NULL)
theIPAddr = ntohl(*(UInt32*)(theHostent->h_addr_list[0]));
else
theIPAddr = SocketUtils::ConvertStringToAddr(theDNSName);
//
// Give this information to the ClientSocket object
theClient->GetRTSPClient()->GetSocket()->Set(theIPAddr, (UInt16)thePort);
theErr = QTSS_SetValue(inParams->inClientSession, sProxyClientInfoAttr, 0, &theClient, sizeof(theClient));
Assert(theErr == QTSS_NoErr);
//
// Start the process of connecting to the origin server, first
// just by telling the ClientSocket to send an empty message
theErr = theClient->GetRTSPClient()->GetSocket()->Send(NULL, 0);
if (theErr != QTSS_NoErr)
//
// we are connecting. This function will be called when connection is set up
return HandleRTSPClientErr(inParams->inRTSPRequest, theClient, theErr);
}
//
// If we aren't in the middle of sending a request, we have to set it up
if (!theClient->GetRTSPClient()->IsTransactionInProgress())
theErr = DoRequestPreProcessing(theClient, inParams->inRTSPRequest, inParams->inClientSession);
else
//
// Continue our attempt to send this request
theErr = theClient->GetRTSPClient()->DoTransaction();
if (theErr != QTSS_NoErr)
HandleRTSPClientErr(inParams->inRTSPRequest, theClient, theErr);
else
//
// The response has come back from the origin server. Do whatever
// processing we need on it, and send the response to the proxy client.
theErr = DoRequestPostProcessing(theClient, inParams->inRTSPRequest, inParams->inClientSession);
return theErr;
}
QTSS_Error HandleRTSPClientErr(QTSS_RTSPRequestObject inRequest, ProxyClientInfo* inClient, QTSS_Error inErr)
{
if (inClient->GetSocketStream() == NULL)
{
QTSS_SocketStream theSockStream = NULL;
// Create a socket stream for the TCP socket in the RTSPClient object. The socket stream will
// allow this module to receive events on the socket
QTSS_Error theErr = QTSS_CreateStreamFromSocket(inClient->GetRTSPClient()->GetSocket()->GetSocket()->GetSocketFD(), &theSockStream);
Assert(theErr == QTSS_NoErr);
Assert(theSockStream != NULL);
//
// Cache it for future use
inClient->SetSocketStream(theSockStream);
}
if ((inErr == EAGAIN) || (inErr == EINPROGRESS))
{
//
// Still not done. Return back to the server and wait for an event
// We're making an assumption here that inClient only uses one socket to connect to
// the server. We only have one stream, so we have to make that assumption.
// Note that it is not necessary to have any kind of timeout here, because the server
// naturally times out idle connections. If the server doesn't respond for awhile,
// this session will naturally go away
inClient->GetRTSPClient()->GetSocket()->GetSocket()->DontAutoCleanup();
RequestSocketEvent(inClient->GetSocketStream(), inClient->GetRTSPClient()->GetSocket()->GetEventMask());
return QTSS_NoErr; // We'll get called in the same method again when there is more work to do
}
if (inErr == QTSS_RequestFailed)
{
//
// The remote host responded with a non 200 response. Forward it onto
// the proxy client as normal, let it figure out what to do.
//
// Or this might just be that we got back an error from QTSS_AddRTPStream,
// in which case we don't need to do anything special.
return QTSS_NoErr;
}
else
return QTSSModuleUtils::SendErrorResponse(inRequest, qtssServerGatewayTimeout,
sRemoteHostRefusedConnectionErr);
}
void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask)
{
//
// Job of this function is to convert a CommonUtilitiesLib event mask to a QTSS Event mask
QTSS_EventType theEvent = 0;
if (inEventMask & EV_RE)
theEvent |= QTSS_ReadableEvent;
if (inEventMask & EV_WR)
theEvent |= QTSS_WriteableEvent;
QTSS_Error theErr = QTSS_RequestEvent(inStream, theEvent);
Assert(theErr == QTSS_NoErr);
}
QTSS_Error DoRequestPreProcessing(ProxyClientInfo* inProxyClientInfo,
QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inSession)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqMethod, 0,
(void**)&theMethod, &theLen);
Assert(theErr == QTSS_NoErr);
char theTransportHeaderBuf[128];
StrPtrLen theTransportHeader(theTransportHeaderBuf, 0);
//
// The session ID passed to us is this server's session ID. Make sure to replace it
// with the session ID passed to us by the upstream server, or else the upstream
// server will not be able to properly handle this request.
StrPtrLen* theProxyClientSessionIDPtr = inProxyClientInfo->GetRTSPClient()->GetSessionID();
//
// Truncate the \r\n at the end of this
StrPtrLen theProxyClientSessionID(*theProxyClientSessionIDPtr);
if (theProxyClientSessionID.Len > 0)
{
Assert(theProxyClientSessionID.Len >= 2);
//
// Truncate off the EOL
if ((theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\r') ||
(theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\n'))
theProxyClientSessionID.Len--;
if ((theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\r') ||
(theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\n'))
theProxyClientSessionID.Len--;
//
// Truncate off "Session: "
Assert(theProxyClientSessionID.Len > 9);
theProxyClientSessionID.Ptr += 9;
theProxyClientSessionID.Len -= 9;
}
StrPtrLen theRequest;
theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqFullRequest, 0,
(void**)&theRequest.Ptr, &theRequest.Len);
Assert(theErr == QTSS_NoErr);
if (*theMethod == qtssSetupMethod)
{
//
// If this is a SETUP, we need to do some special processing,
// and rewrite the Transport header before sending it on.
//
// First, tell the server to setup a new QTSS_RTPStreamObject
// The proxy only supports UDP transport right now, so make sure that that is what we get
QTSS_RTPStreamObject theStream = NULL;
theErr = QTSS_AddRTPStream(inSession, inRequest, &theStream, qtssASFlagsForceUDPTransport);
if (theErr != QTSS_NoErr)
{
Assert(theErr == QTSS_RequestFailed); // if any other error is returned,
// our error handling logic will get confused.
return theErr;
}
//
// Tell the ProxyClientInfo object about this new stream.
// This will also allocate client ports on this proxy.
ProxyDemuxerTask* theProxyDemuxerTask = inProxyClientInfo->AddStream(theStream);
//
// Build a new Transport header
UInt16 theRTPPort = theProxyDemuxerTask->GetSockets()->GetSocketA()->GetLocalPort();
qtss_sprintf(theTransportHeaderBuf, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", theRTPPort, theRTPPort + 1);
theTransportHeader.Len = ::strlen(theTransportHeaderBuf);
}
//
// Build the new request and send it
iovec theNewRequest[5];
UInt32 totalResponseLen = 0;
UInt32 theNumVecs = ReplaceSessionAndTransportHeaders(&theRequest, theNewRequest, &theProxyClientSessionID, &theTransportHeader, &totalResponseLen);
return inProxyClientInfo->GetRTSPClient()->SendRTSPRequest(theNewRequest, theNumVecs);
}
QTSS_Error DoRequestPostProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inClientSession)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqMethod, 0, (void**)&theMethod, &theLen);
Assert(theErr == QTSS_NoErr);
char theTransportHeaderBuf[128];
StrPtrLen theTransportHeader(theTransportHeaderBuf, 0);
StrPtrLen theSessionID;
theErr = QTSS_GetValuePtr(inClientSession, qtssCliSesRTSPSessionID, 0, (void**)&theSessionID.Ptr, &theSessionID.Len);
Assert(theErr == QTSS_NoErr);
StrPtrLen theResponse(inProxyClientInfo->GetRTSPClient()->GetResponse(), inProxyClientInfo->GetRTSPClient()->GetResponseLen());
if ((*theMethod == qtssSetupMethod) &&
(inProxyClientInfo->GetRTSPClient()->GetStatus() == 200))
{
ProxyDemuxerTask* thisStreamsDemuxer = inProxyClientInfo->GetLastDemuxerTask();
thisStreamsDemuxer->SetOriginServerPort(inProxyClientInfo->GetRTSPClient()->GetServerPort());
//
// Build a new Transport header. This should basically be exactly what the
// server would be sending back if the proxy module wasn't generating the response
UInt16 theSvrRTPPort = 0;
UInt16 theCliRTPPort = 0;
UInt32 theLen = sizeof(theSvrRTPPort);
theErr = QTSS_GetValue(thisStreamsDemuxer->GetStream(), qtssRTPStrSvrRTPPort, 0, &theSvrRTPPort, &theLen);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_GetValue(thisStreamsDemuxer->GetStream(), qtssRTPStrClientRTPPort, 0, &theCliRTPPort, &theLen);
Assert(theErr == QTSS_NoErr);
char theIPAddrStr[20];
theLen = 20;
theErr = QTSS_GetValue(inClientSession, qtssCliRTSPSessLocalAddrStr, 0, &theIPAddrStr[0], &theLen);
Assert(theErr == QTSS_NoErr);
theIPAddrStr[theLen] = '\0';
qtss_sprintf(theTransportHeader.Ptr, "Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d;source=%s\r\n",
theCliRTPPort, theCliRTPPort + 1, theSvrRTPPort, theSvrRTPPort + 1, theIPAddrStr);
theTransportHeader.Len = ::strlen(theTransportHeader.Ptr);
}
else if ((*theMethod == qtssPlayMethod) &&
(inProxyClientInfo->GetRTSPClient()->GetStatus() == 200))
{
//
// The client session needs to know that we are entering the playing state.
// Otherwise, when we write RTP packet data to the QTSS_RTPStreamObjects,
// the server won't really be able to figure out what to do with the packets.
theErr = QTSS_Play(inClientSession, inRequest, 0);
if (theErr != QTSS_NoErr)
{
Assert(theErr == QTSS_RequestFailed);
return theErr;
}
}
else if ((*theMethod == qtssPauseMethod) &&
(inProxyClientInfo->GetRTSPClient()->GetStatus() == 200))
{
//
// Same thing as above, just let the server know that we are no longer playing
theErr = QTSS_Pause(inClientSession);
Assert(theErr == QTSS_NoErr);
}
//
// Build our ioVec with the new response
// When building the iovec used for the response, we need to leave the first
// iovec in the array empty, per QTSS API conventions.
iovec theNewResponse[7];
theNewResponse[0].iov_len = 0;
UInt32 totalResponseLen = 0;
//
// Internally, the server keeps an RTSP Session ID for this QTSS_ClientSessionObject.
// We need to replace the one the upstream server has given us with this server's
// RTSP Session ID. Otherwise, when the server receives the next RTSP request,
// this server will not be able to map the RTSP session ID properly.
UInt32 theNumVecs = ReplaceSessionAndTransportHeaders(&theResponse, &theNewResponse[1], &theSessionID, &theTransportHeader, &totalResponseLen);
theNumVecs++; // Take into account that the first one is empty
//
// Also add in the content body of the response if there is one
theNewResponse[theNumVecs].iov_base = inProxyClientInfo->GetRTSPClient()->GetContentBody();
theNewResponse[theNumVecs].iov_len = inProxyClientInfo->GetRTSPClient()->GetContentLength();
totalResponseLen += theNewResponse[theNumVecs].iov_len;
theNumVecs++;
UInt32 lenWritten = 0;
theErr = QTSS_WriteV(inRequest, theNewResponse, theNumVecs, totalResponseLen, &lenWritten);
Assert(lenWritten == totalResponseLen);
Assert(theErr == QTSS_NoErr);
return QTSS_NoErr;
}
UInt32 ReplaceSessionAndTransportHeaders(StrPtrLen* inResponse, iovec* outNewResponse, StrPtrLen* inNewSessionID,
StrPtrLen* inNewTransportHeader, UInt32* outNewResponseLen)
{
static StrPtrLen sTransportHeader("Transport");
static StrPtrLen sSessionHeader("Session");
StringParser reqParser(inResponse);
StrPtrLen theHeaderName;
UInt32 curVecIndex = 0;
outNewResponse[0].iov_base = inResponse->Ptr;
*outNewResponseLen = 0;
while (reqParser.GetDataRemaining() > 0)
{
reqParser.ConsumeWord(&theHeaderName);
if (theHeaderName.EqualIgnoreCase(sTransportHeader.Ptr, sTransportHeader.Len))
{
//
// Mark off the length of the last section of the header
outNewResponse[curVecIndex].iov_len = (UInt32) ((PointerSizedUInt) theHeaderName.Ptr) - ((PointerSizedUInt) outNewResponse[curVecIndex].iov_base );
*outNewResponseLen += outNewResponse[curVecIndex].iov_len;
//
// Insert the new Transport header
outNewResponse[curVecIndex+1].iov_base = inNewTransportHeader->Ptr;
outNewResponse[curVecIndex+1].iov_len = inNewTransportHeader->Len;
*outNewResponseLen += inNewTransportHeader->Len;
//
// Move onto the next iovec
curVecIndex+=2;
//
// Mark the start of the next section
reqParser.GetThruEOL(NULL);
outNewResponse[curVecIndex].iov_base = reqParser.GetCurrentPosition();
}
else if (theHeaderName.EqualIgnoreCase(sSessionHeader.Ptr, sSessionHeader.Len))
{
reqParser.ConsumeLength(NULL, 2); //skip over ": "
//
// Mark off the length of the last section of the header
outNewResponse[curVecIndex].iov_len = (UInt32) ((PointerSizedUInt) reqParser.GetCurrentPosition()) - ((PointerSizedUInt) outNewResponse[curVecIndex].iov_base);
*outNewResponseLen += outNewResponse[curVecIndex].iov_len;
//
// Insert new session ID
outNewResponse[curVecIndex+1].iov_base = inNewSessionID->Ptr;
outNewResponse[curVecIndex+1].iov_len = inNewSessionID->Len;
*outNewResponseLen += inNewSessionID->Len;
//
// Move onto the next empty vec
curVecIndex+=2;
//
// Move past the session ID. Be careful, there may be a ';' that we need to look for
StrPtrLen theSessionID;
reqParser.GetThruEOL(&theSessionID);
outNewResponse[curVecIndex].iov_base = &theSessionID.Ptr[theSessionID.Len];
while (theSessionID.Ptr < outNewResponse[curVecIndex].iov_base)
{
if (*theSessionID.Ptr == ';')
{
outNewResponse[curVecIndex].iov_base = theSessionID.Ptr;
break;
}
theSessionID.Ptr++;
}
}
else
reqParser.GetThruEOL(NULL);
}
//
// Finish off the vec by marking the len of the last section
outNewResponse[curVecIndex].iov_len = (UInt32) ((PointerSizedUInt) reqParser.GetCurrentPosition()) - ((PointerSizedUInt) outNewResponse[curVecIndex].iov_base );
//
// And finish off the total length
*outNewResponseLen += outNewResponse[curVecIndex].iov_len;
//
// Return the number of vecs written
return curVecIndex+1;
}
QTSS_Error ProcessIncomingRTCPPacket(QTSS_RTCPProcess_Params* inParams)
{
ProxyClientInfo* theClient = NULL;
UInt32 theLen = sizeof(theClient);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sProxyClientInfoAttr, 0,
&theClient, &theLen);
//
// This role receives ALL RTCP packets. We are only interested in RTCP packets
// sent to proxy sessions. So we figure this out based on whether there
// is a ProxyClientInfo object in the client session
if (theErr != QTSS_NoErr)
return QTSS_NoErr;
//
// Let's forward this RTCP packet to the right upstream server
ProxyDemuxerTask* theTask = theClient->GetDemuxerTaskForStream(inParams->inRTPStream);
Assert(theTask != NULL);
if (theTask == NULL)
return QTSS_NoErr;
//
// Using the RTCP socket (SocketB) of the pair, send the packet to the origin server's
// RTCP port (the port number stored is the RTP port)
(void)theTask->GetSockets()->GetSocketB()->
SendTo(theTask->GetRemoteAddr(), theTask->GetOriginServerPort() + 1, inParams->inRTCPPacketData, inParams->inRTCPPacketDataLen);
return QTSS_NoErr;
}
SInt64 ProxyTask::Run()
{
const UInt32 kMaxRTCPPacketSize = 2048;
char thePacketBuffer[kMaxRTCPPacketSize];
QTSS_PacketStruct thePacketStruct;
thePacketStruct.packetTransmitTime = QTSS_Milliseconds();
thePacketStruct.packetData = thePacketBuffer;
(void)this->GetEvents();
OSMutexLocker locker(sSocketPool->GetMutex());
for (OSQueueIter iter(sSocketPool->GetSocketQueue()); !iter.IsDone(); iter.Next())
{
UInt32 theRemoteAddr = 0;
UInt16 theRemotePort = 0;
UDPSocketPair* thePair = (UDPSocketPair*)iter.GetCurrent()->GetEnclosingObject();
Assert(thePair != NULL);
for (UInt32 x = 0; x < 2; x++)
{
QTSS_WriteFlags theFlags = qtssWriteFlagsNoFlags;
UDPSocket* theSocket = NULL;
if (x == 0)
{
theFlags = qtssWriteFlagsIsRTP;
theSocket = thePair->GetSocketA();
}
else
{
theFlags = qtssWriteFlagsIsRTCP;
theSocket = thePair->GetSocketB();
}
Assert(theSocket->GetDemuxer() != NULL);
OSMutexLocker locker(theSocket->GetDemuxer()->GetMutex());
//get all the outstanding packets for this socket
while (true)
{
UInt32 thePacketLen = 0;
theSocket->RecvFrom(&theRemoteAddr, &theRemotePort, thePacketStruct.packetData,
kMaxRTCPPacketSize, &thePacketLen);
if (thePacketLen == 0)
break;//no more packets on this socket!
ProxyDemuxerTask* theDemuxerTask = (ProxyDemuxerTask*)theSocket->GetDemuxer()->GetTask(theRemoteAddr, 0);
if (theDemuxerTask != NULL)
{
QTSS_RTPStreamObject theStream = theDemuxerTask->GetStream();
(void)QTSS_Write(theStream, &thePacketStruct, thePacketLen, NULL, theFlags);
}
}
}
}
return kProxyTaskPollIntervalMsec;
}
UDPSocketPair* ProxySocketPool::ConstructUDPSocketPair()
{
Assert(sProxyTask != NULL);
//construct a pair of UDP sockets, the lower one for RTP data (outgoing only, no demuxer
//necessary), and one for RTCP data (incoming, so definitely need a demuxer).
//These are nonblocking sockets that DON'T receive events (we are going to poll for data)
return NEW
UDPSocketPair( NEW UDPSocket(sProxyTask, UDPSocket::kWantsDemuxer | Socket::kNonBlockingSocketType),
NEW UDPSocket(sProxyTask, UDPSocket::kWantsDemuxer | Socket::kNonBlockingSocketType));
}
void ProxySocketPool::DestructUDPSocketPair(UDPSocketPair* inPair)
{
delete inPair->GetSocketA();
delete inPair->GetSocketB();
delete inPair;
}
void ProxySocketPool::SetUDPSocketOptions(UDPSocketPair* inPair)
{
#ifdef __Win32__
//
// On Win32, apparently the socket buffer size matters even though this is UDP and being
// used for sending... on UNIX typically the socket buffer size doesn't matter because the
// packet goes right down to the driver. On Win32, unless this is really big, we get packet loss.
inPair->GetSocketA()->SetSocketBufSize(256 * 1024);
inPair->GetSocketB()->SetSocketBufSize(256 * 1024);
#endif
//
// Always set the Rcv buf size for the RTCP sockets. This is important because the
// server is going to be getting many packets on these sockets.
inPair->GetSocketA()->SetSocketRcvBufSize(512 * 1024);
inPair->GetSocketB()->SetSocketRcvBufSize(512 * 1024);
}
ProxyClientInfo::~ProxyClientInfo()
{
UInt32 theHostAddr = fClient.GetSocket()->GetHostAddr();
for (OSQueueIter iter(&fDemuxerTaskQueue); !iter.IsDone(); )
{
OSQueueElem* theElem = iter.GetCurrent();
ProxyDemuxerTask* theTask = (ProxyDemuxerTask*)theElem->GetEnclosingObject();
//
// Move onto the next element becase we are going to be deleting this one
iter.Next();
theElem->Remove(); // Get off the queue before deleting
//
// Clean up
UDPSocketPair* thePair = theTask->GetSockets();
thePair->GetSocketA()->GetDemuxer()->UnregisterTask(theHostAddr, 0, theTask);
thePair->GetSocketB()->GetDemuxer()->UnregisterTask(theHostAddr, 0, theTask);
delete theTask;
sSocketPool->ReleaseUDPSocketPair(thePair);
}
}
ProxyDemuxerTask* ProxyClientInfo::GetDemuxerTaskForStream(QTSS_RTPStreamObject inStream)
{
//
// Iterate through the queue of demuxer tasks, finding the one with
// the stream that matches this input
for (OSQueueIter iter(&fDemuxerTaskQueue); !iter.IsDone(); iter.Next())
{
ProxyDemuxerTask* theTask = (ProxyDemuxerTask*)iter.GetCurrent()->GetEnclosingObject();
if (theTask->GetStream() == inStream)
return theTask;
}
return NULL;
}
ProxyDemuxerTask* ProxyClientInfo::AddStream(QTSS_RTPStreamObject inStream)
{
//
// Allocate some UDP sockets out of our pool to receive the UDP data from
// the server. Demuxing is based on the origin server's (host's) IP addr, so
// pass that to the socket pool so it can properly allocate a pair of UDP sockets.
// We don't know what the remote port is yet (we only find out when we get the
// SETUP response from the origin), so just pass in 0.
UInt32 theHostAddr = fClient.GetSocket()->GetHostAddr();
UInt32 theLocalAddr = fClient.GetSocket()->GetLocalAddr();
UDPSocketPair* thePair = sSocketPool->GetUDPSocketPair(theLocalAddr, 0, theHostAddr, 0);
fLastDemuxerTask = NEW ProxyDemuxerTask(inStream, thePair);
fDemuxerTaskQueue.EnQueue(fLastDemuxerTask->GetQueueElem());
//
// Tell the demuxers for these sockets to send any packets from this IP addr
// to the ProxyDemuxerTask for this stream. This is how incoming UDP packets
// will be routed to the proper QTSS_RTPStreamObject
thePair->GetSocketA()->GetDemuxer()->RegisterTask(theHostAddr, 0, fLastDemuxerTask);
thePair->GetSocketB()->GetDemuxer()->RegisterTask(theHostAddr, 0, fLastDemuxerTask);
//
// return the newly created ProxyDemuxerTask
return fLastDemuxerTask;
}

View file

@ -0,0 +1,43 @@
/*
*
* @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: QTSSProxyModule.h
Contains: QTSS API module
*/
#ifndef _QTSSPROXYMODULE_H_
#define _QTSSPROXYMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSProxyModule_Main(void* inPrivateArgs);
}
#endif //_QTSSPROXYMODULE_H_

View file

@ -0,0 +1,780 @@
/*
*
* @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: QTSSFileModule.cpp
Contains: Implementation of module described in QTSSFileModule.h.
*/
#include <string.h>
#include "QTSSRTPFileModule.h"
#include "RTPFileSession.h"
#include "OSMemory.h"
#include "OSArrayObjectDeleter.h"
#include "QTSSModuleUtils.h"
#include "StringFormatter.h"
#include "SDPSourceInfo.h"
#include "QTSSMemoryDeleter.h"
#include "QTSS.h"
struct FileSession
{
public:
FileSession() : fAdjustedPlayTime(0), fNextPacketLen(0),
fStream(NULL), fSpeed(1), fStartTime(0), fStopTime(-1)
{}
RTPFileSession fFile;
SInt64 fAdjustedPlayTime;
QTSS_PacketStruct fPacketStruct;
UInt32 fNextPacketLen;
QTSS_RTPStreamObject fStream;
Float32 fSpeed;
Float64 fStartTime;
Float64 fStopTime;
};
// ref to the prefs dictionary object
static QTSS_ModulePrefsObject sFileModulePrefs;
static StrPtrLen sRTPSuffix(".rtp");
static StrPtrLen sSDPHeader1("v=0\r\ns=");
static StrPtrLen sSDPHeader2;
static StrPtrLen sSDPHeader3("c=IN IP4 ");
static StrPtrLen sSDPHeader4("\r\na=control:/\r\n");
static const StrPtrLen kCacheControlHeader("must-revalidate");
// ATTRIBUTES IDs
static QTSS_AttributeID sFileSessionAttr = qtssIllegalAttrID;
static QTSS_AttributeID sSeekToNonexistentTimeErr = qtssIllegalAttrID;
static QTSS_AttributeID sBadQTFileErr = qtssIllegalAttrID;
static QTSS_AttributeID sExpectedDigitFilenameErr = qtssIllegalAttrID;
static QTSS_AttributeID sTrackDoesntExistErr = qtssIllegalAttrID;
// OTHER DATA
static UInt32 sFlowControlProbeInterval = 50;
static UInt32 sDefaultFlowControlProbeInterval= 50;
static Float32 sMaxAllowedSpeed = 4;
static Float32 sDefaultMaxAllowedSpeed = 4;
// FUNCTIONS
static QTSS_Error QTSSRTPFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParamBlock);
static QTSS_Error RereadPrefs();
static QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParamBlock);
static QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParamBlock);
static QTSS_Error CreateRTPFileSession(QTSS_StandardRTSP_Params* inParamBlock, const StrPtrLen& inPath, FileSession** outFile);
static QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParamBlock);
static QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParamBlock);
static QTSS_Error SendPackets(QTSS_RTPSendPackets_Params* inParams);
static QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams);
QTSS_Error QTSSRTPFileModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSRTPFileModuleDispatch);
}
QTSS_Error QTSSRTPFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParamBlock->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParamBlock->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPPreProcessor_Role:
return ProcessRTSPRequest(&inParamBlock->rtspPreProcessorParams);
case QTSS_RTPSendPackets_Role:
return SendPackets(&inParamBlock->rtpSendPacketsParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession(&inParamBlock->clientSessionClosingParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Register for roles
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
// Add text messages attributes
static char* sSeekToNonexistentTimeName = "QTSSFileModuleSeekToNonExistentTime";
static char* sBadQTFileName = "QTSSFileModuleBadQTFile";
static char* sExpectedDigitFilenameName = "QTSSFileModuleExpectedDigitFilename";
static char* sTrackDoesntExistName = "QTSSFileModuleTrackDoesntExist";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sSeekToNonexistentTimeName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sSeekToNonexistentTimeName, &sSeekToNonexistentTimeErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sBadQTFileName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sBadQTFileName, &sBadQTFileErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sExpectedDigitFilenameName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sExpectedDigitFilenameName, &sExpectedDigitFilenameErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sTrackDoesntExistName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sTrackDoesntExistName, &sTrackDoesntExistErr);
// Add an RTP session attribute for tracking FileSession objects
static char* sFileSessionName = "QTSSRTPFileModuleSession";
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sFileSessionName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sFileSessionName, &sFileSessionAttr);
// Tell the server our name!
static char* sModuleName = "QTSSRTPFileModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
//
// We need some prefs created and maintained by the QTSSFileModule,
// cuz we don't want to duplicate basically the same stuff
StrPtrLen theFileModule("QTSSFileModule");
sFileModulePrefs = QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName(theFileModule));
static StrPtrLen sEHeader("\r\ne=");
static StrPtrLen sUHeader("\r\nu=");
static StrPtrLen sHTTP("http://");
static StrPtrLen sAdmin("admin@");
// Read our preferences
RereadPrefs();
//build the sdp that looks like: \r\ne=http://streaming.apple.com\r\ne=qts@apple.com\r\n.
// Get the default DNS name of the server
StrPtrLen theDefaultDNS;
(void)QTSS_GetValuePtr(inParams->inServer, qtssSvrDefaultDNSName, 0,
(void**)&theDefaultDNS.Ptr, &theDefaultDNS.Len);
StrPtrLen sdpURL;
StrPtrLen adminEmail;
sdpURL.Ptr = QTSSModuleUtils::GetStringAttribute(sFileModulePrefs, "sdp_url", "");
sdpURL.Len = ::strlen(sdpURL.Ptr);
adminEmail.Ptr = QTSSModuleUtils::GetStringAttribute(sFileModulePrefs, "admin_email", "");
adminEmail.Len = ::strlen(adminEmail.Ptr);
UInt32 sdpURLLen = sdpURL.Len;
UInt32 adminEmailLen = adminEmail.Len;
if (sdpURLLen == 0)
sdpURLLen = theDefaultDNS.Len + sHTTP.Len + 1;
if (adminEmailLen == 0)
adminEmailLen = theDefaultDNS.Len + sAdmin.Len;
//calculate the length of the string & allocate memory
sSDPHeader2.Len = (sEHeader.Len * 2) + sdpURLLen + adminEmailLen + 10;
sSDPHeader2.Ptr = NEW char[sSDPHeader2.Len];
//write it!
StringFormatter sdpFormatter(sSDPHeader2);
sdpFormatter.Put(sUHeader);
//if there are preferences for default URL & admin email, use those. Otherwise, build the
//proper string based on default dns name.
if (sdpURL.Len == 0)
{
sdpFormatter.Put(sHTTP);
sdpFormatter.Put(theDefaultDNS);
sdpFormatter.PutChar('/');
}
else
sdpFormatter.Put(sdpURL);
sdpFormatter.Put(sEHeader);
//now do the admin email.
if (adminEmail.Len == 0)
{
sdpFormatter.Put(sAdmin);
sdpFormatter.Put(theDefaultDNS);
}
else
sdpFormatter.Put(adminEmail);
sdpFormatter.PutEOL();
sSDPHeader2.Len = (UInt32)sdpFormatter.GetCurrentOffset();
delete [] sdpURL.Ptr;
delete [] adminEmail.Ptr;
// Report to the server that this module handles DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN
static QTSS_RTSPMethod sSupportedMethods[] = { qtssDescribeMethod, qtssSetupMethod, qtssTeardownMethod, qtssPlayMethod, qtssPauseMethod };
QTSSModuleUtils::SetupSupportedMethods(inParams->inServer, sSupportedMethods, 5);
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
QTSSModuleUtils::GetAttribute(sFileModulePrefs, "flow_control_probe_interval", qtssAttrDataTypeUInt32,
&sFlowControlProbeInterval, &sDefaultFlowControlProbeInterval, sizeof(sFlowControlProbeInterval));
QTSSModuleUtils::GetAttribute(sFileModulePrefs, "max_allowed_speed", qtssAttrDataTypeFloat32,
&sMaxAllowedSpeed, &sDefaultMaxAllowedSpeed, sizeof(sMaxAllowedSpeed));
return QTSS_NoErr;
}
QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theMethodLen = 0;
if ((QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0,
(void**)&theMethod, &theMethodLen) != QTSS_NoErr) || (theMethodLen != sizeof(QTSS_RTSPMethod)))
{
Assert(0);
return QTSS_RequestFailed;
}
switch (*theMethod)
{
case qtssDescribeMethod:
return DoDescribe(inParams);
case qtssSetupMethod:
return DoSetup(inParams);
case qtssPlayMethod:
return DoPlay(inParams);
case qtssTeardownMethod:
// Tell the server that this session should be killed, and send a TEARDOWN response
(void)QTSS_Teardown(inParams->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
break;
case qtssPauseMethod:
(void)QTSS_Pause(inParams->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
break;
default:
break;
}
return QTSS_NoErr;
}
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParamBlock)
{
// Check and see if this is a request we should handle. We handle all requests with URLs that
// end in a '.rtp'
char* theFullPathStr = NULL;
QTSS_Error theError = QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFullPathStr);
QTSSCharArrayDeleter theFullPathDeleter(theFullPathStr);
Assert(theError == QTSS_NoErr);
StrPtrLen theFullPath(theFullPathStr);
if ((theFullPath.Len <= sRTPSuffix.Len) ||
(!sRTPSuffix.NumEqualIgnoreCase(&theFullPath.Ptr[theFullPath.Len - sRTPSuffix.Len], sRTPSuffix.Len)))
return QTSS_RequestFailed;
// It is, so let's set everything up...
//
// Get the FileSession for this DESCRIBE, if any.
UInt32 theLen = sizeof(FileSession*);
FileSession* theFile = NULL;
QTSS_Error theErr = QTSS_NoErr;
(void)QTSS_GetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, (void*)&theFile, &theLen);
if ( theFile != NULL )
{
//
// There is already a file for this session. This can happen if there are multiple DESCRIBES,
// or a DESCRIBE has been issued with a Session ID, or some such thing.
if ( !theFullPath.Equal( *theFile->fFile.GetMoviePath() ) )
{
delete theFile;
theFile = NULL;
// NULL out the attribute value, just in case.
(void)QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile));
}
}
if ( theFile == NULL )
{
theErr = CreateRTPFileSession(inParamBlock, theFullPath, &theFile);
if (theErr != QTSS_NoErr)
{
(void)QTSS_Teardown(inParamBlock->inClientSession);
return theErr;
}
// Store this newly created file object in the RTP session.
theErr = QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile));
}
//generate the SDP.
UInt32 totalSDPLength = sSDPHeader1.Len;
iovec theSDPVec[10];//1 for the RTSP header, 6 for the sdp header, 1 for the sdp body
theSDPVec[1].iov_base = sSDPHeader1.Ptr;
theSDPVec[1].iov_len = sSDPHeader1.Len;
//filename goes here
//(void)QTSS_GetValuePtr(inParamBlock->inRTSPRequest, qtssRTSPReqFilePath, 0, (void**)&theSDPVec[2].iov_base, (UInt32*)&theSDPVec[2].iov_len);
char* filenameStr = NULL;
(void)QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqFilePath, 0, &filenameStr);
QTSSCharArrayDeleter filenameStrDeleter(filenameStr);
theSDPVec[2].iov_base = filenameStr;
theSDPVec[2].iov_len = ::strlen(filenameStr);
totalSDPLength += theSDPVec[2].iov_len;
//url & admin email goes here
theSDPVec[3].iov_base = sSDPHeader2.Ptr;
theSDPVec[3].iov_len = sSDPHeader2.Len;
totalSDPLength += sSDPHeader2.Len;
//connection header
theSDPVec[4].iov_base = sSDPHeader3.Ptr;
theSDPVec[4].iov_len = sSDPHeader3.Len;
totalSDPLength += sSDPHeader3.Len;
//append IP addr
(void)QTSS_GetValuePtr(inParamBlock->inRTSPSession, qtssRTSPSesLocalAddrStr, 0,
(void**)&theSDPVec[5].iov_base, (UInt32*)&theSDPVec[5].iov_len);
totalSDPLength += theSDPVec[5].iov_len;
//last static sdp line
theSDPVec[6].iov_base = sSDPHeader4.Ptr;
theSDPVec[6].iov_len = sSDPHeader4.Len;
totalSDPLength += sSDPHeader4.Len;
//now append content-determined sdp
theSDPVec[7].iov_base = theFile->fFile.GetSDPFile()->Ptr;
theSDPVec[7].iov_len = theFile->fFile.GetSDPFile()->Len;
totalSDPLength += theSDPVec[7].iov_len;
Assert(theSDPVec[2].iov_base != NULL);
// Append the Last Modified header to be a good caching proxy citizen before sending the Describe
//(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssLastModifiedHeader,
// theFile->fFile.GetQTFile()->GetModDateStr(), DateBuffer::kDateBufferLen);
(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
//ok, we have a filled out iovec. Let's send it!
QTSSModuleUtils::SendDescribeResponse(inParamBlock->inRTSPRequest, inParamBlock->inClientSession,
&theSDPVec[0], 8, totalSDPLength);
return QTSS_NoErr;
}
QTSS_Error CreateRTPFileSession(QTSS_StandardRTSP_Params* inParamBlock, const StrPtrLen& inPath, FileSession** outFile)
{
*outFile = NEW FileSession();
StrPtrLen thePath(inPath);
RTPFileSession::ErrorCode theErr = (*outFile)->fFile.Initialize(thePath, 8);
if (theErr != RTPFileSession::errNoError)
{
delete *outFile;
*outFile = NULL;
if (theErr == RTPFileSession::errFileNotFound)
return QTSSModuleUtils::SendErrorResponse( inParamBlock->inRTSPRequest,
qtssClientNotFound,
sBadQTFileErr);
AssertV(0, theErr);
}
return QTSS_NoErr;
}
QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParamBlock)
{
//setup this track in the file object
FileSession* theFile = NULL;
UInt32 theLen = sizeof(FileSession*);
QTSS_Error theErr = QTSS_GetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, (void*)&theFile, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*)))
{
// This is possible, as clients are not required to send a DESCRIBE. If we haven't set
// anything up yet, set everything up
char* theFullPathStr = NULL;
theErr = QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFullPathStr);
Assert(theErr == QTSS_NoErr);
QTSSCharArrayDeleter theFullPathDeleter(theFullPathStr);
StrPtrLen theFullPath(theFullPathStr);
if ((theFullPath.Len <= sRTPSuffix.Len) ||
(!sRTPSuffix.NumEqualIgnoreCase(&theFullPath.Ptr[theFullPath.Len - sRTPSuffix.Len], sRTPSuffix.Len)))
return QTSS_RequestFailed;
theErr = CreateRTPFileSession(inParamBlock, theFullPath, &theFile);
if (theErr != QTSS_NoErr)
return theErr;
// Store this newly created file object in the RTP session.
theErr = QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile));
}
//unless there is a digit at the end of this path (representing trackID), don't
//even bother with the request
char* theDigitStr = NULL;
(void)QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr);
QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr);
if (theDigitStr == NULL)
return QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,
qtssClientBadRequest, sExpectedDigitFilenameErr);
UInt32 theTrackID = ::strtol(theDigitStr, NULL, 10);
RTPFileSession::ErrorCode qtfileErr = theFile->fFile.AddTrack(theTrackID);
//if we get an error back, forward that error to the client
if (qtfileErr == RTPFileSession::errTrackDoesntExist)
return QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,
qtssClientNotFound, sTrackDoesntExistErr);
else if (qtfileErr != RTPFileSession::errNoError)
return QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,
qtssUnsupportedMediaType, sBadQTFileErr);
//find the payload for this track ID (if applicable)
StrPtrLen* thePayload = NULL;
UInt32 thePayloadType = qtssUnknownPayloadType;
Float32 bufferDelay = (Float32) 3.0; // FIXME need a constant defined for 3.0 value. It is used multiple places
for (UInt32 x = 0; x < theFile->fFile.GetSourceInfo()->GetNumStreams(); x++)
{
SourceInfo::StreamInfo* theStreamInfo = theFile->fFile.GetSourceInfo()->GetStreamInfo(x);
if (theStreamInfo->fTrackID == theTrackID)
{
thePayload = &theStreamInfo->fPayloadName;
thePayloadType = theStreamInfo->fPayloadType;
bufferDelay = theStreamInfo->fBufferDelay;
break;
}
}
//Create a new RTP stream
QTSS_RTPStreamObject newStream = NULL;
theErr = QTSS_AddRTPStream(inParamBlock->inClientSession, inParamBlock->inRTSPRequest, &newStream, 0);
if (theErr != QTSS_NoErr)
return theErr;
// Set the payload type, payload name & timescale of this track
SInt32 theTimescale = theFile->fFile.GetTrackTimeScale(theTrackID);
theErr = QTSS_SetValue(newStream, qtssRTPStrBufferDelayInSecs, 0, &bufferDelay, sizeof(bufferDelay));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadName, 0, thePayload->Ptr, thePayload->Len);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadType, 0, &thePayloadType, sizeof(thePayloadType));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrTimescale, 0, &theTimescale, sizeof(theTimescale));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrTrackID, 0, &theTrackID, sizeof(theTrackID));
Assert(theErr == QTSS_NoErr);
// Set the number of quality levels. Allow up to 6
static UInt32 sNumQualityLevels = 0;
theErr = QTSS_SetValue(newStream, qtssRTPStrNumQualityLevels, 0, &sNumQualityLevels, sizeof(sNumQualityLevels));
Assert(theErr == QTSS_NoErr);
// Get the SSRC of this track
UInt32* theTrackSSRC = NULL;
UInt32 theTrackSSRCSize = 0;
(void)QTSS_GetValuePtr(newStream, qtssRTPStrSSRC, 0, (void**)&theTrackSSRC, &theTrackSSRCSize);
// The RTP stream should ALWAYS have an SSRC assuming QTSS_AddStream succeeded.
Assert((theTrackSSRC != NULL) && (theTrackSSRCSize == sizeof(UInt32)));
//give the file some info it needs.
theFile->fFile.SetTrackSSRC(theTrackID, *theTrackSSRC);
theFile->fFile.SetTrackCookie(theTrackID, newStream);
//
// Our array has now been updated to reflect the fields requested by the client.
//send the setup response
//(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssLastModifiedHeader,
// theFile->fFile.GetQTFile()->GetModDateStr(), DateBuffer::kDateBufferLen);
(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
//send the setup response
(void)QTSS_SendStandardRTSPResponse(inParamBlock->inRTSPRequest, newStream, 0);
return QTSS_NoErr;
}
QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParamBlock)
{
FileSession** theFile = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inParamBlock->inClientSession, sFileSessionAttr, 0, (void**)&theFile, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*)))
return QTSS_RequestFailed;
Float64* theStartTime = 0;
theErr = QTSS_GetValuePtr(inParamBlock->inRTSPRequest, qtssRTSPReqStartTime, 0, (void**)&theStartTime, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(Float64)))
return QTSSModuleUtils::SendErrorResponse( inParamBlock->inRTSPRequest,
qtssClientBadRequest, sSeekToNonexistentTimeErr);
RTPFileSession::ErrorCode qtFileErr = (*theFile)->fFile.Seek(*theStartTime);
if (qtFileErr != RTPFileSession::errNoError)
return QTSSModuleUtils::SendErrorResponse( inParamBlock->inRTSPRequest,
qtssClientBadRequest, sSeekToNonexistentTimeErr);
//make sure to clear the next packet the server would have sent!
(*theFile)->fPacketStruct.packetData = NULL;
// Set the movie duration and size parameters
Float64 movieDuration = (*theFile)->fFile.GetMovieDuration();
(void)QTSS_SetValue(inParamBlock->inClientSession, qtssCliSesMovieDurationInSecs, 0, &movieDuration, sizeof(movieDuration));
UInt64 movieSize = (*theFile)->fFile.GetAddedTracksRTPBytes();
(void)QTSS_SetValue(inParamBlock->inClientSession, qtssCliSesMovieSizeInBytes, 0, &movieSize, sizeof(movieSize));
//UInt32 bitsPerSecond = (*theFile)->fFile.GetBytesPerSecond() * 8;
//(void)QTSS_SetValue(inParamBlock->inClientSession, qtssCliSesMovieAverageBitRate, 0, &bitsPerSecond, sizeof(bitsPerSecond));
//
// For the purposes of the speed header, check to make sure all tracks are
// over a reliable transport
Bool16 allTracksReliable = true;
// Set the timestamp & sequence number parameters for each track.
QTSS_RTPStreamObject* theRef = NULL;
for ( UInt32 theStreamIndex = 0;
QTSS_GetValuePtr(inParamBlock->inClientSession, qtssCliSesStreamObjects, theStreamIndex, (void**)&theRef, &theLen) == QTSS_NoErr;
theStreamIndex++)
{
UInt32* theTrackID = NULL;
theErr = QTSS_GetValuePtr(*theRef, qtssRTPStrTrackID, 0, (void**)&theTrackID, &theLen);
Assert(theErr == QTSS_NoErr);
Assert(theTrackID != NULL);
Assert(theLen == sizeof(UInt32));
SInt16 theSeqNum = (*theFile)->fFile.GetNextTrackSequenceNumber(*theTrackID);
SInt32 theTimestamp = (*theFile)->fFile.GetSeekTimestamp(*theTrackID);
Assert(theRef != NULL);
Assert(theLen == sizeof(QTSS_RTPStreamObject));
theErr = QTSS_SetValue(*theRef, qtssRTPStrFirstSeqNumber, 0, &theSeqNum, sizeof(theSeqNum));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(*theRef, qtssRTPStrFirstTimestamp, 0, &theTimestamp, sizeof(theTimestamp));
Assert(theErr == QTSS_NoErr);
if (allTracksReliable)
{
QTSS_RTPTransportType theTransportType = qtssRTPTransportTypeUDP;
theLen = sizeof(theTransportType);
theErr = QTSS_GetValue(*theRef, qtssRTPStrTransportType, 0, &theTransportType, &theLen);
Assert(theErr == QTSS_NoErr);
if (theTransportType == qtssRTPTransportTypeUDP)
allTracksReliable = false;
}
}
//Tell the server to start playing this movie. We do want it to send RTCP SRs, but
//we DON'T want it to write the RTP header
theErr = QTSS_Play(inParamBlock->inClientSession, inParamBlock->inRTSPRequest, qtssPlayFlagsSendRTCP);
if (theErr != QTSS_NoErr)
return theErr;
// qtssRTPSesAdjustedPlayTimeInMsec is valid only after calling QTSS_Play
// theAdjustedPlayTime is a way to delay the sending of data until a time after the
// the Play response should have been received.
SInt64* theAdjustedPlayTime = 0;
theErr = QTSS_GetValuePtr(inParamBlock->inClientSession, qtssCliSesAdjustedPlayTimeInMsec, 0, (void**)&theAdjustedPlayTime, &theLen);
Assert(theErr == QTSS_NoErr);
Assert(theAdjustedPlayTime != NULL);
Assert(theLen == sizeof(SInt64));
(*theFile)->fAdjustedPlayTime = *theAdjustedPlayTime;
//
// This module supports the Speed header if the client wants the stream faster than normal.
Float32 theSpeed = 1;
theLen = sizeof(theSpeed);
theErr = QTSS_GetValue(inParamBlock->inRTSPRequest, qtssRTSPReqSpeed, 0, &theSpeed, &theLen);
Assert(theErr != QTSS_BadArgument);
Assert(theErr != QTSS_NotEnoughSpace);
if (theErr == QTSS_NoErr)
{
if (theSpeed > sMaxAllowedSpeed)
theSpeed = sMaxAllowedSpeed;
if ((theSpeed <= 0) || (!allTracksReliable))
theSpeed = 1;
}
(*theFile)->fSpeed = theSpeed;
if (theSpeed != 1)
{
//
// If our speed is not 1, append the RTSP speed header in the response
char speedBuf[32];
qtss_sprintf(speedBuf, "%10.5f", theSpeed);
StrPtrLen speedBufPtr(speedBuf);
(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssSpeedHeader,
speedBufPtr.Ptr, speedBufPtr.Len);
}
//
// Record the requested start and stop time.
(*theFile)->fStopTime = -1;
theLen = sizeof((*theFile)->fStopTime);
theErr = QTSS_GetValue(inParamBlock->inRTSPRequest, qtssRTSPReqStopTime, 0, &(*theFile)->fStopTime, &theLen);
Assert(theErr == QTSS_NoErr);
(*theFile)->fStartTime = 0;
theLen = sizeof((*theFile)->fStopTime);
theErr = QTSS_GetValue(inParamBlock->inRTSPRequest, qtssRTSPReqStartTime, 0, &(*theFile)->fStartTime, &theLen);
Assert(theErr == QTSS_NoErr);
//the client doesn't necessarily specify this information in a play,
//if it doesn't, fall back on some defaults.
if ((*theFile)->fStartTime == -1)
(*theFile)->fStartTime = 0;
(void)QTSS_SendStandardRTSPResponse(inParamBlock->inRTSPRequest, inParamBlock->inClientSession, qtssPlayRespWriteTrackInfo);
return QTSS_NoErr;
}
QTSS_Error SendPackets(QTSS_RTPSendPackets_Params* inParams)
{
FileSession** theFile = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sFileSessionAttr, 0, (void**)&theFile, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*)))
{
Assert( 0 );
return QTSS_RequestFailed;
}
while (true)
{
if ((*theFile)->fPacketStruct.packetData == NULL)
{
void* theCookie = NULL;
Float64 theTransmitTime = (*theFile)->fFile.GetNextPacket((UInt8**)&(*theFile)->fPacketStruct.packetData, &(*theFile)->fNextPacketLen, &theCookie);
//
// Check to see if we should stop playing based on a client specified stop time
if (((*theFile)->fStopTime != -1) && (theTransmitTime > (*theFile)->fStopTime))
{
// We should indeed stop playing
(void)QTSS_Pause(inParams->inClientSession);
inParams->outNextPacketTime = qtssDontCallSendPacketsAgain;
return QTSS_NoErr;
}
//
// Adjust transmit time based on speed
Float64 theOffsetFromStartTime = theTransmitTime - (*theFile)->fStartTime;
theTransmitTime = (*theFile)->fStartTime + (theOffsetFromStartTime / (*theFile)->fSpeed);
(*theFile)->fStream = (QTSS_RTPStreamObject)theCookie;
(*theFile)->fPacketStruct.packetTransmitTime = (*theFile)->fAdjustedPlayTime + ((SInt64)(theTransmitTime * 1000));
#if RTP_FILE_MODULE_DEBUGGING >= 8
UInt16* theSeqNumPtr = (UInt16*)(*theFile)->fNextPacket;
UInt32* theTimestampP = (UInt32*)(*theFile)->fNextPacket;
UInt32* theTrackID = NULL;
(void)QTSS_GetValuePtr((*theFile)->fStream, qtssRTPStrTrackID, 0, (void**)&theTrackID, &theLen);
qtss_printf("Got packet. Seq num: %d. Timestamp: %d. TrackID: %d. Transmittime: %f\n",theSeqNumPtr[1], theTimestampP[1], *theTrackID, theTransmitTime);
#endif
}
//We are done playing all streams!
if ((*theFile)->fPacketStruct.packetData == NULL)
{
#if RTP_FILE_MODULE_DEBUGGING >= 8
qtss_printf("done w all packets\n");
#endif
inParams->outNextPacketTime = qtssDontCallSendPacketsAgain;
return QTSS_NoErr;
}
//we have a packet that needs to be sent now
Assert((*theFile)->fStream != NULL);
// Send the packet!
theErr = QTSS_Write((*theFile)->fStream, &(*theFile)->fPacketStruct, (*theFile)->fNextPacketLen, NULL, qtssWriteFlagsIsRTP);
if ( theErr == QTSS_WouldBlock )
{
if ((*theFile)->fPacketStruct.packetTransmitTime == -1)
inParams->outNextPacketTime = sFlowControlProbeInterval; // for buffering, try me again in # MSec
else
{
Assert((*theFile)->fPacketStruct.packetTransmitTime > inParams->inCurrentTime);
inParams->outNextPacketTime = (*theFile)->fPacketStruct.packetTransmitTime - inParams->inCurrentTime;
}
return QTSS_NoErr;
}
(*theFile)->fPacketStruct.packetData = NULL;
}
Assert(0);
return QTSS_NoErr;
}
QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
{
FileSession** theFile = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sFileSessionAttr, 0, (void**)&theFile, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*)) || (theFile == NULL))
return QTSS_RequestFailed;
delete *theFile;
return QTSS_NoErr;
}

View file

@ -0,0 +1,45 @@
/*
*
* @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: QTSSFileModule.h
Contains: Content source module that uses the QTFileLib to serve Hinted QuickTime
files to clients.
*/
#ifndef _RTPRTPFILEMODULE_H_
#define _RTPRTPFILEMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSRTPFileModule_Main(void* inPrivateArgs);
}
#endif //_RTPRTPFILEMODULE_H_

View file

@ -0,0 +1,571 @@
/*
*
* @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: RTPFileSession.cpp
Contains:
*/
#define RTPFILESESSIONDEBUG 0
#include "RTPFileSession.h"
#include "OSMemory.h"
static OSRefTable sOpenFileMap;
RTPFileSession::RTPFileSession()
: fFileSource(NULL),
fFileLength(0),
fCurrentPosition(0),
fFile(NULL),
fTrackInfo(NULL),
fNumTracksEnabled(0),
fReadBuffer(NULL),
fReadBufferOffset(0),
fDataBuffer(NULL),
fDataBufferSize(0),
fDataBufferLen(0),
fCurrentPacket(NULL),
fAddedTracksRTPBytes(0)
{}
RTPFileSession::~RTPFileSession()
{
// Check to see if we should destroy this file
OSMutexLocker locker (sOpenFileMap.GetMutex());
#if RTPFILESESSIONDEBUG
qtss_printf("Dropping refcount on file\n");
#endif
if (fFile == NULL)
return;
sOpenFileMap.Release(fFile->GetRef());
if (fFile->GetRef()->GetRefCount() == 0)
{
#if RTPFILESESSIONDEBUG
qtss_printf("Refcount dropped to 0. Deleting file\n");
#endif
sOpenFileMap.UnRegister(fFile->GetRef());
delete fFile;
}
// Delete our data buffer
delete [] fDataBuffer;
delete [] fTrackInfo;
}
RTPFileSession::ErrorCode RTPFileSession::Initialize(StrPtrLen& inFilePath, Float32 inBufferSeconds)
{
Assert(fFile == NULL);
// Check to see if this file is already open
OSMutexLocker locker(sOpenFileMap.GetMutex());
OSRef* theFileRef = sOpenFileMap.Resolve((StrPtrLen*)&inFilePath);
if (theFileRef == NULL)
{
//qtss_printf("Didn't find file in map. Creating new one\n");
fFile = NEW RTPFile();
ErrorCode theErr = fFile->Initialize(inFilePath);
if (theErr != errNoError)
{
delete fFile;
fFile = NULL;
return theErr;
}
OS_Error osErr = sOpenFileMap.Register(fFile->GetRef());
Assert(osErr == OS_NoErr);
//unless we do this, the refcount won't increment (and we'll delete the session prematurely
OSRef* debug = sOpenFileMap.Resolve((StrPtrLen*)&inFilePath);
Assert(debug == fFile->GetRef());
}
else
{
//qtss_printf("Found file. Refcounting.\n");
fFile = (RTPFile*)theFileRef->GetObject();
}
//Open the file no matter what
//fFileSource.Set(inFilePath.Ptr);
//Assert(fFileSource.GetLength() > 0);
QTSS_Error theErr = QTSS_OpenFileObject(inFilePath.Ptr, 0, &fFileSource);
Assert(theErr == QTSS_NoErr);
//
// Get the file length
UInt32 theLen = sizeof(fFileLength);
theErr = QTSS_GetValue(fFileSource, qtssFlObjLength, 0, &fFileLength, &theLen);
Assert(theErr == QTSS_NoErr);
Assert(theLen == sizeof(fFileLength));
// Allocate our data buffer
fDataBufferSize = this->PowerOf2Floor((UInt32)(inBufferSeconds * fFile->GetBytesPerSecond()));
// Check to see if the size is out of range. If so, adjust it
if (fDataBufferSize > kMaxDataBufferSize)
fDataBufferSize = kMaxDataBufferSize;
if (fDataBufferSize < kBlockSize)
fDataBufferSize = kBlockSize;
fReadBuffer = fDataBuffer = NEW UInt8[fDataBufferSize];
// Allocate a buffer of TrackInfos
fTrackInfo = NEW RTPFileSessionTrackInfo[fFile->GetMaxTrackNumber() + 1];
::memset(fTrackInfo, 0, fFile->GetMaxTrackNumber() * sizeof(RTPFileSessionTrackInfo));
return errNoError;
}
RTPFileSession::ErrorCode RTPFileSession::AddTrack(UInt32 trackID)
{
if (fFile->TrackExists(trackID))
{
if (!fTrackInfo[trackID].fEnabled)
{
fTrackInfo[trackID].fEnabled = true;
fAddedTracksRTPBytes += fFile->GetTrackBytes(trackID);
fNumTracksEnabled++;
}
else
return errTrackAlreadyAdded;
}
else
return errTrackDoesntExist;
return errNoError;
}
RTPFileSession::ErrorCode RTPFileSession::Seek(Float64 inTime)
{
if ((inTime < 0) || (inTime > fFile->GetMovieDuration()))
return errSeekToNonexistentTime;
UInt64 theBlockLocation = fFile->GetBlockLocation(inTime);
Assert(theBlockLocation >= fFile->fHeader.fDataStartPos);
Assert(theBlockLocation < fFileLength);
// Seek to the right file location.
//fFileSource.Seek(theBlockLocation);
QTSS_Error theErr = QTSS_Seek(fFileSource, theBlockLocation);
Assert(theErr == QTSS_NoErr);
fCurrentPosition = theBlockLocation;
// Read the file data
this->ReadAndAdvise();
for (UInt32 x = 0; x <= fFile->GetMaxTrackNumber(); x++)
fTrackInfo[x].fMarked = false;
//
// We need to find out what the first packet is for each enabled track.
// So scan ahead until we find the very first packet we need to send.
// At that point, "freeze" the current block in memory, and that position,
// because that's the position we'll be starting from when GetNextPacket gets
// called. In order to "freeze" we store lots of info about the current position
// on the stack with the variables defined below.
//
// Keep on going until we find the first packets for all the enabled tracks,
// and if that involves traversing multiple blocks, keep those blocks in a temporary
// buffer, allowing us to easily go back to the start point when done.
UInt8* theStartPos = NULL;
UInt32 origDataBufferLen = 0;
UInt64 currentFileOffset = 0;
UInt32 tracksFound = 0;
// Needed to call GetNextPacket
UInt8* thePacketP = NULL;
UInt32 thePacketLength = 0;
void* theCookie = NULL;
while (tracksFound < fNumTracksEnabled)
{
Float64 theTransmitTime = this->GetNextPacket(&thePacketP, &thePacketLength, &theCookie);
if (thePacketP == NULL)
{
Assert(tracksFound > 0);
break; // We're at the end of the file!
}
// Ignore < 0 timed packets
RTPFilePacket* thePacket = (RTPFilePacket*)thePacketP;
Assert((thePacket - 1)->fTransmitTime == theTransmitTime);
if (theTransmitTime < 0)
theTransmitTime = 0;
UInt32 theTrackID = (thePacket - 1)->fTrackID;
if ((theTransmitTime >= inTime) && (!fTrackInfo[theTrackID].fMarked) &&
(fTrackInfo[theTrackID].fEnabled))
{
// This is the first packet for this track after our fCurrentPtr mark.
// Record the first seq # and timestamp of the packet
UInt16* theSeqNumPtr = (UInt16*)thePacketP;
UInt32* theTimestampPtr = (UInt32*)thePacketP;
fTrackInfo[theTrackID].fSeekSeqNumber = theSeqNumPtr[1];
fTrackInfo[theTrackID].fSeekTimestamp = theTimestampPtr[1];
fTrackInfo[theTrackID].fMarked = true;
if (tracksFound == 0)
{
//
// If this is the first packet that we're going to send (for all
// streams), then mark the position, and make sure that if we
// need to dump this buffer to find first packets for other tracks,
// we'll be able to come back to this very place so we can start streaming.
fReadBuffer = NULL;
theStartPos = (UInt8*)(thePacket-1);
origDataBufferLen = fDataBufferLen;
currentFileOffset = fCurrentPosition;
}
tracksFound++;
}
}
if (fReadBuffer != NULL)
{
// We had to skip ahead in the file. Restore everything to
// the way it was when we found the first packet, so GetNextPacket
// will work fine.
delete [] fReadBuffer;
fReadBuffer = fDataBuffer;
Assert(origDataBufferLen > 0);
fDataBufferLen = origDataBufferLen;
Assert(currentFileOffset > 0);
//fFileSource.Seek(currentFileOffset);
theErr = QTSS_Seek(fFileSource, theBlockLocation);
Assert(theErr == QTSS_NoErr);
}
// Start at the first packet we need to send.
Assert(theStartPos != NULL);
fCurrentPacket = theStartPos;
return errNoError;
}
UInt16 RTPFileSession::GetNextTrackSequenceNumber(UInt32 inTrackID)
{
Assert(inTrackID <= fFile->GetMaxTrackNumber());
Assert(fTrackInfo[inTrackID].fMarked);
return fTrackInfo[inTrackID].fSeekSeqNumber;
}
UInt32 RTPFileSession::GetSeekTimestamp(UInt32 inTrackID)
{
Assert(inTrackID <= fFile->GetMaxTrackNumber());
Assert(fTrackInfo[inTrackID].fMarked);
return fTrackInfo[inTrackID].fSeekTimestamp;
}
Float64 RTPFileSession::GetNextPacket(UInt8** outPacket, UInt32* outPacketLength, void** outCookie)
{
Bool16 isValidPacket = false;
RTPFilePacket* thePacket = NULL;
// Loop until we find a legal packet
while (!isValidPacket)
{
// If we are between blocks, read the next block
if (fCurrentPacket == NULL)
{
if (fCurrentPosition == fFileLength)
{
#if RTPFILESESSIONDEBUG
qtss_printf("RTPFileSession::GetNextPacket fCurrentPosition == fFileLength quit\n");
#endif
*outPacket = NULL;
return -1;
}
this->ReadAndAdvise();
}
Assert(fCurrentPacket != NULL);
thePacket = (RTPFilePacket*)fCurrentPacket;
if (thePacket->fTrackID & kPaddingBit)
{
// We hit a padding packet, move the fCurrentPacket pointer to the next block
fReadBufferOffset += kBlockSize;
fReadBufferOffset &= kBlockMask; //Rounds down to the nearest block size
#if RTPFILESESSIONDEBUG
qtss_printf("Found a pad packet. Moving on\n");
#endif
// Check to make sure we aren't at the end of the buffer
if (fReadBufferOffset >= fDataBufferLen)
fCurrentPacket = NULL;
else
fCurrentPacket = fDataBuffer + fReadBufferOffset;
}
else if (!fTrackInfo[thePacket->fTrackID].fEnabled)
{
// This is a valid packet, but track not enabled, so skip it
Assert(thePacket->fTrackID <= fFile->GetMaxTrackNumber());
this->SkipToNextPacket(thePacket);
}
else
// This is a valid packet, and the track is enabled
isValidPacket = true;
}
// We must have a valid packet here
Assert(thePacket != NULL);
Assert(thePacket->fTrackID <= fFile->GetMaxTrackNumber());
// Set the return values
*outPacket = (UInt8*)(thePacket + 1);
*outPacketLength = thePacket->fPacketLength;
*outCookie = fTrackInfo[thePacket->fTrackID].fCookie;
// Set the packet's SSRC
UInt32* ssrcPtr = (UInt32*)*outPacket;
ssrcPtr[2] = fTrackInfo[thePacket->fTrackID].fSSRC;
Float64 transmitTime = thePacket->fTransmitTime;
this->SkipToNextPacket(thePacket);
return transmitTime;
}
void RTPFileSession::SkipToNextPacket(RTPFilePacket* inCurPacket)
{
// Skip over this packet
Assert(inCurPacket->fPacketLength < 1500);
fReadBufferOffset += inCurPacket->fPacketLength;
fCurrentPacket += (inCurPacket->fPacketLength + sizeof(RTPFilePacket));
// Check to see if we need to read more data
if (fReadBufferOffset >= fDataBufferLen)
{
#if RTPFILESESSIONDEBUG
qtss_printf("In SkipToNextPacket. Out of data\n");
#endif
fCurrentPacket = NULL;
}
}
void RTPFileSession::ReadAndAdvise()
{
if (fReadBuffer == NULL)
{
// In some situations, callers of this function may not want the fDataBuffer
// to be disturbed (see Seek()). If that's the case, the caller will set
// fReadBuffer to NULL, and we must allocate it here
fReadBuffer = NEW UInt8[fDataBufferSize];
}
// Read the next block. There should always be at least one packet
// here, as we have a valid block in the block table.
#if RTPFILESESSIONDEBUG
//qtss_printf("Moving onto next block. File loc: %qd\n",fFileSource.GetCurOffset());
#endif
fDataBufferLen = 0;
//(void)fFileSource.Read(fDataBuffer, fDataBufferSize, &fDataBufferLen);
QTSS_Error theErr = QTSS_Read(fFileSource, fDataBuffer, fDataBufferSize, &fDataBufferLen);
Assert(theErr == QTSS_NoErr);
Assert(fDataBufferLen > sizeof(RTPFilePacket));
fCurrentPosition += fDataBufferLen;
// Now do an advise for the next block, if this block isn't the last.
if (fCurrentPosition < fFileLength)
{
Assert(fDataBufferLen == fDataBufferSize);
//fFileSource.Advise(fFileSource.GetCurOffset(), fDataBufferSize);
theErr = QTSS_Advise(fFileSource, fCurrentPosition, fDataBufferSize);
}
fReadBufferOffset = 0;
fCurrentPacket = fDataBuffer;
}
UInt32 RTPFileSession::PowerOf2Floor(UInt32 inNumToFloor)
{
UInt32 retVal = 0x10000000;
while (retVal > 0)
{
if (retVal & inNumToFloor)
return retVal;
else
retVal >>= 1;
}
return retVal;
}
RTPFile::RTPFile()
: fTrackInfo(NULL),
fBlockMap(NULL),
fBytesPerSecond(0),
fMaxTrackNumber(0)
{}
RTPFile::~RTPFile()
{
delete [] fFilePath.Ptr;
delete [] fSDPData.Ptr;
delete [] fTrackInfo;
delete [] fBlockMap;
}
RTPFileSession::ErrorCode RTPFile::Initialize(const StrPtrLen& inFilePath)
{
//OSFileSource theFile(inFilePath.Ptr);
QTSS_Object theFile = NULL;
QTSS_Error theErr = QTSS_OpenFileObject(inFilePath.Ptr, 0, &theFile);
if (theErr != QTSS_NoErr)
return RTPFileSession::errFileNotFound;
// Copy the path.
fFilePath.Ptr = NEW char[inFilePath.Len + 1];
::memcpy(fFilePath.Ptr, inFilePath.Ptr, inFilePath.Len);
fFilePath.Len = inFilePath.Len;
// Setup our osref
fRef.Set(fFilePath, this);
// Read the header
//OS_Error theErr = theFile.Read(&fHeader, sizeof(fHeader));
UInt32 theLengthRead = 0;
theErr = QTSS_Read(theFile, &fHeader, sizeof(fHeader), &theLengthRead);
Assert(theErr == QTSS_NoErr);
Assert(theLengthRead == sizeof(fHeader));
// Read the SDP data
fSDPData.Len = fHeader.fSDPLen;
fSDPData.Ptr = NEW char[fSDPData.Len + 1];
//theErr = theFile.Read(fSDPData.Ptr, fSDPData.Len);
theErr = QTSS_Read(theFile, fSDPData.Ptr, fSDPData.Len, &theLengthRead);
Assert(theErr == QTSS_NoErr);
Assert(theLengthRead == fSDPData.Len);
// Parse the SDP Information
fSourceInfo.Parse(fSDPData.Ptr, fSDPData.Len);
// Read the track info
fTrackInfo = NEW RTPFileTrackInfo[fHeader.fNumTracks];
//theErr = theFile.Read(fTrackInfo, sizeof(RTPFileTrackInfo) * fHeader.fNumTracks);
theErr = QTSS_Read(theFile, fTrackInfo, sizeof(RTPFileTrackInfo) * fHeader.fNumTracks, &theLengthRead);
Assert(theErr == QTSS_NoErr);
Assert(theLengthRead == sizeof(RTPFileTrackInfo) * fHeader.fNumTracks);
// Create and read the block map
fBlockMap = NEW UInt8[fHeader.fBlockMapSize];
//theErr = theFile.Read(fBlockMap, fHeader.fBlockMapSize);
theErr = QTSS_Read(theFile, fBlockMap, fHeader.fBlockMapSize, &theLengthRead);
Assert(theErr == QTSS_NoErr);
Assert(theLengthRead == fHeader.fBlockMapSize);
// Calculate bit rate of all the tracks combined
Float64 totalBytes = 0;
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
totalBytes += (Float64)fTrackInfo[x].fBytesInTrack;
totalBytes /= fHeader.fMovieDuration;
fBytesPerSecond = (UInt32)totalBytes;
//Get the max track number
fMaxTrackNumber = 0;
for (UInt32 y = 0; y < fHeader.fNumTracks; y++)
if (fTrackInfo[y].fID > fMaxTrackNumber)
fMaxTrackNumber = fTrackInfo[y].fID;
(void)QTSS_CloseFileObject(theFile);
return RTPFileSession::errNoError;
}
SInt64 RTPFile::GetBlockLocation(Float64 inTimeInSecs)
{
if (inTimeInSecs == 0)
return fHeader.fDataStartPos;
UInt32 theTime = 0;
UInt32 x = 0;
for ( ; x < fHeader.fBlockMapSize ; x++)
{
theTime += fBlockMap[x];
if (theTime >= (UInt32)inTimeInSecs)
{
if ((theTime > (UInt32)inTimeInSecs) && (x > 0))
// If we've moved too far, go back to the previous block
x--;
return fHeader.fDataStartPos + (kBlockSize * x);
}
}
return fHeader.fDataStartPos + (kBlockSize * x);// The requested time must be in the last block (or nonexistent).
}
Float64 RTPFile::GetTrackDuration(UInt32 inTrackID)
{
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
{
if (fTrackInfo[x].fID == inTrackID)
return fTrackInfo[x].fDuration;
}
Assert(0);
return -1;
}
UInt32 RTPFile::GetTrackTimeScale(UInt32 inTrackID)
{
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
{
if (fTrackInfo[x].fID == inTrackID)
return fTrackInfo[x].fTimescale;
}
Assert(0);
return 0;
}
UInt64 RTPFile::GetTrackBytes(UInt32 inTrackID)
{
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
{
if (fTrackInfo[x].fID == inTrackID)
return fTrackInfo[x].fBytesInTrack;
}
Assert(0);
return 0;
}
Bool16 RTPFile::TrackExists(UInt32 inTrackID)
{
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
{
if (fTrackInfo[x].fID == inTrackID)
return true;
}
return false;
}

View file

@ -0,0 +1,227 @@
/*
*
* @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: RTPFileSession.h
Contains:
*/
#ifndef __RTPFILESESSIONH__
#define __RTPFILESESSIONH__
#include "RTPFileDefs.h"
#include "OSHeaders.h"
#include "OSRef.h"
#include "QTSS.h" // This object uses QTSS API file I/O
#include "SDPSourceInfo.h"
class RTPFile;
class RTPFileSession
{
public:
// One per client
RTPFileSession();
~RTPFileSession();
//
// Class error codes
enum ErrorCode
{
errNoError = 0,
errFileNotFound = 1,
errTrackAlreadyAdded = 2,
errTrackDoesntExist = 3,
errSeekToNonexistentTime = 4
};
//
// INITIALIZE
ErrorCode Initialize(StrPtrLen& inFilePath, Float32 inBufferSeconds);
//
// ACCESSORS
//
// Global information
inline Float64 GetMovieDuration();
inline StrPtrLen* GetSDPFile();
inline SourceInfo* GetSourceInfo();
inline StrPtrLen* GetMoviePath();
UInt64 GetAddedTracksRTPBytes() { return fAddedTracksRTPBytes; }
//
// Track functions
inline Float64 GetTrackDuration(UInt32 inTrackID);
inline UInt32 GetTrackTimeScale(UInt32 inTrackID);
UInt16 GetNextTrackSequenceNumber(UInt32 inTrackID);
UInt32 GetSeekTimestamp(UInt32 TrackID);
//
// MODIFIERS
//
// Track modifiers
ErrorCode AddTrack(UInt32 inTrackID);
void SetTrackSSRC(UInt32 inTrackID, UInt32 inSSRC) { fTrackInfo[inTrackID].fSSRC = inSSRC; }
void SetTrackCookie(UInt32 inTrackID, void *inCookie){ fTrackInfo[inTrackID].fCookie = inCookie; }
// Seek to a time
ErrorCode Seek(Float64 inTime);
// GetNextPacket. Returns the transmit time for this packet
Float64 GetNextPacket(UInt8** outPacket, UInt32* outPacketLength, void** outCookie);
private:
// Utility functions
void SkipToNextPacket(RTPFilePacket* inCurPacket);
void ReadAndAdvise();
UInt32 PowerOf2Floor(UInt32 inNumToFloor);
enum
{
kMaxDataBufferSize = 262144
};
struct RTPFileSessionTrackInfo
{
Bool16 fEnabled;
Bool16 fMarked; // is the seek timestamp, seq num recorded?
UInt32 fSSRC;
UInt32 fSeekTimestamp;
UInt16 fSeekSeqNumber;
void* fCookie;
};
QTSS_Object fFileSource;
//OSFileSource fFileSource;
UInt64 fFileLength;
UInt64 fCurrentPosition;
RTPFile* fFile;
RTPFileSessionTrackInfo* fTrackInfo;
UInt32 fNumTracksEnabled;
UInt8* fReadBuffer; // Buffer that file data gets read into.
// Usually same as fDataBuffer
UInt32 fReadBufferOffset;
UInt8* fDataBuffer; // Buffer for file data
UInt32 fDataBufferSize;
UInt32 fDataBufferLen;
UInt8* fCurrentPacket;
UInt64 fAddedTracksRTPBytes;
friend class RTPFile;
};
class RTPFile
{
public:
// One per file
RTPFile();
~RTPFile();
RTPFileSession::ErrorCode Initialize(const StrPtrLen& inFilePath);
Float64 GetMovieDuration() { return fHeader.fMovieDuration; }
Float64 GetTrackDuration(UInt32 inTrackID);
UInt32 GetTrackTimeScale(UInt32 inTrackID);
UInt64 GetTrackBytes(UInt32 inTrackID);
Bool16 TrackExists(UInt32 inTrackID);
UInt32 GetBytesPerSecond() { return fBytesPerSecond; }
UInt32 GetMaxTrackNumber() { return fMaxTrackNumber; }
StrPtrLen* GetSDPFile() { return &fSDPData; }
SourceInfo* GetSourceInfo() { return &fSourceInfo; }
OSRef* GetRef() { return &fRef; }
// Returns the location in the file corresponding to this time,
// rounded to the nearest start of block.
SInt64 GetBlockLocation(Float64 inTimeInSecs);
private:
RTPFileTrackInfo* fTrackInfo;
RTPFileHeader fHeader;
UInt8* fBlockMap;
StrPtrLen fSDPData;
SDPSourceInfo fSourceInfo;
UInt32 fBytesPerSecond;
UInt32 fMaxTrackNumber;
OSRef fRef;
StrPtrLen fFilePath;
friend class RTPFileSession;
};
inline StrPtrLen* RTPFileSession::GetSDPFile()
{
return fFile->GetSDPFile();
}
inline SourceInfo* RTPFileSession::GetSourceInfo()
{
return &fFile->fSourceInfo;
}
inline StrPtrLen* RTPFileSession::GetMoviePath()
{
return &fFile->fFilePath;
}
inline Float64 RTPFileSession::GetMovieDuration()
{
return fFile->fHeader.fMovieDuration;
}
inline Float64 RTPFileSession::GetTrackDuration(UInt32 inTrackID)
{
return fFile->GetTrackDuration(inTrackID);
}
inline UInt32 RTPFileSession::GetTrackTimeScale(UInt32 inTrackID)
{
return fFile->GetTrackTimeScale(inTrackID);
}
#endif

View file

@ -0,0 +1,380 @@
/*
*
* @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: QTSSRawFileModule.cpp
Contains: Implementation of Raw File module
*/
#include "QTSSRawFileModule.h"
#include "OSHeaders.h"
#include "StrPtrLen.h"
#include "OSArrayObjectDeleter.h"
#include "QTSSModuleUtils.h"
#include "OSMemory.h"
#include "ev.h"
#include "QTSSMemoryDeleter.h"
#define RAWFILE_FILE_ASYNC 1
#define RAW_FILE_DEBUGGING 0
// ATTRIBUTES IDs
static QTSS_AttributeID sStateAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFileAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFileBufferAttr = qtssIllegalAttrID;
static QTSS_AttributeID sReadOffsetAttr = qtssIllegalAttrID;
static QTSS_AttributeID sWriteOffsetAttr = qtssIllegalAttrID;
// STATIC DATA
static StrPtrLen sRawSuffix(".raw");
static const UInt32 kReadingBufferState = 0;
static const UInt32 kWritingBufferState = 1;
// FUNCTIONS
static QTSS_Error QTSSRawFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Preprocess(QTSS_StandardRTSP_Params* inParams);
QTSS_Error QTSSRawFileModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSRawFileModuleDispatch);
}
QTSS_Error QTSSRawFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_RTSPPreProcessor_Role:
return Preprocess(&inParams->rtspPreProcessorParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
static char* sStateName = "QTSSRawFileModuleState";
static char* sFileName = "QTSSRawFileModuleFile";
static char* sFileBufferName = "QTSSRawFileModuleFileBuffer";
static char* sReadOffsetName = "QTSSRawFileModuleReadOffset";
static char* sWriteOffsetName = "QTSSRawFileModuleWriteOffset";
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sStateName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sStateName, &sStateAttr);
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileName, &sFileAttr);
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileBufferName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileBufferName, &sFileBufferAttr);
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sReadOffsetName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sReadOffsetName, &sReadOffsetAttr);
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sWriteOffsetName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sWriteOffsetName, &sWriteOffsetAttr);
// Tell the server our name!
static char* sModuleName = "QTSSRawFileModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Preprocess(QTSS_StandardRTSP_Params* inParams)
{
static UInt32 sFileBufSize = 32768;
static UInt32 sInitialState = kReadingBufferState;
static UInt32 sZero = 0;
UInt32 theLen = 0;
UInt32* theStateP = NULL;
QTSS_Error theErr = QTSS_NoErr;
QTSS_Object theFile = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sStateAttr, 0, (void**)&theStateP, &theLen);
if ((theStateP == NULL) || (theLen != sizeof(UInt32)))
{
// Initial state. We haven't started sending the file yet, so
// check to see if this is our request, and if it is, set everything up.
// Only operate if this is a DESCRIBE
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
if ((QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0,
(void**)&theMethod, &theLen) != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)))
{
Assert(0);
return QTSS_RequestFailed;
}
if (*theMethod != qtssDescribeMethod)
return QTSS_RequestFailed;
// Check to see if this is a raw file request
char* theFilePath = NULL;
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFilePath);
QTSSCharArrayDeleter theFilePathDeleter(theFilePath);
theLen = ::strlen(theFilePath);
// Copy the full path, and append a ".raw"
OSCharArrayDeleter rawPath(NEW char[theLen + sRawSuffix.Len + 4]);
::memcpy(rawPath.GetObject(), theFilePath, theLen);
::strcpy(rawPath.GetObject() + theLen, sRawSuffix.Ptr);
#if RAWFILE_FILE_ASYNC
theErr = QTSS_OpenFileObject(rawPath.GetObject(), qtssOpenFileAsync, &theFile);
#else
theErr = QTSS_OpenFileObject(rawPath.GetObject(), qtssOpenFileAsync, &theFile);
#endif
// If the file doesn't exist, and if this is a path with a '.raw' at the end,
// check to see if the path without the extra .raw exists
if (theErr != QTSS_NoErr)
{
theFile = NULL;
rawPath.GetObject()[theLen] = '\0';
if (theLen > sRawSuffix.Len)
{
StrPtrLen comparer((theFilePath + theLen) - sRawSuffix.Len, sRawSuffix.Len);
if (comparer.Equal(sRawSuffix))
{
#if RAWFILE_FILE_ASYNC
theErr = QTSS_OpenFileObject(rawPath.GetObject(), qtssOpenFileAsync, &theFile);
#else
theErr = QTSS_OpenFileObject(rawPath.GetObject(), kOpenFileNoFlags, &theFile);
#endif
}
}
}
// If the file doesn't exist, we should probably return a 404 not found.
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
// Before sending any response, set keep alive to off for this connection
// Regardless of what the client sends, the server always closes the connection after sending the file
static Bool16 sFalse = false;
(void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
// We have a real file. Setup all the dictionary values we need
(void)QTSS_SetValue(inParams->inRTSPSession, sFileAttr, 0, &theFile, sizeof(theFile));
// Create a buffer to store data.
char* theFileBuffer = NEW char[sFileBufSize];
(void)QTSS_SetValue(inParams->inRTSPSession, sFileBufferAttr, 0, &theFileBuffer, sizeof(theFileBuffer));
// Store our initial state
(void)QTSS_SetValue(inParams->inRTSPSession, sStateAttr, 0, &sInitialState, sizeof(sInitialState));
theStateP = &sInitialState; // so we can proceed normally
(void)QTSS_SetValue(inParams->inRTSPSession, sReadOffsetAttr, 0, &sZero, sizeof(sZero));
(void)QTSS_SetValue(inParams->inRTSPSession, sWriteOffsetAttr, 0, &sZero, sizeof(sZero));
}
// Get our attributes
char** theFileBufferP = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileBufferAttr, 0, (void**)&theFileBufferP, &theLen);
Assert(theFileBufferP != NULL);
Assert(theLen == sizeof(char*));
QTSS_Object* theFileP = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileAttr, 0, (void**)&theFileP, &theLen);
Assert(theFileP != NULL);
Assert(theLen == sizeof(QTSS_Object));
UInt32* theReadOffsetP = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sReadOffsetAttr, 0, (void**)&theReadOffsetP, &theLen);
Assert(theReadOffsetP != NULL);
Assert(theLen == sizeof(UInt32));
UInt32* theWriteOffsetP = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sWriteOffsetAttr, 0, (void**)&theWriteOffsetP, &theLen);
Assert(theWriteOffsetP != NULL);
Assert(theLen == sizeof(UInt32));
UInt32 theReadOffset = *theReadOffsetP;
UInt32 theWriteOffset = *theWriteOffsetP;
UInt32 theState = *theStateP;
Bool16 isBlocked = false;
// Get the length of the file onto the stack
theLen = sizeof(UInt64);
UInt64 theFileLength = 0;
theErr = QTSS_GetValue(*theFileP, qtssFlObjLength, 0, (void*)&theFileLength, &theLen);
Assert(theErr == QTSS_NoErr);
Assert(theLen == sizeof(UInt64));
// Get the offset in the file onto the stack
theLen = sizeof(UInt64);
UInt64 theOffset = 0;
theErr = QTSS_GetValue(*theFileP, qtssFlObjPosition, 0, (void*)&theOffset, &theLen);
Assert(theErr == QTSS_NoErr);
Assert(theLen == sizeof(UInt64));
while (!isBlocked)
{
// If we have less than the full buffer size left to go in the file,
// down adjust our buffer size to be the amount of data remaining in the file
UInt32 theBufferSize = sFileBufSize;
if ((theFileLength - theOffset) < sFileBufSize)
theBufferSize = (UInt32) (theFileLength - theOffset);
switch (theState)
{
case kReadingBufferState:
{
// Read as much data as possible out of the file
UInt32 theRecvLen = 0;
(void)QTSS_Read(*theFileP,
(*theFileBufferP) + theReadOffset,
theBufferSize - theReadOffset,
&theRecvLen);
theReadOffset += theRecvLen;
theOffset += theRecvLen;
#if RAW_FILE_DEBUGGING
qtss_printf("Got %"_U32BITARG_" bytes back from file read. Now at: %"_64BITARG_"u\n", theRecvLen, theOffset);
#endif
if (theReadOffset < theBufferSize)
{
#if RAW_FILE_DEBUGGING
qtss_printf("Flow controlled on file. Waiting for read event\n");
#endif
isBlocked = true;
break;
}
theReadOffset = 0;
Assert(theWriteOffset == 0);
theState = kWritingBufferState;
}
case kWritingBufferState:
{
UInt32 theWrittenLen = 0;
// for debugging purposes, construct an IOVec out of this data
iovec theVec[5];
UInt32 units = (theBufferSize - theWriteOffset) /4;
UInt32 offset = theWriteOffset;
theVec[1].iov_base = (*theFileBufferP) + offset;
theVec[1].iov_len = units;
offset += units;
theVec[2].iov_base = (*theFileBufferP) + offset;
theVec[2].iov_len = units;
offset += units;
theVec[3].iov_base = (*theFileBufferP) + offset;
theVec[3].iov_len = units;
offset += units;
theVec[4].iov_base = (*theFileBufferP) + offset;
theVec[4].iov_len = theBufferSize - offset;
(void)QTSS_WriteV( inParams->inRTSPSession,
theVec, 5,
theBufferSize - theWriteOffset,
&theWrittenLen);
theWriteOffset += theWrittenLen;
#if RAW_FILE_DEBUGGING
qtss_printf("Got %"_U32BITARG_" bytes back from socket write.\n", theWrittenLen);
#endif
if (theWriteOffset < theBufferSize)
{
#if RAW_FILE_DEBUGGING
qtss_printf("Flow controlled on socket. Waiting for write event.\n");
#endif
isBlocked = true;
break;
}
// Check to see if we're done. If we are, delete stuff and return
if (theOffset == theFileLength)
{
#if RAW_FILE_DEBUGGING
qtss_printf("File transfer complete\n");
#endif
return QTSS_NoErr;
}
theWriteOffset = 0;
Assert(theReadOffset == 0);
theState = kReadingBufferState;
}
}
}
Assert(isBlocked);
// We've reached a blocking condition for some reason.
// Save our state, request an event, and return.
(void)QTSS_SetValue(inParams->inRTSPSession, sReadOffsetAttr, 0, &theReadOffset, sizeof(theReadOffset));
(void)QTSS_SetValue(inParams->inRTSPSession, sWriteOffsetAttr, 0, &theWriteOffset, sizeof(theWriteOffset));
(void)QTSS_SetValue(inParams->inRTSPSession, sStateAttr, 0, &theState, sizeof(theState));
// If we're reading, wait for the file to become readable
if (theState == kReadingBufferState)
(void)QTSS_RequestEvent(*theFileP, QTSS_ReadableEvent);
// If we're writing, wait for the socket to become writable
else
(void)QTSS_RequestEvent(inParams->inRTSPSession, QTSS_WriteableEvent);
return QTSS_NoErr;
}
QTSS_Error CloseRTSPSession(QTSS_RTSPSession_Params* inParams)
{
// In this role, the allocated resources are deleted before closing the RTSP session
UInt32 theLen = 0;
// Get our file buffer pointer and delete it
char** theFileBufferP = NULL; // File buffer pointer
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileBufferAttr, 0, (void**)&theFileBufferP, &theLen);
if (theFileBufferP != NULL)
delete [] *theFileBufferP;
QTSS_Object* theFileP = NULL;
// Get our file pointer and delete it
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileAttr, 0, (void**)&theFileP, &theLen);
Assert(theFileP != NULL);
Assert(theLen == sizeof(QTSS_Object));
(void)QTSS_CloseFileObject(*theFileP);
return QTSS_NoErr;
}

View file

@ -0,0 +1,47 @@
/*
*
* @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: QTSSRawFileModule.h
Contains: A module that returns the entire contents of a file to the client.
Only does this if the suffix of the file is .raw
*/
#ifndef __QTSS_RAW_FILE_MODULE_H__
#define __QTSS_RAW_FILE_MODULE_H__
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSRawFileModule_Main(void* inPrivateArgs);
}
#endif // __QTSS_RAW_FILE_MODULE_H__

View file

@ -0,0 +1 @@
Installing the QTSSRawFileModule To use QTSSRawFileModule, add it to your QTSSModules folder, and restart the QuickTime Streaming Server. What does QTSSRawFileModule do? Users of QTSSRawFileModule should be familiar with the RTSP protocol. This protocol is documented as RFC 2326. http://www.landfield.com/rfcs/rfc2236.html When an RTSP client issues a DESCRIBE, the QTSSRawFileModule will examine the request URL. If the suffix of the filename in the URL is ".raw", the module will look for a file in the location specified by the URL. If this file exists, the module will return the entire contents of the file to the client as the RTSP response. QTSSRawFileModule will not prepend RTSP headers to the response, so the "Raw File" itself must include headers. For example: Request URL: rtsp://streaming.site.com/subdir/rawfile.raw QTSSRawFileModule looks for: <movies directory>/subdir/rawfile.raw If there is a file at that location, QTSSRawFileModule will send its contents to the client as the RTSP response. QTSSRawFileModule also allows you to override default server behavior for URLs that don't have a ".raw" suffix. On every DESCRIBE sent to the server, QTSSRawFileModule appends a ".raw" to the end of the URL, no matter what is in the URL. If it finds a file at that location, it will return the contents of the file to the client as the RTSP response. For example: Request URL: rtsp://streaming.site.com/dir/example.mov QTSSRawFileModule looks for: <movies directory>/dir/example.mov.raw If there is a file at that location, QTSSRawFileModule will send its contents to the client as the RTSP response. How to use QTSSRawFileModule to send redirects: A redirect is a special type of RTSP response that tells the client to reissue the same request with a different URL. The new URL is specified in the "Location" header of the redirect response. This is extremely handy if content gets moved around on a site, and the administrator doesn't want the old links to break. The following is a sample redirect response: RTSP/1.0 302 Found CSeq: 1 Server: QTSS/2.0 Location: rtsp://streaming.site.com/new/newexample.mov To have QTSSRawFileModule issue a redirect, place the contents of the above redirect response into a text file, give it a name with a .raw suffix, and place it somewhere inside your media folder. For example, if it is called "example.mov.raw", and placed directly inside the media folder, when the client issues a DESCRIBE request for rtsp://streaming.site.com/example.mov The QTSSRawFileModule will redirect the client to the new URL, specified in the location header. The client will immediately send a DESCRIBE to the new URL.

View file

@ -0,0 +1,5 @@
RTSP/1.0 302 Found
CSeq: 1
Server: QTSS/2.0
Location: rtsp://www.myserver.com/mymovie

View file

@ -0,0 +1,420 @@
/*
*
* @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: QTSSRefMovieModule.cpp
Contains: Implements a module to create RTSP text ref movies
*/
#include "QTSSRefMovieModule.h"
#include "OS.h"
#include "OSMemory.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "ev.h"
#include "QTFile.h"
#include "QTSSModuleUtils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
//------------------------------------------------------------------------
// STATIC DATA
//------------------------------------------------------------------------
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_PrefsObject sServerPrefs = NULL;
static QTSS_ServerObject sServer = NULL;
// HTTP reply header
static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: QTSS/4.0\r\n"
"Connection: Close\r\nContent-Type: video/quicktime\r\n";
static Bool16 sRefMovieXferEnabled = true;
static Bool16 sDefaultRefMovieXferEnabled = true;
static UInt32 sServerIPAddr = 0x74000001; // 127.0.0.1
static UInt16 sRTSPReplyPort = 0;
static UInt16 sDefaultRTSPReplyPort = 0;
//------------------------------------------------------------------------
// FUNCTION PROTOTYPES
//------------------------------------------------------------------------
QTSS_Error QTSSRefMovieModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error RereadPrefs();
static QTSS_Error Filter(QTSS_Filter_Params* inParams);
static void url_strcpy(char* dest, const char* src);
static QTSS_Error SendTheResponse(QTSS_RTSPSessionObject theSession, QTSS_StreamRef stream, StrPtrLen& movie);
static Bool16 FileExists(StrPtrLen& path, StrPtrLen& movie);
static Bool16 IsHTTPGet(StrPtrLen& theRequest);
static Bool16 IsTunneledRTSP(StrPtrLen& theRequest);
static Bool16 IsAdminURL(StrPtrLen& theUrl);
static Bool16 ParseURL(StrPtrLen& theRequest, char* outURL, UInt16 maxlen);
static Bool16 IsHomeDirURL(StrPtrLen& theUrl);
//------------------------------------------------------------------------
// MODULE FUNCTIONS IMPLEMENTATION
//------------------------------------------------------------------------
QTSS_Error QTSSRefMovieModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSRefMovieModuleDispatch);
}
// Dispatch this module's role call back.
QTSS_Error QTSSRefMovieModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPFilter_Role:
return Filter(&inParams->rtspFilterParams);
}
return QTSS_NoErr;
}
// Handle the QTSS_Register role call back.
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
// Tell the server our name!
static char* sModuleName = "QTSSRefMovieModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
// Handle the QTSS_Initialize role call back.
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
QTSS_Error err = QTSS_NoErr;
UInt32 ulen = sizeof(sServerIPAddr);
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
// Get prefs object
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
sServerPrefs = inParams->inPrefs;
sServer = inParams->inServer;
// Get the Server's IP address for later use.
err = QTSS_GetValue(sServer, qtssSvrDefaultIPAddr, 0, &sServerIPAddr, &ulen);
err = RereadPrefs();
return err;
}
// Handle the QTSS_RereadPrefs_Role role call back.
QTSS_Error RereadPrefs()
{
QTSSModuleUtils::GetAttribute(sPrefs, "refmovie_xfer_enabled", qtssAttrDataTypeBool16,
&sRefMovieXferEnabled, &sDefaultRefMovieXferEnabled, sizeof(sRefMovieXferEnabled));
QTSSModuleUtils::GetAttribute(sPrefs, "refmovie_rtsp_port", qtssAttrDataTypeUInt16,
&sRTSPReplyPort, &sDefaultRTSPReplyPort, sizeof(sRTSPReplyPort));
return QTSS_NoErr;
}
// url_strcpy - works like strcpy except that it handles URL escape
// conversions as it copies the string.
void url_strcpy(char* dest, const char* src)
{
int c1, c2;
while(*src)
{
if (*src == '%')
{
src++;
c1 = *src++;
if (c1 >= '0' && c1 <= '9')
c1 -= '0';
else if (c1 >= 'A' && c1 <= 'F')
c1 -= 'A' + 10;
else if (c1 >= 'a' && c1 <= 'f')
c1 -= 'a' + 10;
else
c1 = 0;
c2 = *src;
if (c2 >= '0' && c2 <= '9')
c2 -= '0';
else if (c2 >= 'A' && c2 <= 'F')
c2 -= 'A' + 10;
else if (c2 >= 'a' && c2 <= 'f')
c2 -= 'a' + 10;
else
c2 = 32;
*dest = (c1 * 16) + c2;
}
else
{
*dest = *src;
}
dest++;
src++;
}
*dest = '\0';
}
// This sends the HTTP response to the server that contains the RTSPtext Ref movie
QTSS_Error SendTheResponse(QTSS_RTSPSessionObject theSession, QTSS_StreamRef stream, StrPtrLen& movie)
{
QTSS_Error err = QTSS_NoErr;
char theMovieFile[512];
theMovieFile[sizeof(theMovieFile) -1] = 0;
char tmp[600];
tmp[sizeof(tmp) -1] = 0;
char tmp2[80];
tmp2[sizeof(tmp2) -1] = 0;
UInt8 x1, x2, x3, x4;
// send the HTTP reply header to the client
err= QTSS_Write(stream, sResponseHeader, ::strlen(sResponseHeader), NULL, qtssWriteFlagsBufferData);
if (err != QTSS_NoErr)
return QTSS_NoErr;
UInt32 ip4address = 0;
UInt32 len = sizeof(ip4address);
err = QTSS_GetValue(theSession, qtssRTSPSesLocalAddr, 0, &ip4address, &len);
// Format the server IP address for building the RTSP address in the reply.
x1 = (UInt8)((ip4address >> 24) & 0xff);
x2 = (UInt8)((ip4address >> 16) & 0xff);
x3 = (UInt8)((ip4address >> 8) & 0xff);
x4 = (UInt8)((ip4address) & 0xff);
if (movie.Len > sizeof(theMovieFile) -1 )
movie.Len = sizeof(theMovieFile) -1;
::memcpy(theMovieFile, movie.Ptr, movie.Len);
theMovieFile[movie.Len] = '\0';
UInt16 port = sRTSPReplyPort;
if (0 == port)
{
len = sizeof(port);
err = QTSS_GetValue(theSession, qtssRTSPSesLocalPort, 0, &port, &len);
}
// construct the RTSP address reply string for the client.
qtss_snprintf(tmp,sizeof(tmp) -1, "rtsptext\r\nrtsp://%d.%d.%d.%d:%d%s\r\n", x1,x2,x3,x4, port, theMovieFile);
// send the 'Content-Length:' part of the HTTP reply
qtss_snprintf(tmp2, sizeof(tmp2) -1, "Content-Length: %d\r\n\r\n", (int) ::strlen(tmp));
err = QTSS_Write(stream, tmp2, ::strlen(tmp2), NULL, qtssWriteFlagsBufferData);
if (err != QTSS_NoErr)
return QTSS_NoErr;
// send the formatted RTSP reference part of the reply
err = QTSS_Write(stream, tmp, ::strlen(tmp), NULL, qtssWriteFlagsBufferData);
if (err != QTSS_NoErr)
return QTSS_NoErr;
// flush the pending write to the client.
err = QTSS_Flush(stream);
return err;
}
// This determines if the specified movie file
// exists at the designated path.
Bool16 FileExists(StrPtrLen& path, StrPtrLen& movie)
{
struct stat sb;
char fullpath[1024];
// if the movie path ends in a '/' then there is no movie file to be found.
// (This is probably a user error in typing the URL.)
if(movie.Ptr[movie.Len-1] == '/')
return false;
// copy path to our local buffer
::memcpy(fullpath, path.Ptr, path.Len);
// remove any URL escape characters when we contruct the full path.
::url_strcpy(fullpath+path.Len, (char*) movie.Ptr);
fullpath[path.Len+movie.Len] = '\0';
// check for file existance with the POSIX stat() function.
if (::stat(fullpath, &sb) != 0)
return false;
else
return true;
}
// This determines if an incoming request is an HTTP GET
// request.
Bool16 IsHTTPGet(StrPtrLen& theRequest)
{
StrPtrLen token = theRequest;
token.Len = 3;
return token.EqualIgnoreCase(StrPtrLen("GET"));
}
// This determines if an incoming request is actually
// an RTSP request tunneled in a HTTP request.
Bool16 IsTunneledRTSP(StrPtrLen& theRequest)
{
if (::strstr((char*)theRequest.Ptr, "x-rtsp-tunneled") != 0)
return true;
return false;
}
// This determines if a URL in an HTTP request is actually
// a server admin request.
Bool16 IsAdminURL(StrPtrLen& theUrl)
{
StrPtrLen token = theUrl;
token.Len = 15;
return token.EqualIgnoreCase(StrPtrLen("/modules/admin/"));
}
Bool16 IsHomeDirURL(StrPtrLen& theUrl)
{
StrPtrLen token = theUrl;
token.Len = 2;
return token.EqualIgnoreCase(StrPtrLen("/~"));
}
// Parse out the URL from the HTTP GET line.
Bool16 ParseURL(StrPtrLen& theRequest, char* outURL, UInt16 maxlen)
{
StringParser reqParse(&theRequest);
StrPtrLen strPtr;
::memset(outURL, 0, maxlen);
reqParse.ConsumeWord(&strPtr);
if ( !strPtr.Equal(StrPtrLen("GET")) )
{
return false;
}
reqParse.ConsumeWhitespace();
reqParse.ConsumeUntilWhitespace(&strPtr);
if (strPtr.Len == 0)
return false;
else if ((UInt16)strPtr.Len > maxlen-1)
strPtr.Len = maxlen-1;
::memcpy(outURL, strPtr.Ptr, strPtr.Len);
return true;
}
// Handle the QTSS_RTSPFilter_Role role call back.
QTSS_Error Filter(QTSS_Filter_Params* inParams)
{
QTSS_Error err = QTSS_NoErr;
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
QTSS_RTSPSessionObject theSession = inParams->inRTSPSession;
char theURL[512];
// If this module is disabled do nothing but return.
if (!sRefMovieXferEnabled)
return QTSS_NoErr;
// Get the full RTSP request from the server's attribute.
StrPtrLen theFullRequest;
err = QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len);
if (err != QTSS_NoErr)
{
return QTSS_NoErr;
}
// if this is not an HTTP GET then ignore it.
if (!IsHTTPGet(theFullRequest))
return QTSS_NoErr;
// If this is a tunneled RTSP request we ignore it.
if (IsTunneledRTSP(theFullRequest))
{
return QTSS_NoErr;
}
// if we can't parse out the URL then we just ignore this request.
if (!ParseURL(theFullRequest, theURL, 512))
{
return QTSS_NoErr;
}
// Make sure that this is not an admin request before
// we go any further.
StrPtrLen movie(theURL);
if (IsAdminURL(movie))
{
// The file path in the URL is actually an admin request.
// Just ignore it and let the admin module handle it.
return QTSS_NoErr;
}
Bool16 isHomeDir = IsHomeDirURL(movie);
// Get the server's movie folder location.
char* movieFolderString = NULL;
err = QTSS_GetValueAsString (sServerPrefs, qtssPrefsMovieFolder, 0, &movieFolderString);
if (err != QTSS_NoErr)
return QTSS_NoErr;
OSCharArrayDeleter movieFolder(movieFolderString);
StrPtrLen theMovieFolder(movieFolderString);
if (!isHomeDir && !FileExists(theMovieFolder, movie))
{
// we couldn't find a file at the specified location
// so we will ignore this HTTP request and let some other module
// deal with the issue.
return QTSS_NoErr;
}
else
{
// Eureka!!! We found a file at the specified location.
// We assume that it is a valid movie file and we send
// the client an RTSP text reference movie in the HTTP reply.
err = SendTheResponse(theSession,theRequest, movie);
}
return QTSS_NoErr;
}

View file

@ -0,0 +1,44 @@
/*
*
* @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: QTSSRefMovieModule.h
Contains: A module that serves an RTSP text ref movie from an HTTP request.
*/
#ifndef __QTSSREFMOVIEMODULE_H__
#define __QTSSREFMOVIEMODULE_H__
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSRefMovieModule_Main(void* inPrivateArgs);
}
#endif //__QTSSREFMOVIEMODULE_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
/*
*
* @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: QTSSReflectorModule.h
Contains: QTSS API module
*/
#ifndef _QTSSREFLECTORMODULE_H_
#define _QTSSREFLECTORMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSReflectorModule_Main(void* inPrivateArgs);
}
#endif //_QTSSREFLECTORMODULE_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
/*
*
* @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: QTSSReflectorModule.h
Contains: QTSS API module
*/
#ifndef _QTSSRELAYMODULE_H_
#define _QTSSRELAYMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSRelayModule_Main(void* inPrivateArgs);
}
#endif //_QTSSRELAYMODULE_H_

View file

@ -0,0 +1,667 @@
/*
*
* @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: QTSSSplitterModule.cpp
Contains: Implementation of QTSSSplitterModule class.
*/
#include "QTSSSplitterModule.h"
#include "QTSSModuleUtils.h"
#include "ReflectorSession.h"
#include "OSArrayObjectDeleter.h"
#include "OSMemory.h"
//ReflectorOutput objects
#include "RTPSessionOutput.h"
#include "RelayOutput.h"
//SourceInfo objects
#include "RTSPSourceInfo.h"
// Fixes
#include "QTSSMemoryDeleter.h"
#include "FilePrefsSource.h"
// ATTRIBUTES
static QTSS_AttributeID sOutputAttr = qtssIllegalAttrID;
static QTSS_AttributeID sSessionAttr = qtssIllegalAttrID;
static QTSS_AttributeID sStreamCookieAttr = qtssIllegalAttrID;
static QTSS_AttributeID sRemoteHostRespondedWithAnErrorErr = qtssIllegalAttrID;
static QTSS_AttributeID sRemoteHostRefusedConnectionErr = qtssIllegalAttrID;
static QTSS_AttributeID sExpectedDigitFilenameErr = qtssIllegalAttrID;
static QTSS_AttributeID sBadTrackIDErr = qtssIllegalAttrID;
// STATIC DATA
static const UInt32 kSessionStartingIdleTimeInMsec = 20;
static const StrPtrLen kCacheControlHeader("no-cache");
static QTSS_PrefsObject sServerPrefs = NULL;
static OSRefTable* sSessionMap = NULL;
// Important strings
static StrPtrLen sRCFSuffix(".rcf");
static StrPtrLen sRTSPSourceStr("relay_source");
// FUNCTION PROTOTYPES
static QTSS_Error QTSSSplitterModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams);
static ReflectorSession* FindOrCreateSession(StrPtrLen* inPath, QTSS_StandardRTSP_Params* inParams);
static QTSS_Error HandleSourceInfoErr(QTSS_Error rtspSourceInfoErr, QTSS_StandardRTSP_Params* inParams,
ReflectorSession* inSession, RTSPClient* inClient);
static void DeleteSessionOnError(ReflectorSession* inSession, QTSS_ClientSessionObject inCliSession);
static void NullOutSessionAttr(QTSS_ClientSessionObject inSession);
static QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession);
static QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession);
static QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams);
static void IssueTeardown(ReflectorSession* inSession);
static void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSSplitterModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSSplitterModuleDispatch);
}
QTSS_Error QTSSSplitterModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RTSPPreProcessor_Role:
return ProcessRTSPRequest(&inParams->rtspRequestParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession(&inParams->clientSessionClosingParams);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_Shutdown_Role);
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
// Add text messages attributes
static char* sRemoteHostRespondedWithAnErrorName = "QTSSSplitterModuleRemoteHostError";
static char* sRemoteHostRefusedConnectionName = "QTSSSplitterModuleRemoteHostRefused";
static char* sExpectedDigitFilenameName = "QTSSSplitterModuleExpectedDigitFilename";
static char* sBadTrackIDErrName = "QTSSSplitterModuleBadTrackID";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRemoteHostRespondedWithAnErrorName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRemoteHostRespondedWithAnErrorName, &sRemoteHostRespondedWithAnErrorErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionName, &sRemoteHostRefusedConnectionErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sExpectedDigitFilenameName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sExpectedDigitFilenameName, &sExpectedDigitFilenameErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sBadTrackIDErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sBadTrackIDErrName, &sBadTrackIDErr);
// Add an RTP session attribute for tracking ReflectorSession objects
static char* sOutputName = "QTSSSplitterModuleOutput";
static char* sSessionName= "QTSSSplitterModuleSession";
static char* sStreamCookieName = "QTSSSplitterModuleStreamCookie";
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sOutputName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sOutputName, &sOutputAttr);
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sSessionName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sSessionName, &sSessionAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamCookieName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamCookieName, &sStreamCookieAttr);
// Reflector stream needs to setup some parameters too.
ReflectorStream::Register();
// RTPSessionOutput needs to do the same
RTPSessionOutput::Register();
// Tell the server our name!
static char* sModuleName = "QTSSSplitterModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sSessionMap = NEW OSRefTable();
sServerPrefs = inParams->inPrefs;
//
// Instead of passing our own module prefs object, as one might expect,
// here we pass in the QTSSReflectorModule's, because the prefs that
// apply to ReflectorStream are stored in that module's prefs
StrPtrLen theReflectorModule("QTSSReflectorModule");
QTSS_ModulePrefsObject theReflectorPrefs =
QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName(theReflectorModule));
// Call helper class initializers
ReflectorStream::Initialize(theReflectorPrefs);
ReflectorSession::Initialize();
// Report to the server that this module handles DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN
static QTSS_RTSPMethod sSupportedMethods[] = { qtssDescribeMethod, qtssSetupMethod, qtssTeardownMethod, qtssPlayMethod, qtssPauseMethod };
QTSSModuleUtils::SetupSupportedMethods(inParams->inServer, sSupportedMethods, 5);
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
if ((QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0,
(void**)&theMethod, &theLen) != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)))
{
Assert(0);
return QTSS_RequestFailed;
}
if (*theMethod == qtssDescribeMethod)
return DoDescribe(inParams);
RTPSessionOutput** theOutput = NULL;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)))
return QTSS_RequestFailed;
switch (*theMethod)
{
case qtssSetupMethod:
return DoSetup(inParams, (*theOutput)->GetReflectorSession());
case qtssPlayMethod:
return DoPlay(inParams, (*theOutput)->GetReflectorSession());
case qtssTeardownMethod:
// Tell the server that this session should be killed, and send a TEARDOWN response
(void)QTSS_Teardown(inParams->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
break;
case qtssPauseMethod:
(void)QTSS_Pause(inParams->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
break;
default:
break;
}
return QTSS_NoErr;
}
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams)
{
QTSS_Error theErr = QTSS_NoErr;
// If this URL doesn't end with an .rcf, don't even bother. Ah, if only the QTSSReflectorModule
// could make this same check as well
StrPtrLen theURI;
(void)QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqURI, 0, (void**)&theURI.Ptr, &theURI.Len);
if ((theURI.Len < sRCFSuffix.Len) || (!sRCFSuffix.EqualIgnoreCase(&theURI.Ptr[theURI.Len - sRCFSuffix.Len], sRCFSuffix.Len)))
return QTSS_NoErr;
// Check and see if we are in the process of setting this connection up already
ReflectorSession* theSession = NULL;
UInt32 theLen = sizeof(ReflectorSession*);
(void)QTSS_GetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, &theLen);
if (theSession == NULL)
{
// If we are not already in the process of initializing and setting up this ReflectorSession,
// attempt to create one or attach to one.
// Check and see if the full path to this file matches an existing ReflectorSession
StrPtrLen thePathPtr;
OSCharArrayDeleter rcfPath(QTSSModuleUtils::GetFullPath( inParams->inRTSPRequest,
qtssRTSPReqFilePath,
&thePathPtr.Len, NULL));
thePathPtr.Ptr = rcfPath.GetObject();
theSession = FindOrCreateSession(&thePathPtr, inParams);
// If this function returned an error, this request shouldn't be handled by this module
if (theSession == NULL)
return QTSS_NoErr;
}
if (!((RTSPSourceInfo*)theSession->GetSourceInfo())->IsDescribeComplete())
{
// Only the session owner need worry about this code. this is an easy.
// Way of checking this.
Assert(theLen == sizeof(ReflectorSession*));
// If we haven't finished the describe yet, call
// Describe again on the RTSPSourceInfo object.
QTSS_Error theErr = ((RTSPSourceInfo*)theSession->GetSourceInfo())->Describe();
if (theErr != QTSS_NoErr)
return HandleSourceInfoErr(theErr, inParams, theSession,
((RTSPSourceInfo*)theSession->GetSourceInfo())->GetRTSPClient());
else
{
// Describe has completed. At this point we can setup the ReflectorSession.
// However, tell it not to consider this session completely setup yet, as
theErr = theSession->SetupReflectorSession(theSession->GetSourceInfo(), inParams, ReflectorSession::kDontMarkSetup);
if (theErr != QTSS_NoErr)
{
// If we get an error here, for some reason we couldn't bind the ports, etc, etc.
// Just abort
DeleteSessionOnError(theSession, inParams->inClientSession);
return theErr;
}
}
}
if (!theSession->IsSetup())
{
// Only the session owner need worry about this code. this is an easy.
// Way of checking this.
Assert(theLen == sizeof(ReflectorSession*));
// If we get here, the DESCRIBE has completed, but if we are the owner that isn't enough.
// We need to make sure that the SETUP and PLAY requests execute as well.
theErr = ((RTSPSourceInfo*)theSession->GetSourceInfo())->SetupAndPlay();
if (theErr != QTSS_NoErr)
return HandleSourceInfoErr(theErr, inParams, theSession,
((RTSPSourceInfo*)theSession->GetSourceInfo())->GetRTSPClient());
// We've completed the SETUP and PLAY process if we are here. The ReflectorSession
// is completely setup.
theSession->ManuallyMarkSetup();
// NULL out the sSessionAttr, we don't need it anymore.
NullOutSessionAttr(inParams->inClientSession);
}
//ok, we've found or setup the proper reflector session, create an RTPSessionOutput object,
//and add it to the session's list of outputs
RTPSessionOutput* theNewOutput = NEW RTPSessionOutput(inParams->inClientSession, theSession, sServerPrefs, sStreamCookieAttr );
theSession->AddOutput(theNewOutput, true);
// And vice-versa, store this reflector session in the RTP session.
(void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theNewOutput, sizeof(theNewOutput));
// Finally, send the DESCRIBE response
//above function has signalled that this request belongs to us, so let's respond
iovec theDescribeVec[2] = { 0 };
Assert(theSession->GetLocalSDP()->Ptr != NULL);
theDescribeVec[1].iov_base = theSession->GetLocalSDP()->Ptr;
theDescribeVec[1].iov_len = theSession->GetLocalSDP()->Len;
(void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
QTSSModuleUtils::SendDescribeResponse(inParams->inRTSPRequest, inParams->inClientSession,
&theDescribeVec[0], 2, theSession->GetLocalSDP()->Len);
return QTSS_NoErr;
}
ReflectorSession* FindOrCreateSession(StrPtrLen* inPath, QTSS_StandardRTSP_Params* inParams)
{
// This function assumes that inPath is NULL terminated
StrPtrLen theFileData;
RTSPSourceInfo* theInfo = NULL;
(void)QTSSModuleUtils::ReadEntireFile(inPath->Ptr, &theFileData);
if (theFileData.Len > 0)
theInfo = NEW RTSPSourceInfo(Socket::kNonBlockingSocketType);
else
return NULL;
// We need to interpret this file as a standard prefs file, so let the
// FilePrefsSource object parse it, then call ParsePrefs on the RTSPSourceInfo object,
// which will parse out the RCF metadata.
FilePrefsSource thePrefsSource(true);// Allow duplicates
(void)thePrefsSource.InitFromConfigFile(inPath->Ptr);
QTSS_Error theErr = theInfo->ParsePrefs(&thePrefsSource, 0);
if (theErr != QTSS_NoErr)
{
delete theInfo;
return NULL;
}
// Ok, look for a reflector session matching the URL specified in the RCF file.
// A unique broadcast is defined by the URL, the URL is the argument to resolve.
OSMutexLocker locker(sSessionMap->GetMutex());
OSRef* theSessionRef = sSessionMap->Resolve(theInfo->GetRTSPClient()->GetURL());
ReflectorSession* theSession = NULL;
if (theSessionRef == NULL)
{
//If this URL doesn't already have a reflector session, we must make a new
//one. We already have the proper sourceInfo object, so we only need to construct the session
theSession = NEW ReflectorSession(theInfo->GetRTSPClient()->GetURL(), theInfo);
//put the session's ID into the session map.
theErr = sSessionMap->Register(theSession->GetRef());
Assert(theErr == QTSS_NoErr);
//unless we do this, the refcount won't increment (and we'll delete the session prematurely
OSRef* debug = sSessionMap->Resolve(theInfo->GetRTSPClient()->GetURL());
Assert(debug == theSession->GetRef());
// Create a socket stream for the TCP socket in the RTSPClient object. The socket stream will
// allow this module to receive events on the socket
QTSS_StreamRef theSockStream = NULL;
theErr = QTSS_CreateStreamFromSocket(theInfo->GetRTSPClient()->GetSocket()->GetSocket()->GetSocketFD(), &theSockStream);
Assert(theErr == QTSS_NoErr);
Assert(theSockStream != NULL);
// Store the socket stream in the Reflector Session so we can get at it easily later on
theSession->SetSocketStream(theSockStream);
// This RTSP session is the "owner" of this ReflectorSession, and will be responsible
// for setting it up properly, so we should make sure this attribute gets set
UInt32 theLen = sizeof(theSession);
theErr = QTSS_SetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, theLen);
Assert(theErr == QTSS_NoErr);
}
else
{
// We aren't the owner of this ReflectorSession, and only the owner needs to keep this
// RTSPSourceInfo object around.
delete theInfo;
theSession = (ReflectorSession*)theSessionRef->GetObject();
if (!theSession->IsSetup())
{
// We are not the creator of this session, and it may not be setup yet. If it isn't,
// we should simply wait for it to be setup.
sSessionMap->Release(theSession->GetRef());
// Give the owner some time to finish setting it up.
(void)QTSS_SetIdleTimer(kSessionStartingIdleTimeInMsec);
return NULL; // There isn't a completed session... yet.............
}
}
Assert(theSession != NULL);
return theSession;
}
QTSS_Error HandleSourceInfoErr(QTSS_Error rtspSourceInfoErr, QTSS_StandardRTSP_Params* inParams,
ReflectorSession* inSession, RTSPClient* inClient)
{
// If we get an EAGAIN here, the DESCRIBE hasn't completed yet
if ((rtspSourceInfoErr == EAGAIN) || (rtspSourceInfoErr == EINPROGRESS))
{
// We're making an assumption here that inClient only uses one socket to connect to
// the server. We only have one stream, so we have to make that assumption.
// Note that it is not necessary to have any kind of timeout here, because the server
// naturally times out idle connections. If the server doesn't respond for awhile,
// this session will naturally go away
inClient->GetSocket()->GetSocket()->DontAutoCleanup();
RequestSocketEvent(inSession->GetSocketStream(), inClient->GetSocket()->GetEventMask());
return QTSS_NoErr; // We'll get called in the same method again when there is more work to do
}
// We've encountered a fatal error for this session, so delete it.
DeleteSessionOnError(inSession, inParams->inClientSession);
if (rtspSourceInfoErr == QTSS_RequestFailed)
{
// This happens if the remote host responded with an error.
char tempBuf[20];
qtss_sprintf(tempBuf, "%"_U32BITARG_"", inClient->GetStatus());
StrPtrLen tempBufPtr(&tempBuf[0]);
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerGatewayTimeout,
sRemoteHostRespondedWithAnErrorErr, &tempBufPtr);
}
else
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerGatewayTimeout,
sRemoteHostRefusedConnectionErr);
}
void DeleteSessionOnError(ReflectorSession* inSession, QTSS_ClientSessionObject inCliSession)
{
// Make sure to destroy the socket stream as well
Assert(inSession->GetSocketStream() != NULL);
(void)QTSS_DestroySocketStream(inSession->GetSocketStream());
OSMutexLocker locker (sSessionMap->GetMutex());
//decrement the ref count
sSessionMap->Release(inSession->GetRef());
// We are here if we are the owner of this session and we encountered an error
// while trying to setup the session. We have the session map mutex, so the
// refcount at this point *must* be 0.
Assert(inSession->GetRef()->GetRefCount() == 0);
sSessionMap->UnRegister(inSession->GetRef());
delete inSession;
// Make sure the session is NULLd out, because it's deleted now!
NullOutSessionAttr(inCliSession);
}
void NullOutSessionAttr(QTSS_ClientSessionObject inSession)
{
ReflectorSession* theNull = NULL;
UInt32 theLen = sizeof(theNull);
QTSS_Error theErr = QTSS_SetValue(inSession, sSessionAttr, 0, (void*)&theNull, theLen);
Assert(theErr == QTSS_NoErr);
}
QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession)
{
//unless there is a digit at the end of this path (representing trackID), don't
//even bother with the request
char* theDigitStr = NULL;
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr);
QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr);
if (theDigitStr == NULL)
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, sExpectedDigitFilenameErr);
UInt32 theTrackID = ::strtol(theDigitStr, NULL, 10);
QTSS_Error theErr = QTSS_NoErr;
// Get info about this trackID
SourceInfo::StreamInfo* theStreamInfo = inSession->GetSourceInfo()->GetStreamInfoByTrackID(theTrackID);
// If theStreamInfo is NULL, we don't have a legit track, so return an error
if (theStreamInfo == NULL)
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,
sBadTrackIDErr);
StrPtrLen* thePayloadName = &theStreamInfo->fPayloadName;
QTSS_RTPPayloadType thePayloadType = theStreamInfo->fPayloadType;
QTSS_RTPStreamObject newStream = NULL;
{
// Ok, this is completely crazy but I can't think of a better way to do this that's
// safe so we'll do it this way for now. Because the ReflectorStreams use this session's
// stream queue, we need to make sure that each ReflectorStream is not reflecting to this
// session while we call QTSS_AddRTPStream. One brutal way to do this is to grab each
// ReflectorStream's mutex, which will stop every reflector stream from running.
for (UInt32 x = 0; x < inSession->GetNumStreams(); x++)
inSession->GetStreamByIndex(x)->GetMutex()->Lock();
theErr = QTSS_AddRTPStream(inParams->inClientSession, inParams->inRTSPRequest, &newStream, 0);
for (UInt32 y = 0; y < inSession->GetNumStreams(); y++)
inSession->GetStreamByIndex(y)->GetMutex()->Unlock();
if (theErr != QTSS_NoErr)
return theErr;
}
// Set up dictionary items for this stream
theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadName, 0, thePayloadName->Ptr, thePayloadName->Len);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadType, 0, &thePayloadType, sizeof(thePayloadType));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrTrackID, 0, &theTrackID, sizeof(theTrackID));
Assert(theErr == QTSS_NoErr);
// Place the stream cookie in this stream for future reference
void* theStreamCookie = inSession->GetStreamCookie(theTrackID);
Assert(theStreamCookie != NULL);
theErr = QTSS_SetValue(newStream, sStreamCookieAttr, 0, &theStreamCookie, sizeof(theStreamCookie));
Assert(theErr == QTSS_NoErr);
// Set the number of quality levels.
static UInt32 sNumQualityLevels = ReflectorSession::kNumQualityLevels;
theErr = QTSS_SetValue(newStream, qtssRTPStrNumQualityLevels, 0, &sNumQualityLevels, sizeof(sNumQualityLevels));
Assert(theErr == QTSS_NoErr);
//send the setup response
(void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, newStream, 0);
return QTSS_NoErr;
}
QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession)
{
// Tell the session what the bitrate of this reflection is. This is nice for logging,
// it also allows the server to scale the TCP buffer size appropriately if we are
// interleaving the data over TCP. This must be set before calling QTSS_Play so the
// server can use it from within QTSS_Play
UInt32 bitsPerSecond = inSession->GetBitRate();
(void)QTSS_SetValue(inParams->inClientSession, qtssCliSesMovieAverageBitRate, 0, &bitsPerSecond, sizeof(bitsPerSecond));
//Server shouldn't send RTCP (reflector does it), but the server should append the server info app packet
QTSS_Error theErr = QTSS_Play(inParams->inClientSession, inParams->inRTSPRequest, qtssPlayFlagsAppendServerInfo);
if (theErr != QTSS_NoErr)
return theErr;
//and send a standard play response
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
return QTSS_NoErr;
}
QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
{
// Check and see if we are in the process of tearing down this connection already
ReflectorSession* theSession = NULL;
UInt32 theLen = sizeof(ReflectorSession*);
(void)QTSS_GetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, &theLen);
if (theSession != NULL)
IssueTeardown(theSession);
else
{
RTPSessionOutput** theOutput = NULL;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)) || (theOutput == NULL))
return QTSS_RequestFailed;
// This function removes the output from the ReflectorSession, then
// checks to see if the session should go away. If it should, this deletes it
theSession = (*theOutput)->GetReflectorSession();
theSession->RemoveOutput(*theOutput, true);
delete (*theOutput);
//check if the ReflectorSession should be deleted
//(it should if its ref count has dropped to 0)
OSMutexLocker locker (sSessionMap->GetMutex());
//decrement the ref count
sSessionMap->Release(theSession->GetRef());
if (theSession->GetRef()->GetRefCount() == 0)
{
sSessionMap->UnRegister(theSession->GetRef());
theLen = sizeof(theSession);
theErr = QTSS_SetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, theLen);
if (theErr != QTSS_NoErr)
delete theSession;
else
IssueTeardown(theSession);
}
}
return QTSS_NoErr;
}
void IssueTeardown(ReflectorSession* inSession)
{
// Tell the RTSPSourceInfo object to initiate or continue the Teardown process
QTSS_Error theErr = ((RTSPSourceInfo*)inSession->GetSourceInfo())->Teardown();
if ((theErr == EAGAIN) || (theErr == EINPROGRESS))
{
RTSPClient* theClient = ((RTSPSourceInfo*)inSession->GetSourceInfo())->GetRTSPClient();
theClient->GetSocket()->GetSocket()->DontAutoCleanup();
RequestSocketEvent(inSession->GetSocketStream(), theClient->GetSocket()->GetEventMask());
}
else
{
// Make sure to destroy the socket stream as well
Assert(inSession->GetSocketStream() != NULL);
(void)QTSS_DestroySocketStream(inSession->GetSocketStream());
delete inSession;
}
}
void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask)
{
//
// Job of this function is to convert a CommonUtilitiesLib event mask to a QTSS Event mask
QTSS_EventType theEvent = 0;
if (inEventMask & EV_RE)
theEvent |= QTSS_ReadableEvent;
if (inEventMask & EV_WR)
theEvent |= QTSS_WriteableEvent;
QTSS_Error theErr = QTSS_RequestEvent(inStream, theEvent);
Assert(theErr == QTSS_NoErr);
}

View file

@ -0,0 +1,43 @@
/*
*
* @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: QTSSSplitterModule.h
Contains: QTSS API module
*/
#ifndef _QTSSSPLITTERMODULE_H_
#define _QTSSSPLITTERMODULE_H_
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSSplitterModule_Main(void* inPrivateArgs);
}
#endif //_QTSSSPLITTERMODULE_H_

View file

@ -0,0 +1,248 @@
/*
*
* @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: RCFSourceInfo.cpp
Contains: Implementation of object defined in .h file
Copyright: © 1998 by Apple Computer, Inc., all rights reserved.
*/
#include "RCFSourceInfo.h"
#include "PrefsSource.h"
#include "StringParser.h"
#include "OSMemory.h"
#include "SocketUtils.h"
void RCFSourceInfo::SetName(const char* inName)
{
if (inName != NULL)
{
fName = NEW char[::strlen(inName) + 1];
::strcpy(fName, inName);
}
}
RCFSourceInfo::~RCFSourceInfo()
{
if (fName != NULL)
delete fName;
// Not necessary anymore as the destructor of the base class will take care
// of deleting all allocated memory for fOutputArray and fStreamArray
/*
if (fOutputArray != NULL)
{
for (UInt32 x = 0; x < fNumOutputs; x++)
delete [] fOutputArray[x].fPortArray;
char* theOutputArray = (char*)fOutputArray;
delete [] theOutputArray;
}
if (fStreamArray != NULL)
{
char* theStreamArray = (char*)fStreamArray;
delete [] theStreamArray;
}
*/
}
void RCFSourceInfo::Parse(XMLTag* relayTag)
{
XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
if (sourceTag == NULL)
return;
fNumStreams = 0;
UInt32 destIPAddr = 0;
UInt32 srcIPAddr = 0;
UInt16 ttl = 0;
XMLTag* prefTag;
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "in_addr");
if (prefTag != NULL)
{
char* destAddrStr = prefTag->GetValue();
if (destAddrStr != NULL)
destIPAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "source_addr");
if (prefTag != NULL)
{
char* srcAddrStr = prefTag->GetValue();
if (srcAddrStr != NULL)
srcIPAddr = SocketUtils::ConvertStringToAddr(srcAddrStr);
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "ttl");
if (prefTag != NULL)
{
char* ttlStr = prefTag->GetValue();
if (ttlStr != NULL)
ttl = atoi(ttlStr);
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("LIST-PREF", "NAME", "udp_ports");
if (prefTag != NULL)
{
fNumStreams = prefTag->GetNumEmbeddedTags();
// Allocate a proper sized stream array
fStreamArray = NEW StreamInfo[fNumStreams];
for (UInt32 x = 0; x < fNumStreams; x++)
{
XMLTag* portTag = prefTag->GetEmbeddedTagByName("VALUE", x);
int port = 0;
if (portTag != NULL)
{
char* portStr = portTag->GetValue();
if (portStr != NULL)
port = atoi(portStr);
}
// Setup all the StreamInfo structures
fStreamArray[x].fSrcIPAddr = srcIPAddr;
fStreamArray[x].fDestIPAddr = destIPAddr;
fStreamArray[x].fPort = port;
fStreamArray[x].fTimeToLive = ttl;
fStreamArray[x].fPayloadType = qtssUnknownPayloadType;
fStreamArray[x].fTrackID = x+1;
}
}
// Now go through all the relay_destination lines (starting from the next line after the
// relay_source line.
this->ParseRelayDestinations(relayTag);
}
void RCFSourceInfo::ParseRelayDestinations(XMLTag* relayTag)
{
// parse the NAME attribute of the relay tag and store it in the relayname attribute
char* name = relayTag->GetAttributeValue("NAME");
if (name != NULL)
{
fName = NEW char[::strlen(name) + 1];
::strcpy(fName, name);
}
UInt32 numTags = relayTag->GetNumEmbeddedTags();
AllocateOutputArray(numTags); // not all these are relay tags, but most are
// Now actually go through and figure out what to put into these OutputInfo structures,
// based on what's on the relay_destination line
fNumOutputs = 0;
for (UInt32 y = 0; y < numTags; y++)
{
XMLTag* destTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "destination", y);
if (destTag == NULL)
return;
char* destType = destTag->GetAttributeValue("TYPE");
if (destType == NULL)
return;
if (!strcmp(destType, "udp_destination"))
ParseDestination(destTag, y);
else if (!strcmp(destType, "announced_destination"))
ParseAnnouncedDestination(destTag, y);
fNumOutputs++;
}
}
void RCFSourceInfo::ParseDestination(XMLTag* destTag, UInt32 index)
{
XMLTag* prefTag;
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "out_addr");
if (prefTag != NULL)
{
char* outAddrStr = prefTag->GetValue();
if (outAddrStr != NULL)
fOutputArray[index].fLocalAddr = SocketUtils::ConvertStringToAddr(outAddrStr);
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "dest_addr");
if (prefTag != NULL)
{
char* destAddrStr = prefTag->GetValue();
if (destAddrStr != NULL)
fOutputArray[index].fDestAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "ttl");
if (prefTag != NULL)
{
char* ttlStr = prefTag->GetValue();
if (ttlStr != NULL)
fOutputArray[index].fTimeToLive = atoi(ttlStr);
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("LIST-PREF", "NAME", "udp_ports");
if (prefTag != NULL)
{
fOutputArray[index].fNumPorts = prefTag->GetNumEmbeddedTags();
fOutputArray[index].fPortArray = NEW UInt16[fOutputArray[index].fNumPorts];
::memset(fOutputArray[index].fPortArray, 0, fOutputArray[index].fNumPorts * sizeof(UInt16));
for (UInt32 x = 0; x < fOutputArray[index].fNumPorts; x++)
{
XMLTag* portTag = prefTag->GetEmbeddedTagByName("VALUE", x);
if (portTag != NULL)
{
char* portStr = portTag->GetValue();
if (portStr != NULL)
{
fOutputArray[index].fPortArray[x] = atoi(portStr);
}
}
}
}
else
{
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "udp_base_port");
if(prefTag == NULL)
qtss_printf("Missing both 'udp_base_port' and 'udp_ports' tags.\n Cannot set up the destination!\n");
else
{
char* basePortStr = prefTag->GetValue();
if (basePortStr != NULL)
fOutputArray[index].fBasePort = atoi(basePortStr);
}
}
}
void RCFSourceInfo::ParseAnnouncedDestination(XMLTag* destTag, UInt32 index)
{
// should log some sort of error
// can't announce without an sdp
}
void RCFSourceInfo::AllocateOutputArray(UInt32 numOutputs)
{
// Allocate the proper number of relay outputs
fOutputArray = NEW OutputInfo[numOutputs];
}

View file

@ -0,0 +1,72 @@
/*
*
* @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: RCFSourceInfo.h
Contains: This object takes input RCF data, and uses it to support the SourceInfo
API.
*/
#ifndef __RCF_SOURCE_INFO_H__
#define __RCF_SOURCE_INFO_H__
#include "StrPtrLen.h"
#include "SourceInfo.h"
#include "XMLParser.h"
#include "StringParser.h"
class RCFSourceInfo : public SourceInfo
{
public:
// Uses the SDP Data to build up the StreamInfo structures
RCFSourceInfo() : fName(NULL) {}
RCFSourceInfo(XMLTag* relayTag) : fName(NULL) { Parse(relayTag); }
RCFSourceInfo(const RCFSourceInfo& copy):SourceInfo(copy) { this->SetName(copy.fName); }
virtual ~RCFSourceInfo();
// Parses out the SDP file provided, sets up the StreamInfo structures
void Parse(XMLTag* relayTag);
// Parses relay_destination lines and builds OutputInfo structs
void ParseRelayDestinations(XMLTag* relayTag);
void SetName(const char* inName);
char* Name() { return fName; }
protected:
virtual void ParseDestination(XMLTag* destTag, UInt32 index);
virtual void ParseAnnouncedDestination(XMLTag* destTag, UInt32 index);
virtual void AllocateOutputArray(UInt32 numOutputs);
char* fName;
};
#endif // __SDP_SOURCE_INFO_H__

View file

@ -0,0 +1,779 @@
/*
*
* @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: RTSPReflectorOutput.cpp
Contains: Implementation of object in .h file
*/
#include "RTPSessionOutput.h"
#include "ReflectorStream.h"
#include <errno.h>
#if DEBUG
#define RTP_SESSION_DEBUGGING 0
#else
#define RTP_SESSION_DEBUGGING 0
#endif
// ATTRIBUTES
static QTSS_AttributeID sStreamPacketCountAttr = qtssIllegalAttrID;
static QTSS_AttributeID sNextSeqNumAttr = qtssIllegalAttrID;
static QTSS_AttributeID sSeqNumOffsetAttr = qtssIllegalAttrID;
static QTSS_AttributeID sLastQualityChangeAttr = qtssIllegalAttrID;
static QTSS_AttributeID sLastRTPPacketIDAttr = qtssIllegalAttrID;
static QTSS_AttributeID sLastRTCPPacketIDAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFirstRTCPCurrentTimeAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFirstRTCPArrivalTimeAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFirstRTCPTimeStampAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFirstRTPCurrentTimeAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFirstRTPArrivalTimeAttr = qtssIllegalAttrID;
static QTSS_AttributeID sFirstRTPTimeStampAttr = qtssIllegalAttrID;
static QTSS_AttributeID sBaseRTPTimeStampAttr = qtssIllegalAttrID;
static QTSS_AttributeID sBaseArrivalTimeStampAttr = qtssIllegalAttrID;
static QTSS_AttributeID sStreamSSRCAttr = qtssIllegalAttrID;
static QTSS_AttributeID sStreamByteCountAttr = qtssIllegalAttrID;
static QTSS_AttributeID sLastRTPTimeStampAttr = qtssIllegalAttrID;
static QTSS_AttributeID sLastRTCPTransmitAttr = qtssIllegalAttrID;
RTPSessionOutput::RTPSessionOutput(QTSS_ClientSessionObject inClientSession, ReflectorSession* inReflectorSession,
QTSS_Object serverPrefs, QTSS_AttributeID inCookieAddrID)
: fClientSession(inClientSession),
fReflectorSession(inReflectorSession),
fCookieAttrID(inCookieAddrID),
fBufferDelayMSecs(ReflectorStream::sOverBufferInMsec),
fBaseArrivalTime(0),
fIsUDP(false),
fTransportInitialized(false),
fMustSynch(true),
fPreFilter(true)
{
// create a bookmark for each stream we'll reflect
this->InititializeBookmarks( inReflectorSession->GetNumStreams() );
}
void RTPSessionOutput::Register()
{
// Add some attributes to QTSS_RTPStream dictionary
static char* sNextSeqNum = "qtssNextSeqNum";
static char* sSeqNumOffset = "qtssSeqNumOffset";
static char* sLastQualityChange = "qtssLastQualityChange";
static char* sLastRTPPacketID = "qtssReflectorStreamLastRTPPacketID";
static char* sLastRTCPPacketID = "qtssReflectorStreamLastRTCPPacketID";
static char* sFirstRTCPArrivalTime = "qtssReflectorStreamStartRTCPArrivalTime";
static char* sFirstRTCPTimeStamp = "qtssReflectorStreamStartRTCPTimeStamp";
static char* sFirstRTCPCurrentTime = "qtssReflectorStreamStartRTCPCurrent";
static char* sFirstRTPArrivalTime = "qtssReflectorStreamStartRTPArrivalTime";
static char* sFirstRTPTimeStamp = "qtssReflectorStreamStartRTPTimeStamp";
static char* sFirstRTPCurrentTime = "qtssReflectorStreamStartRTPCurrent";
static char* sBaseRTPTimeStamp = "qtssReflectorStreamBaseRTPTimeStamp";
static char* sBaseArrivalTimeStamp = "qtssReflectorStreamBaseArrivalTime";
static char* sLastRTPTimeStamp = "qtssReflectorStreamLastRTPTimeStamp";
static char* sLastRTCPTransmit = "qtssReflectorStreamLastRTCPTransmit";
static char* sStreamSSRC = "qtssReflectorStreamSSRC";
static char* sStreamPacketCount = "qtssReflectorStreamPacketCount";
static char* sStreamByteCount = "qtssReflectorStreamByteCount";
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTCPTransmit, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTCPTransmit, &sLastRTCPTransmitAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sNextSeqNum, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sNextSeqNum, &sNextSeqNumAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sSeqNumOffset, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sSeqNumOffset, &sSeqNumOffsetAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastQualityChange, NULL, qtssAttrDataTypeSInt64);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastQualityChange, &sLastQualityChangeAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTPPacketID, NULL, qtssAttrDataTypeUInt64);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTPPacketID, &sLastRTPPacketIDAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTCPPacketID, NULL, qtssAttrDataTypeUInt64);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTCPPacketID, &sLastRTCPPacketIDAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTPTimeStamp, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTPTimeStamp, &sLastRTPTimeStampAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTCPArrivalTime, NULL, qtssAttrDataTypeSInt64);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTCPArrivalTime, &sFirstRTCPArrivalTimeAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTCPTimeStamp, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTCPTimeStamp, &sFirstRTCPTimeStampAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTCPCurrentTime, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTCPCurrentTime, &sFirstRTCPCurrentTimeAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTPCurrentTime, NULL, qtssAttrDataTypeSInt64);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTPCurrentTime, &sFirstRTPCurrentTimeAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTPArrivalTime, NULL, qtssAttrDataTypeSInt64);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTPArrivalTime, &sFirstRTPArrivalTimeAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTPTimeStamp, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTPTimeStamp, &sFirstRTPTimeStampAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sBaseRTPTimeStamp, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sBaseRTPTimeStamp, &sBaseRTPTimeStampAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sBaseArrivalTimeStamp, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sBaseArrivalTimeStamp, &sBaseArrivalTimeStampAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamSSRC, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamSSRC, &sStreamSSRCAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamPacketCount, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamPacketCount, &sStreamPacketCountAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamByteCount, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamByteCount, &sStreamByteCountAttr);
}
Bool16 RTPSessionOutput::IsPlaying()
{
QTSS_RTPSessionState* theState = NULL;
UInt32 theLen = 0;
if (!fClientSession)
return false;
(void)QTSS_GetValuePtr(fClientSession, qtssCliSesState, 0, (void**)&theState, &theLen);
if (theLen == 0 || theState == NULL || *theState != qtssPlayingState)
return false;
return true;
}
void RTPSessionOutput::InitializeStreams()
{
UInt32 theLen = 0;
QTSS_RTPStreamObject* theStreamPtr = NULL;
UInt32 packetCountInitValue = 0;
for (SInt16 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&theStreamPtr, &theLen) == QTSS_NoErr; z++)
{
(void) QTSS_SetValue(*theStreamPtr, sStreamPacketCountAttr, 0, &packetCountInitValue, sizeof(UInt32));
}
}
Bool16 RTPSessionOutput::IsUDP()
{
if (fTransportInitialized)
return fIsUDP;
QTSS_RTPSessionState* theState = NULL;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(fClientSession, qtssCliSesState, 0, (void**)&theState, &theLen);
if (*theState != qtssPlayingState)
return true;
QTSS_RTPStreamObject *theStreamPtr = NULL;
QTSS_RTPTransportType *theTransportTypePtr = NULL;
for (SInt16 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&theStreamPtr, &theLen) == QTSS_NoErr; z++)
{
(void) QTSS_GetValuePtr(*theStreamPtr, qtssRTPStrTransportType, 0, (void**) &theTransportTypePtr, &theLen);
if (theTransportTypePtr && *theTransportTypePtr == qtssRTPTransportTypeUDP)
{
fIsUDP = true;
break; // treat entire session UDP
}
else
{
fIsUDP = false;
}
}
//if (fIsUDP) printf("RTPSessionOutput::RTPSessionOutput Standard UDP client\n");
//else printf("RTPSessionOutput::RTPSessionOutput Buffered Client\n");
fTransportInitialized = true;
return fIsUDP;
}
Bool16 RTPSessionOutput::FilterPacket(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacket)
{
UInt32* packetCountPtr = NULL;
UInt32 theLen = 0;
//see if we started sending and if so then just keep sending (reset on a play)
QTSS_Error writeErr = QTSS_GetValuePtr(*theStreamPtr, sStreamPacketCountAttr, 0,(void**) &packetCountPtr,&theLen);
if (writeErr == QTSS_NoErr && theLen > 0 && *packetCountPtr > 0)
return false;
Assert(theStreamPtr);
Assert(inPacket);
UInt16 seqnum = this->GetPacketSeqNumber(inPacket);
UInt16 firstSeqNum = 0;
theLen = sizeof(firstSeqNum);
if ( QTSS_NoErr != QTSS_GetValue(*theStreamPtr, qtssRTPStrFirstSeqNumber, 0, &firstSeqNum, &theLen) )
return true;
if ( seqnum < firstSeqNum )
{
//printf("RTPSessionOutput::FilterPacket don't send packet = %u < first=%lu\n", seqnum, firstSeqNum);
return true;
}
//printf("RTPSessionOutput::FilterPacket found first packet = %u \n", firstSeqNum);
fPreFilter = false;
return fPreFilter;
}
Bool16 RTPSessionOutput::PacketAlreadySent(QTSS_RTPStreamObject *theStreamPtr, UInt32 inFlags, UInt64* packetIDPtr)
{
Assert(theStreamPtr);
Assert(packetIDPtr);
UInt32 theLen = 0;
UInt64 *lastPacketIDPtr = NULL;
Bool16 packetSent = false;
if (inFlags & qtssWriteFlagsIsRTP)
{
if ( (QTSS_NoErr == QTSS_GetValuePtr(*theStreamPtr, sLastRTPPacketIDAttr, 0, (void**)&lastPacketIDPtr, &theLen))
&& (*packetIDPtr <= *lastPacketIDPtr)
)
{
//printf("RTPSessionOutput::WritePacket Don't send RTP packet id =%qu\n", *packetIDPtr);
packetSent = true;
}
} else if (inFlags & qtssWriteFlagsIsRTCP)
{
if ( QTSS_NoErr == QTSS_GetValuePtr(*theStreamPtr, sLastRTCPPacketIDAttr, 0, (void**)&lastPacketIDPtr, &theLen)
&& (*packetIDPtr <= *lastPacketIDPtr)
)
{
//printf("RTPSessionOutput::WritePacket Don't send RTCP packet id =%qu last packet sent id =%qu\n", *packetIDPtr,*lastPacketIDPtr);
packetSent = true;
}
}
return packetSent;
}
Bool16 RTPSessionOutput::PacketReadyToSend(QTSS_RTPStreamObject *theStreamPtr,SInt64 *currentTimePtr, UInt32 inFlags, UInt64* packetIDPtr, SInt64* timeToSendThisPacketAgainPtr)
{
return true;
}
QTSS_Error RTPSessionOutput::TrackRTCPBaseTime(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
{
Bool16 haveBaseTime = false;
Bool16 haveAllFirstRTPs = true;
UInt32 streamTimeScale = 0;
UInt32 theLen = sizeof(streamTimeScale);
QTSS_Error writeErr = QTSS_GetValue(*theStreamPtr, qtssRTPStrTimescale, 0, (void *) &streamTimeScale, &theLen);
Assert(writeErr == QTSS_NoErr);
UInt32 baseTimeStamp = 0;
theLen = sizeof(baseTimeStamp);
if (!fMustSynch || QTSS_NoErr == QTSS_GetValue(*theStreamPtr, sBaseRTPTimeStampAttr, 0, (void*)&baseTimeStamp, &theLen) ) // we need a starting stream time that is synched
{
haveBaseTime = true;
}
else
{
UInt64 earliestArrivalTime = ~(UInt64) 0; //max value
UInt32 firstStreamTime = 0;
SInt64 firstStreamArrivalTime = 0;
QTSS_RTPStreamObject *findStream = NULL;
if (fMustSynch || QTSS_NoErr != QTSS_GetValuePtr(*theStreamPtr, sBaseArrivalTimeStampAttr, 0, (void**)&fBaseArrivalTime, &theLen) )
{ // we don't have a base arrival time for the session see if we can set one now.
for (SInt32 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&findStream, &theLen) == QTSS_NoErr; z++)
{
SInt64* firstArrivalTimePtr = NULL;
if (QTSS_NoErr != QTSS_GetValuePtr(*findStream, sFirstRTPArrivalTimeAttr, 0, (void**)&firstArrivalTimePtr, &theLen))
{// no packet on this stream yet
haveAllFirstRTPs = false; // not enough info to calc a base time
break;
}
else
{ // we have an arrival time see if it is the first for all streams
if ( (UInt64) *firstArrivalTimePtr < earliestArrivalTime )
{
earliestArrivalTime = *firstArrivalTimePtr;
}
}
}
if (haveAllFirstRTPs) // we can now create a base arrival time and base stream time from that
{
writeErr = QTSS_SetValue(*theStreamPtr, sBaseArrivalTimeStampAttr, 0, &earliestArrivalTime, sizeof(SInt64));
Assert(writeErr == QTSS_NoErr);
fBaseArrivalTime = (SInt64) earliestArrivalTime;
}
}
if (haveAllFirstRTPs)//sBaseRTPTimeStamp
{ // we don't have a base stream time but we have a base session time so calculate the base stream time.
theLen = sizeof(firstStreamTime);
if (QTSS_NoErr != QTSS_GetValue(*theStreamPtr, sFirstRTPTimeStampAttr, 0, (void*)&firstStreamTime, &theLen))
return QTSS_NoErr;
theLen = sizeof(firstStreamArrivalTime);
if (QTSS_NoErr != QTSS_GetValue(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, (void*)&firstStreamArrivalTime, &theLen))
return QTSS_NoErr;
SInt64 arrivalTimeDiffMSecs = (firstStreamArrivalTime - fBaseArrivalTime);// + fBufferDelayMSecs;//add the buffer delay !! not sure about faster than real time arrival times....
UInt32 timeDiffStreamTime = (UInt32)( ( (Float64) arrivalTimeDiffMSecs/(Float64) 1000.0) * (Float64) streamTimeScale );
baseTimeStamp = firstStreamTime - timeDiffStreamTime;
if (QTSS_NoErr == QTSS_SetValue(*theStreamPtr, sBaseRTPTimeStampAttr, 0, (void*)&baseTimeStamp, sizeof(baseTimeStamp)))
haveBaseTime = true;
(void) QTSS_SetValue(*theStreamPtr, qtssRTPStrFirstTimestamp, 0, &baseTimeStamp, sizeof(baseTimeStamp));
fMustSynch = false;
//printf("fBaseArrivalTime =%qd baseTimeStamp %"_U32BITARG_" streamStartTime=%qd diff =%qd\n", fBaseArrivalTime, baseTimeStamp, firstStreamArrivalTime, arrivalTimeDiffMSecs);
}
}
return writeErr;
}
QTSS_Error RTPSessionOutput::RewriteRTCP(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
{
UInt32 theLen;
SInt64 firstRTPCurrentTime = 0;
theLen = sizeof(firstRTPCurrentTime);
QTSS_GetValue(*theStreamPtr, sFirstRTPCurrentTimeAttr, 0, (void*)&firstRTPCurrentTime, &theLen);
SInt64 firstRTPArrivalTime = 0;
theLen = sizeof(firstRTPArrivalTime);
QTSS_GetValue(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, (void*)&firstRTPArrivalTime, &theLen);
UInt32 rtpTime = 0;
theLen = sizeof(rtpTime);
QTSS_GetValue(*theStreamPtr, sFirstRTPTimeStampAttr, 0, (void*)&rtpTime, &theLen);
UInt32* theReport = (UInt32*) inPacketStrPtr->Ptr;
theReport+=2; // point to the NTP time stamp
SInt64* theNTPTimestampP = (SInt64*)theReport;
*theNTPTimestampP = OS::HostToNetworkSInt64(OS::TimeMilli_To_1900Fixed64Secs(*currentTimePtr)); // time now
UInt32 baseTimeStamp = 0;
theLen = sizeof(baseTimeStamp);
(void) QTSS_GetValue(*theStreamPtr, sBaseRTPTimeStampAttr, 0, (void*)&baseTimeStamp, &theLen); // we need a starting stream time that is synched
UInt32 streamTimeScale = 0;
theLen = sizeof(streamTimeScale);
QTSS_GetValue(*theStreamPtr, qtssRTPStrTimescale, 0, (void *) &streamTimeScale, &theLen);
SInt64 packetOffset = *currentTimePtr - fBaseArrivalTime; // real time that has passed
packetOffset -= (firstRTPCurrentTime - firstRTPArrivalTime); // less the initial buffer delay for this stream
if (packetOffset < 0)
packetOffset = 0;
Float64 rtpTimeFromStart = (Float64) packetOffset / (Float64) 1000.0;
UInt32 rtpTimeFromStartInScale = (UInt32) (Float64) ((Float64) streamTimeScale * rtpTimeFromStart);
//printf("rtptime offset time =%f in scale =%"_U32BITARG_"\n", rtpTimeFromStart, rtpTimeFromStartInScale );
theReport += 2; // point to the rtp time stamp of "now" synched and scaled in stream time
*theReport = htonl(baseTimeStamp + rtpTimeFromStartInScale);
theLen = sizeof(UInt32);
UInt32 packetCount = 0;
(void) QTSS_GetValue(*theStreamPtr, sStreamPacketCountAttr, 0, &packetCount,&theLen);
theReport += 1; // point to the rtp packets sent
*theReport = htonl(ntohl(*theReport) * 2);
UInt32 byteCount = 0;
(void) QTSS_GetValue(*theStreamPtr, sStreamByteCountAttr, 0, &byteCount,&theLen);
theReport += 1; // point to the rtp payload bytes sent
*theReport = htonl(ntohl(*theReport) * 2);
return QTSS_NoErr;
}
QTSS_Error RTPSessionOutput::TrackRTCPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
{
QTSS_Error writeErr = QTSS_NoErr;
Assert(inFlags & qtssWriteFlagsIsRTCP);
if (!(inFlags & qtssWriteFlagsIsRTCP))
return -1;
this->TrackRTCPBaseTime(theStreamPtr,inPacketStrPtr,currentTimePtr,inFlags, packetLatenessInMSec,timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
this->RewriteRTCP(theStreamPtr,inPacketStrPtr,currentTimePtr,inFlags, packetLatenessInMSec,timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
return writeErr;
}
QTSS_Error RTPSessionOutput::TrackRTPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
{
QTSS_Error writeErr = QTSS_NoErr;
Assert(inFlags & qtssWriteFlagsIsRTP);
if (!(inFlags & qtssWriteFlagsIsRTP))
return QTSS_NoErr;
ReflectorPacket packetContainer;
packetContainer.SetPacketData(inPacketStrPtr->Ptr, inPacketStrPtr->Len);
packetContainer.fIsRTCP = false;
SInt64 *theTimePtr = NULL;
UInt32 theLen = 0;
if (QTSS_NoErr != QTSS_GetValuePtr(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, (void**)&theTimePtr, &theLen))
{
UInt32 theSSRC = packetContainer.GetSSRC(packetContainer.fIsRTCP);
(void) QTSS_SetValue(*theStreamPtr, sStreamSSRCAttr, 0, &theSSRC, sizeof(theSSRC));
UInt32 rtpTime = packetContainer.GetPacketRTPTime();
writeErr = QTSS_SetValue(*theStreamPtr, sFirstRTPTimeStampAttr, 0, &rtpTime, sizeof(rtpTime));
Assert(writeErr == QTSS_NoErr);
writeErr = QTSS_SetValue(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, arrivalTimeMSecPtr, sizeof(SInt64));
Assert(writeErr == QTSS_NoErr);
writeErr = QTSS_SetValue(*theStreamPtr, sFirstRTPCurrentTimeAttr, 0, currentTimePtr, sizeof(SInt64));
Assert(writeErr == QTSS_NoErr);
UInt32 initValue = 0;
writeErr = QTSS_SetValue(*theStreamPtr, sStreamByteCountAttr, 0, &initValue, sizeof(UInt32));
Assert(writeErr == QTSS_NoErr);
//printf("first rtp on stream stream=%"_U32BITARG_" ssrc=%"_U32BITARG_" rtpTime=%"_U32BITARG_" arrivalTimeMSecPtr=%qd currentTime=%qd\n",(UInt32) theStreamPtr, theSSRC, rtpTime, *arrivalTimeMSecPtr, *currentTimePtr);
}
else
{
UInt32* packetCountPtr = NULL;
UInt32* byteCountPtr = NULL;
UInt32 theLen = 0;
writeErr = QTSS_GetValuePtr(*theStreamPtr, sStreamByteCountAttr, 0, (void**) &byteCountPtr,&theLen);
if (writeErr == QTSS_NoErr && theLen > 0)
*byteCountPtr += inPacketStrPtr->Len - 12;// 12 header bytes
UInt32* theSSRCPtr = 0;
(void) QTSS_GetValuePtr(*theStreamPtr, sStreamSSRCAttr, 0, (void**)&theSSRCPtr, &theLen);
if (*theSSRCPtr != packetContainer.GetSSRC(packetContainer.fIsRTCP))
{
(void) QTSS_RemoveValue(*theStreamPtr,sFirstRTPArrivalTimeAttr,0);
(void) QTSS_RemoveValue(*theStreamPtr,sFirstRTPTimeStampAttr,0);
(void) QTSS_RemoveValue(*theStreamPtr,sFirstRTPCurrentTimeAttr,0);
(void) QTSS_RemoveValue(*theStreamPtr,sStreamPacketCountAttr,0);
(void) QTSS_RemoveValue(*theStreamPtr,sStreamByteCountAttr,0);
fMustSynch = true;
//printf("found different ssrc =%"_U32BITARG_" packetssrc=%"_U32BITARG_"\n",*theSSRCPtr, packetContainer.GetSSRC(packetContainer.fIsRTCP));
}
}
return writeErr;
}
QTSS_Error RTPSessionOutput::TrackPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
{
if (this->IsUDP())
return QTSS_NoErr;
if (inFlags & qtssWriteFlagsIsRTCP)
(void) this->TrackRTCPPackets(theStreamPtr, inPacketStrPtr, currentTimePtr,inFlags, packetLatenessInMSec, timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
else if (inFlags & qtssWriteFlagsIsRTP)
(void) this->TrackRTPPackets(theStreamPtr, inPacketStrPtr, currentTimePtr,inFlags, packetLatenessInMSec, timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
return QTSS_NoErr;
}
QTSS_Error RTPSessionOutput::WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr, Bool16 firstPacket)
{
QTSS_RTPSessionState* theState = NULL;
UInt32 theLen = 0;
QTSS_Error writeErr = QTSS_NoErr;
SInt64 currentTime = OS::Milliseconds();
if (inPacket == NULL || inPacket->Len == 0)
return QTSS_NoErr;
(void)QTSS_GetValuePtr(fClientSession, qtssCliSesState, 0, (void**)&theState, &theLen);
if (theLen == 0 || theState == NULL || *theState != qtssPlayingState)
{ //qtss_printf("QTSS_WouldBlock *theState=%d qtssPlayingState=%d\n", *theState , qtssPlayingState);
return QTSS_WouldBlock;
}
//make sure all RTP streams with this ID see this packet
QTSS_RTPStreamObject *theStreamPtr = NULL;
for (UInt32 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&theStreamPtr, &theLen) == QTSS_NoErr; z++)
{
if (this->PacketMatchesStream(inStreamCookie, theStreamPtr))
{
if ( (inFlags & qtssWriteFlagsIsRTP) && this->FilterPacket(theStreamPtr, inPacket) )
return QTSS_NoErr; // keep looking at packets
if (this->PacketAlreadySent(theStreamPtr,inFlags, packetIDPtr))
return QTSS_NoErr; // keep looking at packets
if (!this->PacketReadyToSend(theStreamPtr,&currentTime, inFlags, packetIDPtr, timeToSendThisPacketAgain))
{ //qtss_printf("QTSS_WouldBlock\n");
return QTSS_WouldBlock; // stop not ready to send packets now
}
// TrackPackets below is for re-writing the rtcps we don't use it right now-- shouldn't need to
// (void) this->TrackPackets(theStreamPtr, inPacket, &currentTime,inFlags, &packetLatenessInMSec, timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
QTSS_PacketStruct thePacket;
thePacket.packetData = inPacket->Ptr;
SInt64 delayMSecs = fBufferDelayMSecs - (currentTime - *arrivalTimeMSecPtr);
thePacket.packetTransmitTime = (currentTime - packetLatenessInMSec);
if (fBufferDelayMSecs > 0 )
thePacket.packetTransmitTime += delayMSecs; // add buffer time where oldest buffered packet as now == 0 and newest is entire buffer time in the future.
writeErr = QTSS_Write(*theStreamPtr, &thePacket, inPacket->Len, NULL, inFlags | qtssWriteFlagsWriteBurstBegin);
if (writeErr == QTSS_WouldBlock)
{
//qtss_printf("QTSS_Write == QTSS_WouldBlock\n");
//
// We are flow controlled. See if we know when flow control will be lifted and report that
*timeToSendThisPacketAgain = thePacket.suggestedWakeupTime;
if (firstPacket)
{ fBufferDelayMSecs = (currentTime - *arrivalTimeMSecPtr);
//qtss_printf("firstPacket fBufferDelayMSecs =%lu \n", fBufferDelayMSecs);
}
}
else
{
fLastIntervalMilliSec = currentTime - fLastPacketTransmitTime;
if (fLastIntervalMilliSec > 100) //reset interval maybe first packet or it has been blocked for awhile
fLastIntervalMilliSec = 5;
fLastPacketTransmitTime = currentTime;
if (inFlags & qtssWriteFlagsIsRTP)
{
(void) QTSS_SetValue (*theStreamPtr, sLastRTPPacketIDAttr, 0, packetIDPtr, sizeof(UInt64));
}
else if (inFlags & qtssWriteFlagsIsRTCP)
{
(void) QTSS_SetValue (*theStreamPtr, sLastRTCPPacketIDAttr, 0, packetIDPtr, sizeof(UInt64));
(void) QTSS_SetValue (*theStreamPtr, sLastRTCPTransmitAttr, 0, &currentTime, sizeof(UInt64));
}
{ // increment packet counts
UInt32* packetCountPtr = NULL;
UInt32 theLen = 0;
(void) QTSS_GetValuePtr(*theStreamPtr, sStreamPacketCountAttr, 0,(void**) &packetCountPtr,&theLen);
if (theLen > 0)
{ *packetCountPtr += 1;
//printf("SET sStreamPacketCountAttr =%lu\n", *packetCountPtr);
}
}
}
}
if ( writeErr != QTSS_NoErr )
break;
}
return writeErr;
}
UInt16 RTPSessionOutput::GetPacketSeqNumber(StrPtrLen* inPacket)
{
if (inPacket->Len < 4)
return 0;
//The RTP seq number is the second short of the packet
UInt16* seqNumPtr = (UInt16*)inPacket->Ptr;
return ntohs(seqNumPtr[1]);
}
void RTPSessionOutput::SetPacketSeqNumber(StrPtrLen* inPacket, UInt16 inSeqNumber)
{
if (inPacket->Len < 4)
return;
//The RTP seq number is the second short of the packet
UInt16* seqNumPtr = (UInt16*)inPacket->Ptr;
seqNumPtr[1] = htons(inSeqNumber);
}
// this routine is not used
Bool16 RTPSessionOutput::PacketShouldBeThinned(QTSS_RTPStreamObject inStream, StrPtrLen* inPacket)
{
return false; // function is disabled.
static UInt16 sZero = 0;
//This function determines whether the packet should be dropped.
//It also adjusts the sequence number if necessary
if (inPacket->Len < 4)
return false;
UInt16 curSeqNum = this->GetPacketSeqNumber(inPacket);
UInt32* curQualityLevel = NULL;
UInt16* nextSeqNum = NULL;
UInt16* theSeqNumOffset = NULL;
SInt64* lastChangeTime = NULL;
UInt32 theLen = 0;
(void)QTSS_GetValuePtr(inStream, qtssRTPStrQualityLevel, 0, (void**)&curQualityLevel, &theLen);
if ((curQualityLevel == NULL) || (theLen != sizeof(UInt32)))
return false;
(void)QTSS_GetValuePtr(inStream, sNextSeqNumAttr, 0, (void**)&nextSeqNum, &theLen);
if ((nextSeqNum == NULL) || (theLen != sizeof(UInt16)))
{
nextSeqNum = &sZero;
(void)QTSS_SetValue(inStream, sNextSeqNumAttr, 0, nextSeqNum, sizeof(UInt16));
}
(void)QTSS_GetValuePtr(inStream, sSeqNumOffsetAttr, 0, (void**)&theSeqNumOffset, &theLen);
if ((theSeqNumOffset == NULL) || (theLen != sizeof(UInt16)))
{
theSeqNumOffset = &sZero;
(void)QTSS_SetValue(inStream, sSeqNumOffsetAttr, 0, theSeqNumOffset, sizeof(UInt16));
}
UInt16 newSeqNumOffset = *theSeqNumOffset;
(void)QTSS_GetValuePtr(inStream, sLastQualityChangeAttr, 0, (void**)&lastChangeTime, &theLen);
if ((lastChangeTime == NULL) || (theLen != sizeof(SInt64)))
{ static SInt64 startTime = 0;
lastChangeTime = &startTime;
(void)QTSS_SetValue(inStream, sLastQualityChangeAttr, 0, lastChangeTime, sizeof(SInt64));
}
SInt64 timeNow = OS::Milliseconds();
if (*lastChangeTime == 0 || *curQualityLevel == 0)
*lastChangeTime =timeNow;
if (*curQualityLevel > 0 && ((*lastChangeTime + 30000) < timeNow) ) // 30 seconds between reductions
{ *curQualityLevel -= 1; // reduce quality value. If we quality doesn't change then we may have hit some steady state which we can't get out of without thinning or increasing the quality
*lastChangeTime =timeNow;
//qtss_printf("RTPSessionOutput set quality to %"_U32BITARG_"\n",*curQualityLevel);
}
//Check to see if we need to drop to audio only
if ((*curQualityLevel >= ReflectorSession::kAudioOnlyQuality) &&
(*nextSeqNum == 0))
{
#if REFLECTOR_THINNING_DEBUGGING || RTP_SESSION_DEBUGGING
qtss_printf(" *** Reflector Dropping to audio only *** \n");
#endif
//All we need to do in this case is mark the sequence number of the first dropped packet
(void)QTSS_SetValue(inStream, sNextSeqNumAttr, 0, &curSeqNum, sizeof(UInt16));
*lastChangeTime =timeNow;
}
//Check to see if we can reinstate video
if ((*curQualityLevel == ReflectorSession::kNormalQuality) && (*nextSeqNum != 0))
{
//Compute the offset amount for each subsequent sequence number. This offset will
//alter the sequence numbers so that they increment normally (providing the illusion to the
//client that there are no missing packets)
newSeqNumOffset = (*theSeqNumOffset) + (curSeqNum - (*nextSeqNum));
(void)QTSS_SetValue(inStream, sSeqNumOffsetAttr, 0, &newSeqNumOffset, sizeof(UInt16));
(void)QTSS_SetValue(inStream, sNextSeqNumAttr, 0, &sZero, sizeof(UInt16));
}
//tell the caller whether to drop this packet or not.
if (*curQualityLevel >= ReflectorSession::kAudioOnlyQuality)
return true;
else
{
//Adjust the sequence number of the current packet based on the offset, if any
curSeqNum -= newSeqNumOffset;
this->SetPacketSeqNumber(inPacket, curSeqNum);
return false;
}
}
void RTPSessionOutput::TearDown()
{
QTSS_CliSesTeardownReason reason = qtssCliSesTearDownBroadcastEnded;
(void)QTSS_SetValue(fClientSession, qtssCliTeardownReason, 0, &reason, sizeof(reason));
(void)QTSS_Teardown(fClientSession);
}

View file

@ -0,0 +1,111 @@
/*
*
* @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: RTSPReflectorOutput.h
Contains: Derived from ReflectorOutput, this implements the WritePacket
method in terms of the QTSS API (that is, it writes to a client
using the QTSS_RTPSessionObject
*/
#ifndef __RTSP_REFLECTOR_OUTPUT_H__
#define __RTSP_REFLECTOR_OUTPUT_H__
#include "ReflectorOutput.h"
#include "ReflectorSession.h"
#include "QTSS.h"
class RTPSessionOutput : public ReflectorOutput
{
public:
// Adds some dictionary attributes
static void Register();
RTPSessionOutput(QTSS_ClientSessionObject inRTPSession, ReflectorSession* inReflectorSession,
QTSS_Object serverPrefs, QTSS_AttributeID inCookieAddrID);
virtual ~RTPSessionOutput() {}
ReflectorSession* GetReflectorSession() { return fReflectorSession; }
void InitializeStreams();
// This writes the packet out to the proper QTSS_RTPStreamObject.
// If this function returns QTSS_WouldBlock, timeToSendThisPacketAgain will
// be set to # of msec in which the packet can be sent, or -1 if unknown
virtual QTSS_Error WritePacket(StrPtrLen* inPacketData, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSec,Bool16 firstPacket );
virtual void TearDown();
SInt64 GetReflectorSessionInitTime() { return fReflectorSession->GetInitTimeMS(); }
virtual Bool16 IsUDP();
virtual Bool16 IsPlaying();
void SetBufferDelay (UInt32 delay) { fBufferDelayMSecs = delay; }
private:
QTSS_ClientSessionObject fClientSession;
ReflectorSession* fReflectorSession;
QTSS_AttributeID fCookieAttrID;
UInt32 fBufferDelayMSecs;
SInt64 fBaseArrivalTime;
Bool16 fIsUDP;
Bool16 fTransportInitialized;
Bool16 fMustSynch;
Bool16 fPreFilter;
UInt16 GetPacketSeqNumber(StrPtrLen* inPacket);
void SetPacketSeqNumber(StrPtrLen* inPacket, UInt16 inSeqNumber);
Bool16 PacketShouldBeThinned(QTSS_RTPStreamObject inStream, StrPtrLen* inPacket);
Bool16 FilterPacket(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacket);
UInt32 GetPacketRTPTime(StrPtrLen* packetStrPtr);
inline Bool16 PacketMatchesStream(void* inStreamCookie, QTSS_RTPStreamObject *theStreamPtr);
Bool16 PacketReadyToSend(QTSS_RTPStreamObject *theStreamPtr,SInt64 *currentTimePtr, UInt32 inFlags, UInt64* packetIDPtr, SInt64* timeToSendThisPacketAgainPtr);
Bool16 PacketAlreadySent(QTSS_RTPStreamObject *theStreamPtr, UInt32 inFlags, UInt64* packetIDPtr);
QTSS_Error TrackRTCPBaseTime(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
QTSS_Error RewriteRTCP(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
QTSS_Error TrackRTPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
QTSS_Error TrackRTCPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
QTSS_Error TrackPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
};
Bool16 RTPSessionOutput::PacketMatchesStream(void* inStreamCookie, QTSS_RTPStreamObject *theStreamPtr)
{
void** theStreamCookie = NULL;
UInt32 theLen = 0;
(void) QTSS_GetValuePtr(*theStreamPtr, fCookieAttrID, 0, (void**)&theStreamCookie, &theLen);
if ((theStreamCookie != NULL) && (*theStreamCookie == inStreamCookie))
return true;
return false;
}
#endif //__RTSP_REFLECTOR_OUTPUT_H__

View file

@ -0,0 +1,750 @@
/*
*
* @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: RTSPSourceInfo.cpp
Contains:
*/
#include "RTSPSourceInfo.h"
#include "StringParser.h"
#include "SDPSourceInfo.h"
#include "OSMemory.h"
#include "StringFormatter.h"
#include "SocketUtils.h"
#include "RelayOutput.h"
#include "StringTranslator.h"
#include "SDPUtils.h"
#include "OSArrayObjectDeleter.h"
StrPtrLen RTSPSourceInfo::sKeyString("rtsp_source");
StrPtrLen RTSPSourceInfo::sAnnouncedKeyString("announced_rtsp_source");
// StrPtrLen's for various keywords on the relay_source & relay_destination lines
static StrPtrLen sOutAddr("out_addr");
static StrPtrLen sDestAddr("dest_addr");
static StrPtrLen sDestPorts("dest_ports");
static StrPtrLen sTtl("ttl");
char* RTSPOutputInfo::CopyString(const char* srcStr)
{
char* dstStr = NULL;
if(srcStr != NULL)
{
UInt32 len = ::strlen(srcStr);
dstStr = NEW char[len + 1];
::memcpy(dstStr, srcStr, len);
dstStr[len] = '\0';
}
return dstStr;
}
void RTSPOutputInfo::Copy(const RTSPOutputInfo& copy)
{
fIsAnnounced = copy.fIsAnnounced;
fAnnouncePort = copy.fAnnouncePort;
if(copy.fDestURl != NULL)
fDestURl = RTSPOutputInfo::CopyString(copy.fDestURl);
if(copy.fUserName != NULL)
fUserName = RTSPOutputInfo::CopyString(copy.fUserName);
if(copy.fPassword != NULL)
fPassword = RTSPOutputInfo::CopyString(copy.fPassword);
}
RTSPSourceInfo::RTSPSourceInfo(const RTSPSourceInfo& copy)
:RCFSourceInfo(copy),
fHostAddr(copy.fHostAddr), fHostPort(copy.fHostPort), fLocalAddr(copy.fLocalAddr),
fNumSetupsComplete(copy.fNumSetupsComplete), fDescribeComplete(copy.fDescribeComplete),
fAnnounce(copy.fAnnounce), fAnnounceIP(copy.fAnnounceIP), fAnnounceActualIP(copy.fAnnounceIP),fQueueElem()
{
fQueueElem.SetEnclosingObject(this);
fSourceURL = RTSPOutputInfo::CopyString(copy.fSourceURL);
fUserName = RTSPOutputInfo::CopyString(copy.fUserName);
fPassword = RTSPOutputInfo::CopyString(copy.fPassword);
fAnnounceURL = RTSPOutputInfo::CopyString(copy.fAnnounceURL);
fRTSPInfoArray = NEW RTSPOutputInfo[fNumOutputs];
for (UInt32 index=0; index < fNumOutputs; index++)
fRTSPInfoArray[index].Copy(copy.fRTSPInfoArray[index]);
// These aren't set anyway and shouldn't be copied around
fClientSocket = NULL;
fClient = NULL;
fRelaySessionCreatorTask = NULL;
fSession = NULL;
fSessionQueue = NULL;
if ((copy.fLocalSDP).Ptr != NULL)
fLocalSDP.Set((copy.fLocalSDP).GetAsCString(), (copy.fLocalSDP).Len);
}
RTSPSourceInfo::~RTSPSourceInfo()
{
OSMutexLocker locker(RelayOutput::GetQueueMutex());
if (fRelaySessionCreatorTask != NULL)
fRelaySessionCreatorTask->fInfo = NULL;
if (fDescribeComplete)
{
Assert(fClientSocket != NULL);
Assert(fClient != NULL);
// the task will delete these objects when it is done with the teardown
TeardownTask* task = new TeardownTask(fClientSocket, fClient);
task->Signal(Task::kStartEvent);
}
else
{
if (fClientSocket != NULL) delete fClientSocket;
if (fClient != NULL) delete fClient;
}
if (fQueueElem.IsMemberOfAnyQueue())
fQueueElem.Remove();
if (fLocalSDP.Ptr != NULL) delete fLocalSDP.Ptr;
fLocalSDP.Len = 0;
if (fSourceURL != NULL) delete fSourceURL;
if (fAnnounceURL != NULL) delete fAnnounceURL;
if (fRTSPInfoArray != NULL) delete [] fRTSPInfoArray;
}
void RTSPSourceInfo::InitClient(UInt32 inSocketType)
{
fClientSocket = NEW TCPClientSocket(inSocketType);
fClient = NEW RTSPClient(fClientSocket, false, RelaySession::sRelayUserAgent);
}
void RTSPSourceInfo::SetClientInfo(UInt32 inAddr, UInt16 inPort, char* inURL, UInt32 inLocalAddr)
{
if (fClientSocket != NULL)
fClientSocket->Set(inAddr, inPort);
StrPtrLen inURLPtrLen(inURL);
if (fClient != NULL)
fClient->Set(inURLPtrLen);
if (inLocalAddr != 0)
fClientSocket->GetSocket()->Bind(inLocalAddr, 0);
}
QTSS_Error RTSPSourceInfo::ParsePrefs(XMLTag* relayTag, Bool16 inAnnounce)
{
XMLTag* prefTag;
UInt32 localAddr = 0;
UInt32 theHostAddr = 0;
UInt16 theHostPort = 554;
char* userName = NULL;
char* password = NULL;
StrPtrLen theURL;
fAnnounce = inAnnounce;
XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
if (sourceTag == NULL)
return QTSS_ValueNotFound;
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "in_addr");
if (prefTag != NULL)
{
char* inAddrStr = prefTag->GetValue();
if (inAddrStr != NULL)
localAddr = SocketUtils::ConvertStringToAddr(inAddrStr);
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "source_addr");
if (prefTag != NULL)
{
char* destAddrStr = prefTag->GetValue();
if (destAddrStr != NULL)
theHostAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "rtsp_port");
if (prefTag != NULL)
{
char* portStr = prefTag->GetValue();
if (portStr != NULL)
theHostPort = atoi(portStr);
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "name");
if (prefTag != NULL)
{
userName = prefTag->GetValue();
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "password");
if (prefTag != NULL)
{
password = prefTag->GetValue();
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "url");
char urlBuff[1024];
if (prefTag != NULL)
{
char* urlString = prefTag->GetValue();
StringTranslator::EncodeURL(urlString, strlen(urlString) + 1, urlBuff, sizeof(urlBuff));
theURL.Set(urlBuff);
}
if (fAnnounce)
{
fAnnounceURL = theURL.GetAsCString();
fAnnounceIP = theHostAddr;
}
else
{
fHostAddr = theHostAddr;
fHostPort = theHostPort;
fSourceURL = theURL.GetAsCString();
fUserName = RTSPOutputInfo::CopyString(userName);
fPassword = RTSPOutputInfo::CopyString(password);
}
return QTSS_NoErr;
}
QTSS_Error RTSPSourceInfo::Describe()
{
QTSS_Error theErr = QTSS_NoErr;
if (!fDescribeComplete)
{
// Work on the describe
theErr = fClient->SendDescribe();
if (theErr != QTSS_NoErr)
return theErr;
if (fClient->GetStatus() != 200)
return QTSS_RequestFailed;
// If the above function returns QTSS_NoErr, we've gotten the describe response,
// so process it.
SDPSourceInfo theSourceInfo(fClient->GetContentBody(), fClient->GetContentLength());
// Copy the Source Info into our local SourceInfo.
fNumStreams = theSourceInfo.GetNumStreams();
fStreamArray = NEW StreamInfo[fNumStreams];
for (UInt32 x = 0; x < fNumStreams; x++)
{
// Copy fPayloadType, fPayloadName, fTrackID, fBufferDelay
fStreamArray[x].Copy(*theSourceInfo.GetStreamInfo(x));
// Copy all stream info data. Also set fSrcIPAddr to be the host addr
fStreamArray[x].fSrcIPAddr = fClientSocket->GetHostAddr();
fStreamArray[x].fDestIPAddr = fClientSocket->GetLocalAddr();
fStreamArray[x].fPort = 0;
fStreamArray[x].fTimeToLive = 0;
}
}
// Ok, describe is complete, copy out the SDP information.
fLocalSDP.Ptr = NEW char[fClient->GetContentLength() + 1];
// Look for an "a=range" line in the SDP. If there is one, remove it.
static StrPtrLen sRangeStr("a=range:");
StrPtrLen theSDPPtr(fClient->GetContentBody(), fClient->GetContentLength());
StringParser theSDPParser(&theSDPPtr);
do
{
// Loop until we reach the end of the SDP or hit a a=range line.
StrPtrLen theSDPLine(theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
if ((theSDPLine.Len > sRangeStr.Len) && (theSDPLine.NumEqualIgnoreCase(sRangeStr.Ptr, sRangeStr.Len)))
break;
} while (theSDPParser.GetThruEOL(NULL));
// Copy what we have so far
::memcpy(fLocalSDP.Ptr, fClient->GetContentBody(), theSDPParser.GetDataParsedLen());
fLocalSDP.Len = theSDPParser.GetDataParsedLen();
// Skip over the range (if it exists)
(void)theSDPParser.GetThruEOL(NULL);
// Copy the rest of the SDP
::memcpy(fLocalSDP.Ptr + fLocalSDP.Len, theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
fLocalSDP.Len += theSDPParser.GetDataRemaining();
#define _WRITE_SDP_ 0
#if _WRITE_SDP_
FILE* outputFile = ::fopen("rtspclient.sdp", "w");
if (outputFile != NULL)
{
fLocalSDP.Ptr[fLocalSDP.Len] = '\0';
qtss_fprintf(outputFile, "%s", fLocalSDP.Ptr);
::fclose(outputFile);
qtss_printf("Wrote sdp to rtspclient.sdp\n");
}
else
qtss_printf("Failed to write sdp\n");
#endif
fDescribeComplete = true;
return QTSS_NoErr;
}
QTSS_Error RTSPSourceInfo::SetupAndPlay()
{
QTSS_Error theErr = QTSS_NoErr;
// Do all the setups. This is async, so when a setup doesn't complete
// immediately, return an error, and we'll pick up where we left off.
while (fNumSetupsComplete < fNumStreams)
{
theErr = fClient->SendUDPSetup(fStreamArray[fNumSetupsComplete].fTrackID, fStreamArray[fNumSetupsComplete].fPort);
if (theErr != QTSS_NoErr)
return theErr;
else if (fClient->GetStatus() != 200)
return QTSS_RequestFailed;
else
fNumSetupsComplete++;
}
// We've done all the setups. Now send a play.
theErr = fClient->SendPlay(0);
if (theErr != QTSS_NoErr)
return theErr;
if (fClient->GetStatus() != 200)
return QTSS_RequestFailed;
return QTSS_NoErr;
}
QTSS_Error RTSPSourceInfo::Teardown()
{
return (QTSS_Error)fClient->SendTeardown();
}
char* RTSPSourceInfo::GetLocalSDP(UInt32* newSDPLen)
{
*newSDPLen = fLocalSDP.Len;
return fLocalSDP.GetAsCString();
}
char* RTSPSourceInfo::GetAnnounceSDP(UInt32 ipAddr, UInt32* newSDPLen)
{
char *announceSDP = NEW char[fLocalSDP.Len * 2];
StringFormatter announceSDPFormatter(announceSDP, fLocalSDP.Len * 2);
StrPtrLen sdpLine;
StringParser sdpParser(&fLocalSDP);
bool added = 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; // remove any existing c lines
case 'm':
{
if (!added)
{
added = true;
// add a c line before the first m line
char ipStr[50];
char buff[50];
StrPtrLen temp(buff);
struct in_addr theIPAddr;
theIPAddr.s_addr = htonl(ipAddr);
SocketUtils::ConvertAddrToString(theIPAddr, &temp);
qtss_sprintf(ipStr, "c=IN IP4 %s", buff);
StrPtrLen tempLine(ipStr);
announceSDPFormatter.Put(tempLine);
announceSDPFormatter.PutEOL();
}
announceSDPFormatter.Put(sdpLine);
announceSDPFormatter.PutEOL();
break;//ignore connection information
}
default:
{
announceSDPFormatter.Put(sdpLine);
announceSDPFormatter.PutEOL();
}
}
}
*newSDPLen = (UInt32)announceSDPFormatter.GetCurrentOffset();
announceSDP[*newSDPLen] = 0;
StrPtrLen theSDPStr(announceSDP);
SDPContainer rawSDPContainer;
if (!rawSDPContainer.SetSDPBuffer( &theSDPStr ))
{ return NULL; // it is screwed up do nothing the sdp will be deleted automatically
}
SDPLineSorter sortedSDP(&rawSDPContainer);
return sortedSDP.GetSortedSDPCopy(); // return a new copy of the sorted SDP
}
void RTSPSourceInfo::ParseAnnouncedDestination(XMLTag* destTag, UInt32 index)
{
XMLTag* prefTag;
fRTSPInfoArray[index].fIsAnnounced = true;
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "out_addr");
if (prefTag != NULL)
{
char* outAddrStr = prefTag->GetValue();
if (outAddrStr != NULL)
fOutputArray[index].fLocalAddr = SocketUtils::ConvertStringToAddr(outAddrStr);
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "dest_addr");
if (prefTag != NULL)
{
char* destAddrStr = prefTag->GetValue();
if (destAddrStr != NULL)
fOutputArray[index].fDestAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "rtsp_port");
if (prefTag != NULL)
{
char* portStr = prefTag->GetValue();
if (portStr != NULL)
fRTSPInfoArray[index].fAnnouncePort = atoi(portStr);
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "name");
if (prefTag != NULL)
{
StrPtrLen userName(prefTag->GetValue());
fRTSPInfoArray[index].fUserName = userName.GetAsCString();
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "url");
if (prefTag != NULL)
{
char urlBuff[1024];
char* urlString = prefTag->GetValue();
StringTranslator::EncodeURL(urlString, strlen(urlString) + 1, urlBuff, sizeof(urlBuff));
StrPtrLen destURL(urlBuff);
fRTSPInfoArray[index].fDestURl = destURL.GetAsCString();
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "password");
if (prefTag != NULL)
{
StrPtrLen password(prefTag->GetValue());
fRTSPInfoArray[index].fPassword = password.GetAsCString();
}
}
void RTSPSourceInfo::AllocateOutputArray(UInt32 numOutputs)
{
// Allocate the proper number of relay outputs
RCFSourceInfo::AllocateOutputArray(numOutputs);
fRTSPInfoArray = new RTSPOutputInfo[numOutputs];
}
Bool16 RTSPSourceInfo::Equal(SourceInfo* inInfo)
{
if (!inInfo->IsRTSPSourceInfo())
return false;
// RTSPSourceInfo* info = dynamic_cast<RTSPSourceInfo *>(inInfo);
RTSPSourceInfo* info = (RTSPSourceInfo *)(inInfo);
// if (info == NULL)
// return false;
if (!fAnnounce)
{
StrPtrLen source(fSourceURL);
if( source.Equal(info->GetSourceURL()) && (fHostAddr == info->GetHostAddr())
&& (fHostPort == info->GetHostPort()) )
return true;
}
else
{
StrPtrLen announceURL(fAnnounceURL);
if ((fAnnounceIP == info->GetAnnounceIP()) && announceURL.Equal(info->GetAnnounceURL()))
return true;
}
return false;
}
void RTSPSourceInfo::SetSourceParameters(UInt32 inHostAddr, UInt16 inHostPort, StrPtrLen& inURL)
{
fHostAddr = inHostAddr;
fHostPort = inHostPort;
fSourceURL = inURL.GetAsCString();
}
void RTSPSourceInfo::StartSessionCreatorTask(OSQueue* inSessionQueue, OSQueue* inSourceQueue)
{
InitClient(Socket::kNonBlockingSocketType);
SetClientInfo(fHostAddr, fHostPort, fSourceURL, fLocalAddr);
if (fUserName != NULL)
fClient->SetName(fUserName);
if (fPassword != NULL)
fClient->SetPassword(fPassword);
fSessionQueue = inSessionQueue;
if(fAnnounce)
inSourceQueue->EnQueue(&fQueueElem);
fSessionCreationState = kSendingDescribe;
fRelaySessionCreatorTask = NEW RelaySessionCreator(this);
fRelaySessionCreatorTask->Signal(Task::kStartEvent);
}
SInt64 RTSPSourceInfo::RelaySessionCreator::Run()
{
OSMutexLocker locker(RelayOutput::GetQueueMutex());
SInt64 result = -1;
if (fInfo != NULL)
result = fInfo->RunCreateSession();
return result;
}
SInt64 RTSPSourceInfo::RunCreateSession()
{
OS_Error osErr = OS_NoErr;
SInt64 result = 500;
if (fSessionCreationState == kSendingDescribe)
{
if (!fDescribeComplete)
{
osErr = fClient->SendDescribe();
if (osErr == OS_NoErr)
{
if (fClient->GetStatus() == 200)
{
// we've gotten the describe response, so process it.
SDPSourceInfo theSourceInfo(fClient->GetContentBody(), fClient->GetContentLength());
// Copy the Source Info into our local SourceInfo.
fNumStreams = theSourceInfo.GetNumStreams();
fStreamArray = NEW StreamInfo[fNumStreams];
for (UInt32 x = 0; x < fNumStreams; x++)
{
// Copy fPayloadType, fPayloadName, fTrackID, fBufferDelay
fStreamArray[x].Copy(*theSourceInfo.GetStreamInfo(x));
// Copy all stream info data. Also set fSrcIPAddr to be the host addr
fStreamArray[x].fSrcIPAddr = fClientSocket->GetHostAddr();
fStreamArray[x].fDestIPAddr = fClientSocket->GetLocalAddr();
fStreamArray[x].fPort = 0;
fStreamArray[x].fTimeToLive = 0;
}
}
else
osErr = ENOTCONN;
}
}
//describe is complete
if(osErr == OS_NoErr)
{
//copy out the SDP information
fLocalSDP.Ptr = NEW char[fClient->GetContentLength() + 1];
// Look for an "a=range" line in the SDP. If there is one, remove it.
static StrPtrLen sRangeStr("a=range:");
StrPtrLen theSDPPtr(fClient->GetContentBody(), fClient->GetContentLength());
StringParser theSDPParser(&theSDPPtr);
do
{
// Loop until we reach the end of the SDP or hit a a=range line.
StrPtrLen theSDPLine(theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
if ((theSDPLine.Len > sRangeStr.Len) && (theSDPLine.NumEqualIgnoreCase(sRangeStr.Ptr, sRangeStr.Len)))
break;
} while (theSDPParser.GetThruEOL(NULL));
// Copy what we have so far
::memcpy(fLocalSDP.Ptr, fClient->GetContentBody(), theSDPParser.GetDataParsedLen());
fLocalSDP.Len = theSDPParser.GetDataParsedLen();
// Skip over the range (if it exists)
(void)theSDPParser.GetThruEOL(NULL);
// Copy the rest of the SDP
::memcpy(fLocalSDP.Ptr + fLocalSDP.Len, theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
fLocalSDP.Len += theSDPParser.GetDataRemaining();
#define _WRITE_SDP_ 0
#if _WRITE_SDP_
FILE* outputFile = ::fopen("rtspclient.sdp", "w");
if (outputFile != NULL)
{
fLocalSDP.Ptr[fLocalSDP.Len] = '\0';
qtss_fprintf(outputFile, "%s", fLocalSDP.Ptr);
::fclose(outputFile);
qtss_printf("Wrote sdp to rtspclient.sdp\n");
}
else
qtss_printf("Failed to write sdp\n");
#endif
fDescribeComplete = true;
fSession = NEW RelaySession(NULL, this);
if (fSession->SetupRelaySession(this) == OS_NoErr)
{
fSessionCreationState = kSendingSetup;
}
else
{
osErr = ENOTCONN;
}
}
}
while ((fSessionCreationState == kSendingSetup) && (osErr == OS_NoErr))
{
osErr = fClient->SendUDPSetup(fStreamArray[fNumSetupsComplete].fTrackID, fStreamArray[fNumSetupsComplete].fPort);
if(osErr == OS_NoErr)
{
if(fClient->GetStatus() == 200)
{
fNumSetupsComplete++;
if (fNumSetupsComplete == fNumStreams)
fSessionCreationState = kSendingPlay;
}
else
osErr = ENOTCONN;
}
}
if (fSessionCreationState == kSendingPlay)
{
osErr = fClient->SendPlay(0);
if (osErr == OS_NoErr)
{
if (fClient->GetStatus() == 200)
fSessionCreationState = kDone;
else
osErr = ENOTCONN;
}
}
if (fSessionCreationState == kDone)
{
// If session was correctly set up,
// add the outputs
if(fSession != NULL)
{
// Format SourceInfo HTML for the stats web page
fSession->FormatHTML(fClient->GetURL());
OSMutexLocker locker(RelayOutput::GetQueueMutex());
fSessionQueue->EnQueue(fSession->GetQueueElem());
for (UInt32 x = 0; x < fNumOutputs; x++)
{
SourceInfo::OutputInfo* theOutputInfo = GetOutputInfo(x);
if (theOutputInfo->fAlreadySetup)
continue; // shouldn't ever happen
RelayOutput* theOutput = NEW RelayOutput(this, x, fSession, true);
if (theOutput->IsValid())
fSession->AddOutput(theOutput, false);
else
delete theOutput;
}
}
fClientSocket->GetSocket()->SetTask(NULL); //detach the task from the socket
result = -1; // let the task die
fRelaySessionCreatorTask = NULL;
}
if ((osErr == EINPROGRESS) || (osErr == EAGAIN))
{
// Request an async event
fClientSocket->GetSocket()->SetTask(fRelaySessionCreatorTask);
fClientSocket->GetSocket()->RequestEvent(fClientSocket->GetEventMask() );
}
else if (osErr != OS_NoErr)
{
// We encountered some fatal error with the socket. Record this as a connection failure
// delete the session
// delete the session
if(fSession != NULL)
{
delete fSession;
fSession = NULL;
}
fClientSocket->GetSocket()->SetTask(NULL); //detach the task from the socket
result = -1; // let the task die
fRelaySessionCreatorTask = NULL;
}
return result;
}
RTSPSourceInfo::TeardownTask::TeardownTask(TCPClientSocket* clientSocket, RTSPClient* client)
{
this->SetTaskName("RTSPSourceInfo::TeardownTask");
fClientSocket = clientSocket;
fClient = client;
}
RTSPSourceInfo::TeardownTask::~TeardownTask()
{
delete fClientSocket;
delete fClient;
}
SInt64 RTSPSourceInfo::TeardownTask::Run()
{
OS_Error err = fClient->SendTeardown();
if ((err == EINPROGRESS) || (err == EAGAIN))
{
// Request an async event
fClientSocket->GetSocket()->SetTask(this);
fClientSocket->GetSocket()->RequestEvent(fClientSocket->GetEventMask() );
return 250;
}
fClientSocket->GetSocket()->SetTask(NULL); //detach the task from the socket
return -1; // we're out of here, this will cause the destructor to be called
}

View file

@ -0,0 +1,243 @@
/*
*
* @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: RTSPSourceInfo.h
Contains:
*/
#ifndef __RTSP_SOURCE_INFO_H__
#define __RTSP_SOURCE_INFO_H__
#include "QTSS.h"
#include "StrPtrLen.h"
#include "RCFSourceInfo.h"
#include "RTSPClient.h"
#include "XMLParser.h"
#include "ClientSocket.h"
#include "RelaySession.h"
class RelaySessionCreator;
class RTSPOutputInfo
{
public:
RTSPOutputInfo() : fIsAnnounced(false),
fAnnouncePort(554),
fDestURl(NULL),
fUserName(NULL),
fPassword(NULL) {}
~RTSPOutputInfo()
{
if (fDestURl != NULL) delete fDestURl;
if (fUserName != NULL) delete fUserName;
if (fPassword != NULL) delete fPassword;
}
static char* CopyString(const char* srcStr);
void Copy(const RTSPOutputInfo& copy); // copies dynamically allocated data too
Bool16 Equal(const RTSPOutputInfo* inInfo)
{ return ((inInfo != NULL) && (fIsAnnounced == inInfo->fIsAnnounced) && (fAnnouncePort == inInfo->fAnnouncePort)
&& (strcmp(fDestURl, inInfo->fDestURl) == 0)); }
Bool16 fIsAnnounced;
UInt16 fAnnouncePort;
char* fDestURl;
char* fUserName;
char* fPassword;
};
class RTSPSourceInfo : public RCFSourceInfo
{
public:
// Specify whether the client should be blocking or non-blocking
RTSPSourceInfo(Bool16 inAnnounce) : fSourceURL(NULL),
fHostAddr(0),
fHostPort(0),
fLocalAddr(0),
fUserName(NULL),
fPassword(NULL),
fRTSPInfoArray(NULL),
fClientSocket(NULL),
fClient(NULL),
fNumSetupsComplete(0),
fDescribeComplete(false),
fAnnounce(inAnnounce),
fAnnounceURL(NULL),
fAnnounceIP(0),
fAnnounceActualIP(0),
fRelaySessionCreatorTask(NULL),
fSession(NULL),
fSessionQueue(NULL),
fQueueElem() { fQueueElem.SetEnclosingObject(this); }
RTSPSourceInfo(const RTSPSourceInfo& copy); // Does copy dynamically allocated data
// Doesn't copy fClientSocket and fClient ptrs
virtual ~RTSPSourceInfo();
// Call this before calling ParsePrefs / Describe
void InitClient(UInt32 inSocketType);
void SetClientInfo(UInt32 inAddr, UInt16 inPort, char* inURL, UInt32 inLocalAddr = 0);
// Call this immediately after the constructor. This object will parse
// the config file and extract the necessary information to connect to an rtsp server.
// Specify the config file line index where the "rtsp_source" line resides
QTSS_Error ParsePrefs(XMLTag* relayTag, Bool16 inAnnounce);
// Connects, sends a DESCRIBE, and parses the incoming SDP data. After this
// function completes sucessfully, GetLocalSDP returns the data, and the
// SourceInfo & DestInfo arrays will be set up. Also sends SETUPs for all the
// tracks, and finishes by issuing a PLAY.
//
// These functions return QTSS_NoErr if the transaction has completed
// successfully. Otherwise, they return:
//
// EAGAIN: the transaction is still in progress, the call should be reissued
// QTSS_RequestFailed: the remote host responded with an error.
// Any other error means that the remote host was unavailable or refused the connection
QTSS_Error Describe();
QTSS_Error SetupAndPlay();
// This function works the same way as the above ones, and should be
// called before destroying the object to let the remote host know that
// we are going away.
QTSS_Error Teardown();
// 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);
virtual char* GetAnnounceSDP(UInt32 ipAddr, UInt32* newSDPLen);
virtual StrPtrLen* GetSourceID() { return fClient->GetURL(); }
// This object looks for this keyword in the FilePrefsSource, where it
// expects the IP address, port, and URL.
static StrPtrLen& GetRTSPSourceString() { return sKeyString; }
RTSPClient* GetRTSPClient() { return fClient; }
TCPClientSocket* GetClientSocket() { return fClientSocket; }
Bool16 IsDescribeComplete(){ return fDescribeComplete; }
RTSPOutputInfo* GetRTSPOutputInfo(UInt32 index) { return &fRTSPInfoArray[index]; }
char* GetSourceURL() { return fSourceURL; }
virtual Bool16 IsRTSPSourceInfo() { return true; }
virtual Bool16 Equal(SourceInfo* inInfo);
Bool16 IsAnnounce() { return fAnnounce; }
char* GetAnnounceURL() { return fAnnounceURL; }
UInt32 GetAnnounceIP() { return fAnnounceIP; }
UInt32 GetAnnounceActualIP() { return fAnnounceActualIP; }
void SetAnnounceActualIP(UInt32 inActualIP) { fAnnounceActualIP = inActualIP; }
UInt32 GetHostAddr() { return fHostAddr; }
UInt32 GetHostPort() { return fHostPort; }
char* GetUsername() { return fUserName; }
char* GetPassword() { return fPassword; }
RelaySession* GetRelaySession() { return fSession; }
void SetSourceParameters(UInt32 inHostAddr, UInt16 inHostPort, StrPtrLen& inURL);
void StartSessionCreatorTask(OSQueue* inSessionQueue, OSQueue* inSourceQueue);
SInt64 RunCreateSession();
protected:
virtual void ParseAnnouncedDestination(XMLTag* destTag, UInt32 index);
virtual void AllocateOutputArray(UInt32 numOutputs);
private:
class RelaySessionCreator : public Task
{
public:
RelaySessionCreator(RTSPSourceInfo* inInfo) : fInfo(inInfo) {this->SetTaskName("RTSPSourceInfo::RelaySessionCreator");}
virtual SInt64 Run();
RTSPSourceInfo* fInfo;
};
class TeardownTask : public Task
{
public:
TeardownTask(TCPClientSocket* clientSocket, RTSPClient* client);
virtual ~TeardownTask();
virtual SInt64 Run();
private:
TCPClientSocket* fClientSocket;
RTSPClient* fClient;
};
char* fSourceURL;
UInt32 fHostAddr;
UInt16 fHostPort;
UInt32 fLocalAddr;
char* fUserName;
char* fPassword;
RTSPOutputInfo* fRTSPInfoArray;
TCPClientSocket* fClientSocket;
RTSPClient* fClient;
UInt32 fNumSetupsComplete;
Bool16 fDescribeComplete;
StrPtrLen fLocalSDP;
Bool16 fAnnounce;
char* fAnnounceURL;
UInt32 fAnnounceIP;
UInt32 fAnnounceActualIP;
RelaySessionCreator* fRelaySessionCreatorTask;
enum // relay session creation states
{
kSendingDescribe = 0,
kSendingSetup = 1,
kSendingPlay = 2,
kDone = 3
};
UInt32 fSessionCreationState;
RelaySession* fSession;
OSQueue* fSessionQueue;
OSQueueElem fQueueElem;
static StrPtrLen sKeyString;
static StrPtrLen sAnnouncedKeyString;
};
#endif // __RTSP_SOURCE_INFO_H__

View file

@ -0,0 +1,167 @@
/*
*
* @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: ReflectorOutput.h
Contains: VERY simple abstract base class that defines one virtual method, WritePacket.
This is extremely useful to the reflector, which, using one of these objects,
can transparently reflect a packet, not being aware of how it will actually be
written to the network
*/
#ifndef __REFLECTOR_OUTPUT_H__
#define __REFLECTOR_OUTPUT_H__
#include "QTSS.h"
#include "StrPtrLen.h"
#include "OSHeaders.h"
#include "MyAssert.h"
#include "OS.h"
#include "OSQueue.h"
class ReflectorOutput
{
public:
ReflectorOutput() : fBookmarkedPacketsElemsArray(NULL), fNumBookmarks(0), fAvailPosition(0), fLastIntervalMilliSec(5), fLastPacketTransmitTime(0) {}
virtual ~ReflectorOutput()
{
if ( fBookmarkedPacketsElemsArray )
{ ::memset( fBookmarkedPacketsElemsArray, 0, sizeof ( OSQueueElem* ) * fNumBookmarks );
delete [] fBookmarkedPacketsElemsArray;
}
}
// an array of packet elements ( from fPacketQueue in ReflectorSender )
// possibly one for each ReflectorSender that sends data to this ReflectorOutput
OSQueueElem **fBookmarkedPacketsElemsArray;
UInt32 fNumBookmarks;
SInt32 fAvailPosition;
QTSS_TimeVal fLastIntervalMilliSec;
QTSS_TimeVal fLastPacketTransmitTime;
Bool16 fNewOutput;
inline OSQueueElem* GetBookMarkedPacket(OSQueue *thePacketQueue);
inline Bool16 SetBookMarkPacket(OSQueueElem* thePacketElemPtr);
// WritePacket
//
// Pass in the packet contents, the cookie of the stream to which it will be written,
// and the QTSS API write flags (this should either be qtssWriteFlagsIsRTP or IsRTCP
// packetLateness is how many MSec's late this packet is in being delivered ( will be < 0 if its early )
// If this function returns QTSS_WouldBlock, timeToSendThisPacketAgain will
// be set to # of msec in which the packet can be sent, or -1 if unknown
virtual QTSS_Error WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSec, Bool16 firstPacket ) = 0;
virtual void TearDown() = 0;
virtual Bool16 IsUDP() = 0;
virtual Bool16 IsPlaying() = 0;
enum { kWaitMilliSec = 5, kMaxWaitMilliSec = 1000 };
protected:
void InititializeBookmarks( UInt32 numStreams )
{
// need 2 bookmarks for each stream ( include RTCPs )
UInt32 numBookmarks = numStreams * 2;
fBookmarkedPacketsElemsArray = new OSQueueElem*[numBookmarks];
::memset( fBookmarkedPacketsElemsArray, 0, sizeof ( OSQueueElem* ) * numBookmarks );
fNumBookmarks = numBookmarks;
}
};
Bool16 ReflectorOutput::SetBookMarkPacket(OSQueueElem* thePacketElemPtr)
{
if (fAvailPosition != -1 && thePacketElemPtr)
{
fBookmarkedPacketsElemsArray[fAvailPosition] = thePacketElemPtr;
for (UInt32 i = 0; i < fNumBookmarks; i++)
{
if (fBookmarkedPacketsElemsArray[i] == NULL)
{
fAvailPosition = i;
return true;
}
}
}
return false;
}
OSQueueElem* ReflectorOutput::GetBookMarkedPacket(OSQueue *thePacketQueue)
{
Assert(thePacketQueue != NULL);
OSQueueElem* packetElem = NULL;
UInt32 curBookmark = 0;
fAvailPosition = -1;
Assert( curBookmark < fNumBookmarks );
// see if we've bookmarked a held packet for this Sender in this Output
while ( curBookmark < fNumBookmarks )
{
OSQueueElem* bookmarkedElem = fBookmarkedPacketsElemsArray[curBookmark];
if ( bookmarkedElem ) // there may be holes in this array
{
if ( bookmarkedElem->IsMember( *thePacketQueue ) )
{
// this packet was previously bookmarked for this specific queue
// remove if from the bookmark list and use it
// to jump ahead into the Sender's over all packet queue
fBookmarkedPacketsElemsArray[curBookmark] = NULL;
fAvailPosition = curBookmark;
packetElem = bookmarkedElem;
break;
}
}
else
{
fAvailPosition = curBookmark;
}
curBookmark++;
}
return packetElem;
}
#endif //__REFLECTOR_OUTPUT_H__

View file

@ -0,0 +1,404 @@
/*
*
* @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: ReflectorSession.cpp
Contains: Implementation of object defined in ReflectorSession.h.
*/
#include "ReflectorSession.h"
#include "RTCPPacket.h"
#include "SocketUtils.h"
#include "EventContext.h"
#include "OSMemory.h"
#include "OS.h"
#include "atomic.h"
#include "QTSSModuleUtils.h"
#include <errno.h>
#ifndef __Win32__
#include <unistd.h>
#endif
#if DEBUG
#define REFLECTOR_SESSION_DEBUGGING 0
#else
#define REFLECTOR_SESSION_DEBUGGING 0
#endif
FileDeleter::FileDeleter(StrPtrLen* inSDPPath)
{
Assert (inSDPPath);
fFilePath.Len = inSDPPath->Len;
fFilePath.Ptr = NEW char[inSDPPath->Len + 1];
Assert (fFilePath.Ptr);
memcpy(fFilePath.Ptr, inSDPPath->Ptr,inSDPPath->Len);
fFilePath.Ptr[inSDPPath->Len] = 0;
}
FileDeleter::~FileDeleter()
{
//qtss_printf("FileDeleter::~FileDeleter delete = %s \n",fFilePath.Ptr);
::unlink(fFilePath.Ptr);
delete fFilePath.Ptr;
fFilePath.Ptr = NULL;
fFilePath.Len = 0;
}
static OSRefTable* sStreamMap = NULL;
void ReflectorSession::Initialize()
{
if (sStreamMap == NULL)
sStreamMap = NEW OSRefTable();
}
ReflectorSession::ReflectorSession(StrPtrLen* inSourceID, SourceInfo* inInfo)
: fIsSetup(false),
fQueueElem(),
fNumOutputs(0),
fStreamArray(NULL),
fFormatter(fHTMLBuf, kMaxHTMLSize),
fSourceInfo(inInfo),
fSocketStream(NULL),
fBroadcasterSession(NULL),
fInitTimeMS(OS::Milliseconds()),
fHasBufferedStreams(false)
{
fQueueElem.SetEnclosingObject(this);
if (inSourceID != NULL)
{
fSourceID.Ptr = NEW char[inSourceID->Len + 1];
::memcpy(fSourceID.Ptr, inSourceID->Ptr, inSourceID->Len);
fSourceID.Len = inSourceID->Len;
fRef.Set(fSourceID, this);
}
}
ReflectorSession::~ReflectorSession()
{
#if REFLECTOR_SESSION_DEBUGGING
qtss_printf("Removing ReflectorSession: %s\n", fSourceInfoHTML.Ptr);
#endif
// For each stream, check to see if the ReflectorStream should be deleted
OSMutexLocker locker (sStreamMap->GetMutex());
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
{
if (fStreamArray[x] == NULL)
continue;
UInt32 refCount = fStreamArray[x]->GetRef()->GetRefCount();
Bool16 unregisterNow = (refCount == 1) ? true : false;
//qtss_printf("ReflectorSession::~ReflectorSession stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
//decrement the ref count
if (refCount > 0) // Refcount may be 0 if there was some error setting up the stream
sStreamMap->Release(fStreamArray[x]->GetRef()); // decrement the refcount
refCount = fStreamArray[x]->GetRef()->GetRefCount();
if (refCount == 0)
{ // Delete this stream if the refcount has dropped to 0
if (unregisterNow)
sStreamMap->UnRegister(fStreamArray[x]->GetRef()); // Refcount may be 0 if there was some error setting up the stream
//qtss_printf("delete stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
delete fStreamArray[x];
fStreamArray[x] = NULL;
}
}
// We own this object when it is given to us, so delete it now
delete [] fStreamArray;
delete fSourceInfo;
fLocalSDP.Delete();
fSourceID.Delete();
}
QTSS_Error ReflectorSession::SetupReflectorSession(SourceInfo* inInfo, QTSS_StandardRTSP_Params* inParams, UInt32 inFlags, Bool16 filterState, UInt32 filterTimeout)
{
if (inInfo == NULL) // use the current SourceInfo
inInfo = fSourceInfo;
// Store a reference to this sourceInfo permanently
Assert((fSourceInfo == NULL) || (inInfo == fSourceInfo));
fSourceInfo = inInfo;
fLocalSDP.Delete();// this must be set to the new SDP.
fLocalSDP.Ptr = inInfo->GetLocalSDP(&fLocalSDP.Len);
// Allocate all our ReflectorStreams, using the SourceInfo
if (fStreamArray != NULL)
{ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
if (fSourceInfo->GetStreamInfo(x)->fPort > 0 && fStreamArray[x] != NULL)
sStreamMap->Release(fStreamArray[x]->GetRef());
}
delete fStreamArray; // keep the array list synchronized with the source info.
fStreamArray = NEW ReflectorStream*[fSourceInfo->GetNumStreams()];
::memset(fStreamArray, 0, fSourceInfo->GetNumStreams() * sizeof(ReflectorStream*));
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
{
// For each ReflectorStream, check and see if there is one that matches
// this stream ID
char theStreamID[ReflectorStream::kStreamIDSize];
StrPtrLen theStreamIDPtr(theStreamID, ReflectorStream::kStreamIDSize);
ReflectorStream::GenerateSourceID(fSourceInfo->GetStreamInfo(x), &theStreamID[0]);
OSMutexLocker locker(sStreamMap->GetMutex());
OSRef* theStreamRef = NULL;
if (false && (inFlags & kIsPushSession)) // always setup our own ports when pushed.
fSourceInfo->GetStreamInfo(x)->fPort = 0;
else
// If the port # of this stream is 0, that means "any port".
// We don't know what the dest port of this stream is yet (this
// can happen while acting as an RTSP client). Never share these streams.
// This can also happen if the incoming data is interleaved in the TCP connection or a dynamic UDP port is requested
if (fSourceInfo->GetStreamInfo(x)->fPort > 0)
{ theStreamRef = sStreamMap->Resolve(&theStreamIDPtr);
#if REFLECTOR_SESSION_DEBUGGING
if (theStreamRef != NULL)
{
ReflectorStream* theRef = (ReflectorStream*)theStreamRef->GetObject();
UInt32 refCount = theRef->GetRef()->GetRefCount();
qtss_printf("stream has port stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
}
#endif
}
if (theStreamRef == NULL)
{
fStreamArray[x] = NEW ReflectorStream(fSourceInfo->GetStreamInfo(x));
// Obviously, we may encounter an error binding the reflector sockets.
// If that happens, we'll just abort here, which will leave the ReflectorStream
// array in an inconsistent state, so we need to make sure in our cleanup
// code to check for NULL.
QTSS_Error theError = fStreamArray[x]->BindSockets(inParams,inFlags, filterState, filterTimeout);
if (theError != QTSS_NoErr)
{
delete fStreamArray[x];
fStreamArray[x] = NULL;
return theError;
}
fStreamArray[x]->SetEnableBuffer(this->fHasBufferedStreams);// buffering is done by the stream's sender
// If the port was 0, update it to reflect what the actual RTP port is.
fSourceInfo->GetStreamInfo(x)->fPort = fStreamArray[x]->GetStreamInfo()->fPort;
//qtss_printf("ReflectorSession::SetupReflectorSession fSourceInfo->GetStreamInfo(x)->fPort= %u\n",fSourceInfo->GetStreamInfo(x)->fPort);
ReflectorStream::GenerateSourceID(fSourceInfo->GetStreamInfo(x), &theStreamID[0]);
theError = sStreamMap->Register(fStreamArray[x]->GetRef());
Assert(theError == QTSS_NoErr);
//unless we do this, the refcount won't increment (and we'll delete the session prematurely
OSRef* debug = sStreamMap->Resolve(&theStreamIDPtr);
Assert(debug == fStreamArray[x]->GetRef());
//UInt32 refCount = fStreamArray[x]->GetRef()->GetRefCount();
//qtss_printf("stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
}
else
fStreamArray[x] = (ReflectorStream*)theStreamRef->GetObject();
}
if (inFlags & kMarkSetup)
fIsSetup = true;
return QTSS_NoErr;
}
void ReflectorSession::AddBroadcasterClientSession(QTSS_StandardRTSP_Params* inParams)
{
if (NULL == fStreamArray || NULL == inParams)
return;
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
{
if (fStreamArray[x] != NULL)
{ //qtss_printf("AddBroadcasterSession=%"_U32BITARG_"\n",inParams->inClientSession);
((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketA())->AddBroadcasterSession(inParams->inClientSession);
((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketB())->AddBroadcasterSession(inParams->inClientSession);
}
}
fBroadcasterSession = inParams->inClientSession;
}
void ReflectorSession::FormatHTML(StrPtrLen* inURL)
{
// Begin writing our source description HTML (used by the relay)
// Line looks like: Relay Source: 17.221.98.239, Ports: 5430 5432 5434
static StrPtrLen sHTMLStart("<H2>Relay Source: ");
static StrPtrLen sPorts(", Ports: ");
static StrPtrLen sHTMLEnd("</H2><BR>");
// Begin writing the HTML
fFormatter.Put(sHTMLStart);
if (inURL == NULL)
{
// If no URL is provided, format the source IP addr as a string.
char theIPAddrBuf[20];
StrPtrLen theIPAddr(theIPAddrBuf, 20);
struct in_addr theAddr;
theAddr.s_addr = htonl(fSourceInfo->GetStreamInfo(0)->fSrcIPAddr);
SocketUtils::ConvertAddrToString(theAddr, &theIPAddr);
fFormatter.Put(theIPAddr);
}
else
fFormatter.Put(*inURL);
fFormatter.Put(sPorts);
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
{
fFormatter.Put(fSourceInfo->GetStreamInfo(x)->fPort);
fFormatter.PutSpace();
}
fFormatter.Put(sHTMLEnd);
// Setup the StrPtrLen to point to the right stuff
fSourceInfoHTML.Ptr = fFormatter.GetBufPtr();
fSourceInfoHTML.Len = fFormatter.GetCurrentOffset();
fFormatter.PutTerminator();
}
void ReflectorSession::AddOutput(ReflectorOutput* inOutput, Bool16 isClient)
{
Assert(fSourceInfo->GetNumStreams() > 0);
// We need to make sure that this output goes into the same bucket for each ReflectorStream.
SInt32 bucket = -1;
SInt32 lastBucket = -1;
while (true)
{
UInt32 x = 0;
for ( ; x < fSourceInfo->GetNumStreams(); x++)
{
bucket = fStreamArray[x]->AddOutput(inOutput, bucket);
if (bucket == -1) // If this output couldn't be added to this bucket,
break; // break and try again
else
{
lastBucket = bucket; // Remember the last successful bucket placement.
if (isClient)
fStreamArray[x]->IncEyeCount();
}
}
if (bucket == -1)
{
// If there was some kind of conflict adding this output to this bucket,
// we need to remove it from the streams to which it was added.
for (UInt32 y = 0; y < x; y++)
{
fStreamArray[y]->RemoveOutput(inOutput);
if (isClient)
fStreamArray[y]->DecEyeCount();
}
// Because there was an error, we need to start the whole process over again,
// this time starting from a higher bucket
lastBucket = bucket = lastBucket + 1;
}
else
break;
}
(void)atomic_add(&fNumOutputs, 1);
}
void ReflectorSession::RemoveOutput(ReflectorOutput* inOutput, Bool16 isClient)
{
(void)atomic_sub(&fNumOutputs, 1);
for (UInt32 y = 0; y < fSourceInfo->GetNumStreams(); y++)
{
fStreamArray[y]->RemoveOutput(inOutput);
if (isClient)
fStreamArray[y]->DecEyeCount();
}
}
void ReflectorSession::TearDownAllOutputs()
{
for (UInt32 y = 0; y < fSourceInfo->GetNumStreams(); y++)
fStreamArray[y]->TearDownAllOutputs();
}
void ReflectorSession::RemoveSessionFromOutput(QTSS_ClientSessionObject inSession)
{
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
{ ((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketA())->RemoveBroadcasterSession(inSession);
((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketB())->RemoveBroadcasterSession(inSession);
}
fBroadcasterSession = NULL;
}
UInt32 ReflectorSession::GetBitRate()
{
UInt32 retval = 0;
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
retval += fStreamArray[x]->GetBitRate();
return retval;
}
Bool16 ReflectorSession::Equal(SourceInfo* inInfo)
{
return fSourceInfo->Equal(inInfo);
}
void* ReflectorSession::GetStreamCookie(UInt32 inStreamID)
{
for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
{
if (fSourceInfo->GetStreamInfo(x)->fTrackID == inStreamID)
return fStreamArray[x]->GetStreamCookie();
}
return NULL;
}

View file

@ -0,0 +1,201 @@
/*
*
* @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: ReflectorSession.h
Contains: This object supports reflecting an RTP multicast stream to N
RTPStreams. It spaces out the packet send times in order to
maximize the randomness of the sending pattern and smooth
the stream.
*/
#include "QTSS.h"
#include "OSRef.h"
#include "StrPtrLen.h"
#include "ResizeableStringFormatter.h"
#include "MyAssert.h"
#include "ReflectorStream.h"
#include "SourceInfo.h"
#include "OSArrayObjectDeleter.h"
#ifndef _FILE_DELETER_
#define _FILE_DELETER_
class FileDeleter
{ public:
FileDeleter(StrPtrLen* inSDPPath);
~FileDeleter();
private:
StrPtrLen fFilePath;
};
#endif
#ifndef __REFLECTOR_SESSION__
#define __REFLECTOR_SESSION__
class ReflectorSession
{
public:
// Public interface to generic RTP packet forwarding engine
//
// Initialize
//
// Call initialize before calling any other function in this class
static void Initialize();
// Create one of these ReflectorSessions per source broadcast. For mapping purposes,
// the object can be constructred using an optional source ID.
//
// Caller may also provide a SourceInfo object, though it is not needed and
// will also need to be provided to SetupReflectorSession when that is called.
ReflectorSession(StrPtrLen* inSourceID, SourceInfo* inInfo = NULL);
virtual ~ReflectorSession();
//
// MODIFIERS
// Call this to initialize and setup the source sockets. Once this function
// completes sucessfully, Outputs may be added through the function calls below.
//
// The SourceInfo object passed in here will be owned by the ReflectorSession. Do not
// delete it.
enum
{
kMarkSetup = 0, //After SetupReflectorSession is called, IsSetup returns true
kDontMarkSetup = 1, //After SetupReflectorSession is called, IsSetup returns false
kIsPushSession = 2 // When setting up streams handle port conflicts by allocating.
};
QTSS_Error SetupReflectorSession(SourceInfo* inInfo, QTSS_StandardRTSP_Params* inParams,
UInt32 inFlags = kMarkSetup, Bool16 filterState = true, UInt32 filterTimeout = 30);
// Packets get forwarded by attaching ReflectorOutput objects to a ReflectorSession.
void AddOutput(ReflectorOutput* inOutput, Bool16 isClient);
void RemoveOutput(ReflectorOutput* inOutput, Bool16 isClient);
void TearDownAllOutputs();
void RemoveSessionFromOutput(QTSS_ClientSessionObject inSession);
void ManuallyMarkSetup() { fIsSetup = true; }
// For the Relay's status, a ReflectorSession can format an informative bit of
// HTML to describe the source. This must be called after the ReflectorSession
// is all setup.
void FormatHTML(StrPtrLen* inURL);
//
// ACCESSORS
OSRef* GetRef() { return &fRef; }
OSQueueElem* GetQueueElem() { return &fQueueElem; }
UInt32 GetNumOutputs() { return fNumOutputs; }
UInt32 GetNumStreams() { return fSourceInfo->GetNumStreams(); }
StrPtrLen* GetSourceInfoHTML() { return &fSourceInfoHTML; }
SourceInfo* GetSourceInfo() { return fSourceInfo; }
StrPtrLen* GetLocalSDP() { return &fLocalSDP; }
StrPtrLen* GetSourcePath() { return &fSourceID; }
Bool16 IsSetup() { return fIsSetup; }
ReflectorStream*GetStreamByIndex(UInt32 inIndex) { return fStreamArray[inIndex]; }
void AddBroadcasterClientSession(QTSS_StandardRTSP_Params* inParams);
QTSS_ClientSessionObject GetBroadcasterSession() { return fBroadcasterSession;}
// For the QTSSSplitterModule, this object can cache a QTSS_StreamRef
void SetSocketStream(QTSS_StreamRef inStream) { fSocketStream = inStream; }
QTSS_StreamRef GetSocketStream() { return fSocketStream; }
// A ReflectorSession keeps track of the aggregate bit rate each
// stream is reflecting (RTP only). Initially, this will return 0
// until enough time passes to compute an accurate average.
UInt32 GetBitRate();
// Returns true if this SourceInfo structure is equivalent to this
// ReflectorSession.
Bool16 Equal(SourceInfo* inInfo);
// Each stream has a cookie associated with it. When the stream writes a packet
// to an output, this cookie is used to identify which stream is writing the packet.
// The below function is useful so outputs can get the cookie value for a stream ID,
// and therefore mux the cookie to the right output stream.
void* GetStreamCookie(UInt32 inStreamID);
//Reflector quality levels:
enum
{
kMaxHTMLSize = 128,
kAudioOnlyQuality = 1, //UInt32
kNormalQuality = 0, //UInt32
kNumQualityLevels = 2 //UInt32
};
SInt64 GetInitTimeMS() { return fInitTimeMS; }
void SetHasBufferedStreams(Bool16 enableBuffer) { fHasBufferedStreams = enableBuffer; }
private:
// Is this session setup?
Bool16 fIsSetup;
// For storage in the session map
OSRef fRef;
StrPtrLen fSourceID;
OSQueueElem fQueueElem; // Relay uses this.
unsigned int fNumOutputs;
ReflectorStream** fStreamArray;
char fHTMLBuf[kMaxHTMLSize];
StrPtrLen fSourceInfoHTML;
ResizeableStringFormatter fFormatter;
// The reflector session needs to hang onto the source info object
// for it's entire lifetime. Right now, this is used for reflector-as-client.
SourceInfo* fSourceInfo;
StrPtrLen fLocalSDP;
// For the QTSSSplitterModule, this object can cache a QTSS_StreamRef
QTSS_StreamRef fSocketStream;
QTSS_ClientSessionObject fBroadcasterSession;
SInt64 fInitTimeMS;
Bool16 fHasBufferedStreams;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,513 @@
/*
*
* @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: ReflectorStream.h
Contains: This object supports reflecting an RTP multicast stream to N
RTPStreams. It spaces out the packet send times in order to
maximize the randomness of the sending pattern and smooth
the stream.
*/
#ifndef _REFLECTOR_STREAM_H_
#define _REFLECTOR_STREAM_H_
#include "QTSS.h"
#include "IdleTask.h"
#include "SourceInfo.h"
#include "UDPSocket.h"
#include "UDPSocketPool.h"
#include "UDPDemuxer.h"
#include "EventContext.h"
#include "SequenceNumberMap.h"
#include "OSMutex.h"
#include "OSQueue.h"
#include "OSRef.h"
#include "RTCPSRPacket.h"
#include "ReflectorOutput.h"
#include "atomic.h"
//This will add some printfs that are useful for checking the thinning
#define REFLECTOR_THINNING_DEBUGGING 0
//Define to use new potential workaround for NAT problems
#define NAT_WORKAROUND 1
class ReflectorPacket;
class ReflectorSender;
class ReflectorStream;
class RTPSessionOutput;
class ReflectorPacket
{
public:
ReflectorPacket() : fQueueElem() { fQueueElem.SetEnclosingObject(this); this->Reset();}
void Reset() { // make packet ready to reuse fQueueElem is always in use
fBucketsSeenThisPacket = 0;
fTimeArrived = 0;
//fQueueElem -- should be set to this
fPacketPtr.Set(fPacketData, 0);
fIsRTCP = false;
fStreamCountID = 0;
fNeededByOutput = false;
}
~ReflectorPacket() {}
void SetPacketData(char *data, UInt32 len) { Assert(kMaxReflectorPacketSize > len); if (len > 0) memcpy(this->fPacketPtr.Ptr,data,len); this->fPacketPtr.Len = len;}
Bool16 IsRTCP() { return fIsRTCP; }
inline UInt32 GetPacketRTPTime();
inline UInt16 GetPacketRTPSeqNum();
inline UInt32 GetSSRC(Bool16 isRTCP);
inline SInt64 GetPacketNTPTime();
private:
enum
{
kMaxReflectorPacketSize = 2060 //jm 5/02 increased from 2048 by 12 bytes for test bytes appended to packets
};
UInt32 fBucketsSeenThisPacket;
SInt64 fTimeArrived;
OSQueueElem fQueueElem;
char fPacketData[kMaxReflectorPacketSize];
StrPtrLen fPacketPtr;
Bool16 fIsRTCP;
Bool16 fNeededByOutput; // is this packet still needed for output?
UInt64 fStreamCountID;
friend class ReflectorSender;
friend class ReflectorSocket;
friend class RTPSessionOutput;
};
UInt32 ReflectorPacket::GetSSRC(Bool16 isRTCP)
{
if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 8)
return 0;
UInt32* theSsrcPtr = (UInt32*)fPacketPtr.Ptr;
if (isRTCP)// RTCP
return ntohl(theSsrcPtr[1]);
if (fPacketPtr.Len < 12)
return 0;
return ntohl(theSsrcPtr[2]); // RTP SSRC
}
UInt32 ReflectorPacket::GetPacketRTPTime()
{
UInt32 timestamp = 0;
if (!fIsRTCP)
{
//The RTP timestamp number is the second long of the packet
if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 8)
return 0;
timestamp = ntohl( ((UInt32*)fPacketPtr.Ptr)[1]);
}
else
{
if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 20)
return 0;
timestamp = ntohl( ((UInt32*)fPacketPtr.Ptr)[4]);
}
return timestamp;
}
UInt16 ReflectorPacket::GetPacketRTPSeqNum()
{
Assert(!fIsRTCP); // not a supported type
if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 4 || fIsRTCP)
return 0;
UInt16 sequence = ntohs( ((UInt16*)fPacketPtr.Ptr)[1]); //The RTP sequenc number is the second short of the packet
return sequence;
}
SInt64 ReflectorPacket::GetPacketNTPTime()
{
Assert(fIsRTCP); // not a supported type
if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 16 || !fIsRTCP)
return 0;
UInt32* theReport = (UInt32*)fPacketPtr.Ptr;
theReport +=2;
SInt64 ntp = 0;
::memcpy(&ntp, theReport, sizeof(SInt64));
return OS::Time1900Fixed64Secs_To_TimeMilli(OS::NetworkToHostSInt64(ntp));
}
//Custom UDP socket classes for doing reflector packet retrieval, socket management
class ReflectorSocket : public IdleTask, public UDPSocket
{
public:
ReflectorSocket();
virtual ~ReflectorSocket();
void AddBroadcasterSession(QTSS_ClientSessionObject inSession) { OSMutexLocker locker(this->GetDemuxer()->GetMutex()); fBroadcasterClientSession = inSession; }
void RemoveBroadcasterSession(QTSS_ClientSessionObject inSession){ OSMutexLocker locker(this->GetDemuxer()->GetMutex()); if (inSession == fBroadcasterClientSession) fBroadcasterClientSession = NULL; }
void AddSender(ReflectorSender* inSender);
void RemoveSender(ReflectorSender* inStreamElem);
Bool16 HasSender() { return (this->GetDemuxer()->GetHashTable()->GetNumEntries() > 0); }
Bool16 ProcessPacket(const SInt64& inMilliseconds,ReflectorPacket* thePacket,UInt32 theRemoteAddr,UInt16 theRemotePort);
ReflectorPacket* GetPacket();
virtual SInt64 Run();
void SetSSRCFilter(Bool16 state, UInt32 timeoutSecs) { fFilterSSRCs = state; fTimeoutSecs = timeoutSecs;}
private:
//virtual SInt64 Run();
void GetIncomingData(const SInt64& inMilliseconds);
void FilterInvalidSSRCs(ReflectorPacket* thePacket,Bool16 isRTCP);
//Number of packets to allocate when the socket is first created
enum
{
kNumPreallocatedPackets = 20, //UInt32
kRefreshBroadcastSessionIntervalMilliSecs = 10000,
kSSRCTimeOut = 30000 // milliseconds before clearing the SSRC if no new ssrcs have come in
};
QTSS_ClientSessionObject fBroadcasterClientSession;
SInt64 fLastBroadcasterTimeOutRefresh;
// Queue of available ReflectorPackets
OSQueue fFreeQueue;
// Queue of senders
OSQueue fSenderQueue;
SInt64 fSleepTime;
UInt32 fValidSSRC;
SInt64 fLastValidSSRCTime;
Bool16 fFilterSSRCs;
UInt32 fTimeoutSecs;
Bool16 fHasReceiveTime;
UInt64 fFirstReceiveTime;
SInt64 fFirstArrivalTime;
UInt32 fCurrentSSRC;
};
class ReflectorSocketPool : public UDPSocketPool
{
public:
ReflectorSocketPool() {}
virtual ~ReflectorSocketPool() {}
virtual UDPSocketPair* ConstructUDPSocketPair();
virtual void DestructUDPSocketPair(UDPSocketPair *inPair);
virtual void SetUDPSocketOptions(UDPSocketPair* inPair);
void DestructUDPSocket( ReflectorSocket* socket);
};
class ReflectorSender : public UDPDemuxerTask
{
public:
ReflectorSender(ReflectorStream* inStream, UInt32 inWriteFlag);
virtual ~ReflectorSender();
// Queue of senders
OSQueue fSenderQueue;
SInt64 fSleepTime;
//Used for adjusting sequence numbers in light of thinning
UInt16 GetPacketSeqNumber(const StrPtrLen& inPacket);
void SetPacketSeqNumber(const StrPtrLen& inPacket, UInt16 inSeqNumber);
Bool16 PacketShouldBeThinned(QTSS_RTPStreamObject inStream, const StrPtrLen& inPacket);
//We want to make sure that ReflectPackets only gets invoked when there
//is actually work to do, because it is an expensive function
Bool16 ShouldReflectNow(const SInt64& inCurrentTime, SInt64* ioWakeupTime);
//This function gets data from the multicast source and reflects.
//Returns the time at which it next needs to be invoked
void ReflectPackets(SInt64* ioWakeupTime, OSQueue* inFreeQueue);
//this is the old way of doing reflect packets. It is only here until the relay code can be cleaned up.
void ReflectRelayPackets(SInt64* ioWakeupTime, OSQueue* inFreeQueue);
OSQueueElem* SendPacketsToOutput(ReflectorOutput* theOutput, OSQueueElem* currentPacket, SInt64 currentTime, SInt64 bucketDelay, Bool16 firstPacket);
UInt32 GetOldestPacketRTPTime(Bool16 *foundPtr);
UInt16 GetFirstPacketRTPSeqNum(Bool16 *foundPtr);
Bool16 GetFirstPacketInfo(UInt16* outSeqNumPtr, UInt32* outRTPTimePtr, SInt64* outArrivalTimePtr);
OSQueueElem*GetClientBufferNextPacketTime(UInt32 inRTPTime);
Bool16 GetFirstRTPTimePacket(UInt16* outSeqNumPtr, UInt32* outRTPTimePtr, SInt64* outArrivalTimePtr);
void RemoveOldPackets(OSQueue* inFreeQueue);
OSQueueElem* GetClientBufferStartPacketOffset(SInt64 offsetMsec);
OSQueueElem* GetClientBufferStartPacket() { return this->GetClientBufferStartPacketOffset(0); };
ReflectorStream* fStream;
UInt32 fWriteFlag;
OSQueue fPacketQueue;
OSQueueElem* fFirstNewPacketInQueue;
OSQueueElem* fFirstPacketInQueueForNewOutput;
//these serve as an optimization, keeping track of when this
//sender needs to run so it doesn't run unnecessarily
inline void SetNextTimeToRun(SInt64 nextTime) { fNextTimeToRun = nextTime;
//qtss_printf("SetNextTimeToRun =%"_64BITARG_"d\n", fNextTimeToRun);
}
Bool16 fHasNewPackets;
SInt64 fNextTimeToRun;
//how often to send RRs to the source
enum
{
kRRInterval = 5000 //SInt64 (every 5 seconds)
};
SInt64 fLastRRTime;
OSQueueElem fSocketQueueElem;
friend class ReflectorSocket;
friend class ReflectorStream;
};
class ReflectorStream
{
public:
enum
{
// A ReflectorStream is uniquely identified by the
// destination IP address & destination port of the broadcast.
// This ID simply contains that information.
//
// A unicast broadcast can also be identified by source IP address. If
// you are attempting to demux by source IP, this ID will not guarentee
// uniqueness and special care should be used.
kStreamIDSize = sizeof(UInt32) + sizeof(UInt16)
};
// Uses a StreamInfo to generate a unique ID
static void GenerateSourceID(SourceInfo::StreamInfo* inInfo, char* ioBuffer);
ReflectorStream(SourceInfo::StreamInfo* inInfo);
~ReflectorStream();
//
// SETUP
//
// Call Register from the Register role, as this object has some QTSS API
// attributes to setup
static void Register();
static void Initialize(QTSS_ModulePrefsObject inPrefs);
//
// MODIFIERS
// Call this to initialize the reflector sockets. Uses the QTSS_RTSPRequestObject
// if provided to report any errors that occur
// Passes the QTSS_ClientSessionObject to the socket so the socket can update the session if needed.
QTSS_Error BindSockets(QTSS_StandardRTSP_Params* inParams, UInt32 inReflectorSessionFlags, Bool16 filterState, UInt32 timeout);
// This stream reflects packets from the broadcast to specific ReflectorOutputs.
// You attach outputs to ReflectorStreams this way. You can force the ReflectorStream
// to put this output into a certain bucket by passing in a certain bucket index.
// Pass in -1 if you don't care. AddOutput returns the bucket index this output was
// placed into, or -1 on an error.
SInt32 AddOutput(ReflectorOutput* inOutput, SInt32 putInThisBucket);
// Removes the specified output from this ReflectorStream.
void RemoveOutput(ReflectorOutput* inOutput); // Removes this output from all tracks
void TearDownAllOutputs(); // causes a tear down and then a remove
// If the incoming data is RTSP interleaved, packets for this stream are identified
// by channel numbers
void SetRTPChannelNum(SInt16 inChannel) { fRTPChannel = inChannel; }
void SetRTCPChannelNum(SInt16 inChannel) { fRTCPChannel = inChannel; }
void PushPacket(char *packet, UInt32 packetLen, Bool16 isRTCP);
//
// ACCESSORS
OSRef* GetRef() { return &fRef; }
UInt32 GetBitRate() { return fCurrentBitRate; }
SourceInfo::StreamInfo* GetStreamInfo() { return &fStreamInfo; }
OSMutex* GetMutex() { return &fBucketMutex; }
void* GetStreamCookie() { return this; }
SInt16 GetRTPChannel() { return fRTPChannel; }
SInt16 GetRTCPChannel() { return fRTCPChannel; }
UDPSocketPair* GetSocketPair() { return fSockets;}
ReflectorSender* GetRTPSender() { return &fRTPSender; }
ReflectorSender* GetRTCPSender() { return &fRTCPSender; }
void SetHasFirstRTCP(Bool16 hasPacket) { fHasFirstRTCPPacket = hasPacket; }
Bool16 HasFirstRTCP() { return fHasFirstRTCPPacket; }
void SetFirst_RTCP_RTP_Time(UInt32 time) { fFirst_RTCP_RTP_Time = time; }
UInt32 GetFirst_RTCP_RTP_Time() { return fFirst_RTCP_RTP_Time; }
void SetFirst_RTCP_Arrival_Time(SInt64 time) { fFirst_RTCP_Arrival_Time = time; }
SInt64 GetFirst_RTCP_Arrival_Time() { return fFirst_RTCP_Arrival_Time; }
void SetHasFirstRTP(Bool16 hasPacket) { fHasFirstRTPPacket = hasPacket; }
Bool16 HasFirstRTP() { return fHasFirstRTPPacket; }
UInt32 GetBufferDelay() { return ReflectorStream::sOverBufferInMsec; }
UInt32 GetTimeScale() { return fStreamInfo.fTimeScale; }
UInt64 fPacketCount;
void SetEnableBuffer(Bool16 enableBuffer) { fEnableBuffer = enableBuffer; }
Bool16 BufferEnabled() { return fEnableBuffer; }
inline void UpdateBitRate(SInt64 currentTime);
static UInt32 sOverBufferInMsec;
void IncEyeCount() { OSMutexLocker locker(&fBucketMutex); fEyeCount ++; }
void DecEyeCount() { OSMutexLocker locker(&fBucketMutex); fEyeCount --; }
UInt32 GetEyeCount() { OSMutexLocker locker(&fBucketMutex); return fEyeCount; }
private:
//Sends an RTCP receiver report to the broadcast source
void SendReceiverReport();
void AllocateBucketArray(UInt32 inNumBuckets);
SInt32 FindBucket();
// Unique ID & OSRef. ReflectorStreams can be mapped & shared
OSRef fRef;
char fSourceIDBuf[kStreamIDSize];
// Reflector sockets, retrieved from the socket pool
UDPSocketPair* fSockets;
ReflectorSender fRTPSender;
ReflectorSender fRTCPSender;
SequenceNumberMap fSequenceNumberMap; //for removing duplicate packets
// All the necessary info about this stream
SourceInfo::StreamInfo fStreamInfo;
enum
{
kReceiverReportSize = 16, //UInt32
kAppSize = 36, //UInt32
kMinNumBuckets = 16, //UInt32
kBitRateAvgIntervalInMilSecs = 30000 // time between bitrate averages
};
// BUCKET ARRAY
//ReflectorOutputs are kept in a 2-dimensional array, "Buckets"
typedef ReflectorOutput** Bucket;
Bucket* fOutputArray;
UInt32 fNumBuckets; //Number of buckets currently
UInt32 fNumElements; //Number of reflector outputs in the array
//Bucket array can't be modified while we are sending packets.
OSMutex fBucketMutex;
// RTCP RR information
char fReceiverReportBuffer[kReceiverReportSize + kAppSize +
RTCPSRPacket::kMaxCNameLen];
UInt32* fEyeLocation;//place in the buffer to write the eye information
UInt32 fReceiverReportSize;
// This is the destination address & port for RTCP
// receiver reports.
UInt32 fDestRTCPAddr;
UInt16 fDestRTCPPort;
// Used for calculating average bit rate
UInt32 fCurrentBitRate;
SInt64 fLastBitRateSample;
unsigned int fBytesSentInThisInterval;// unsigned int because we need to atomic_add
// If incoming data is RTSP interleaved
SInt16 fRTPChannel; //These will be -1 if not set to anything
SInt16 fRTCPChannel;
Bool16 fHasFirstRTCPPacket;
Bool16 fHasFirstRTPPacket;
Bool16 fEnableBuffer;
UInt32 fEyeCount;
UInt32 fFirst_RTCP_RTP_Time;
SInt64 fFirst_RTCP_Arrival_Time;
static UInt32 sBucketSize;
static UInt32 sMaxPacketAgeMSec;
static UInt32 sMaxFuturePacketSec;
static UInt32 sMaxFuturePacketMSec;
static UInt32 sOverBufferInSec;
static UInt32 sBucketDelayInMsec;
static Bool16 sUsePacketReceiveTime;
static UInt32 sFirstPacketOffsetMsec;
friend class ReflectorSocket;
friend class ReflectorSender;
};
void ReflectorStream::UpdateBitRate(SInt64 currentTime)
{
if ((fLastBitRateSample + ReflectorStream::kBitRateAvgIntervalInMilSecs) < currentTime)
{
unsigned int intervalBytes = fBytesSentInThisInterval;
(void)atomic_sub(&fBytesSentInThisInterval, intervalBytes);
// Multiply by 1000 to convert from milliseconds to seconds, and by 8 to convert from bytes to bits
Float32 bps = (Float32)(intervalBytes * 8) / (Float32)(currentTime - fLastBitRateSample);
bps *= 1000;
fCurrentBitRate = (UInt32)bps;
// Don't check again for awhile!
fLastBitRateSample = currentTime;
}
}
#endif //_REFLECTOR_SESSION_H_

View file

@ -0,0 +1,580 @@
/*
*
* @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: RelayOutput.cpp
Contains: Implementation of object described in .h file
*/
#include "RelayOutput.h"
#include "OSMemory.h"
#include "SocketUtils.h"
#include "RTSPSourceInfo.h"
static StrPtrLen sUDPDestStr("udp_destination");
static StrPtrLen sAnnouncedDestStr("announced_destination");
// STATIC DATA
OSQueue RelayOutput::sRelayOutputQueue;
OSMutex RelayOutput::sQueueMutex;
QTSS_ObjectType RelayOutput::qtssRelayOutputObjectType;
QTSS_AttributeID RelayOutput::sOutputType = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputDestAddr = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputLocalAddr = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputUDPPorts = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputRTSPPort = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputURL = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputTTL = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputCurPacketsPerSec = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputCurBitsPerSec = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputTotalPacketsSent = qtssIllegalAttrID;
QTSS_AttributeID RelayOutput::sOutputTotalBytesSent = qtssIllegalAttrID;
static char* sOutputTypeName = "output_type";
static char* sOutputDestAddrName = "output_dest_addr";
static char* sOutputLocalAddrName = "output_local_addr";
static char* sOutputUDPPortsName = "output_udp_ports";
static char* sOutputRTSPPortName = "output_rtsp_port";
static char* sOutputURLName = "output_url";
static char* sOutputTTLName = "output_ttl";
static char* sOutputCurPacketsPerSecName = "output_cur_packetspersec";
static char* sOutputCurBitsPerSecName = "output_cur_bitspersec";
static char* sOutputTotalPacketsSentName = "output_total_packets_sent";
static char* sOutputTotalBytesSentName = "output_total_bytes_sent";
void RelayOutput::Register()
{
// Create the relay output object type
(void)QTSS_CreateObjectType(&qtssRelayOutputObjectType);
// Add the static attributes to the qtssRelayOutputObjectType object
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTypeName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTypeName, &sOutputType); // dest type
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputDestAddrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputDestAddrName, &sOutputDestAddr); // dest addr
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputLocalAddrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputLocalAddrName, &sOutputLocalAddr); // interface addr
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputUDPPortsName, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputUDPPortsName, &sOutputUDPPorts); // udp ports
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputRTSPPortName, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputRTSPPortName, &sOutputRTSPPort); // rtsp port
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputURLName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputURLName, &sOutputURL); // url
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTTLName, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTTLName, &sOutputTTL); // ttl
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputCurPacketsPerSecName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputCurPacketsPerSecName, &sOutputCurPacketsPerSec);// cur packets/sec
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputCurBitsPerSecName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputCurBitsPerSecName, &sOutputCurBitsPerSec); // cur bits/sec
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTotalPacketsSentName, NULL, qtssAttrDataTypeUInt64);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTotalPacketsSentName, &sOutputTotalPacketsSent);// total packets
(void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTotalBytesSentName, NULL, qtssAttrDataTypeUInt64);
(void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTotalBytesSentName, &sOutputTotalBytesSent); // total bytes
}
RelayOutput::RelayOutput(SourceInfo* inInfo, UInt32 inWhichOutput, RelaySession* inRelaySession, Bool16 isRTSPSourceInfo)
: fRelaySession(inRelaySession),
fOutputSocket(NULL, Socket::kNonBlockingSocketType),
fNumStreams(inRelaySession->GetSourceInfo()->GetNumStreams()), // use the reflector session's source info
fOutputInfo(*inInfo->GetOutputInfo(inWhichOutput)),
fQueueElem(),
fFormatter(&fHTMLBuf[0], kMaxHTMLSize),
fPacketsPerSecond(0),
fBitsPerSecond(0),
fLastUpdateTime(0),
fTotalPacketsSent(0),
fTotalBytesSent(0),
fLastPackets(0),
fLastBytes(0),
fClientSocket(NULL),
fClient(NULL),
fDoingAnnounce(false),
fValid(true),
fOutgoingSDP(NULL),
fAnnounceTask(NULL),
fRTSPOutputInfo(NULL)
{
Assert(fNumStreams > 0);
fQueueElem.SetEnclosingObject(this);
fStreamCookieArray = NEW void*[fNumStreams];
fTrackIDArray = NEW UInt32[fNumStreams];
fOutputInfo.fPortArray = NEW UInt16[fNumStreams];//copy constructor doesn't do this
::memset(fOutputInfo.fPortArray, 0, fNumStreams * sizeof(UInt16));
// create a bookmark for each stream we'll reflect
this->InititializeBookmarks( inRelaySession->GetNumStreams() );
// Copy out all the track IDs for each stream
for (UInt32 x = 0; x < fNumStreams; x++)
{
fTrackIDArray[x] = inRelaySession->GetSourceInfo()->GetStreamInfo(x)->fTrackID;
fStreamCookieArray[x] = inRelaySession->GetStreamCookie(fTrackIDArray[x]);
}
// Copy the contents of the output port array
if (inInfo->GetOutputInfo(inWhichOutput)->fPortArray != NULL)
{
UInt32 copySize = fNumStreams;
if (fOutputInfo.fNumPorts < fNumStreams)
copySize = fOutputInfo.fNumPorts;
::memcpy(fOutputInfo.fPortArray, inInfo->GetOutputInfo(inWhichOutput)->fPortArray, copySize * sizeof(UInt16));
}
else if (fOutputInfo.fBasePort != 0)
{
for (UInt32 y = 0; y < fNumStreams; y++)
fOutputInfo.fPortArray[y] = (UInt16) (fOutputInfo.fBasePort + (y * 2) );
}
OS_Error err = BindSocket();
if (err != OS_NoErr)
{
fValid = false;
return;
}
RTSPOutputInfo* rtspInfo = NULL;
if (isRTSPSourceInfo)
{
// in the case of the announce source info, the passed in source info will have the new
// output information, but only the session's source info will have the sdp and url.
RTSPSourceInfo* rtspSourceInfo = (RTSPSourceInfo *)(inInfo);
Assert(rtspSourceInfo != NULL);
RTSPSourceInfo* sessionSourceInfo = (RTSPSourceInfo *)(inRelaySession->GetSourceInfo());
Assert(sessionSourceInfo != NULL);
rtspInfo = rtspSourceInfo->GetRTSPOutputInfo(inWhichOutput);
if (rtspInfo->fIsAnnounced)
{
fRTSPOutputInfo = NEW RTSPOutputInfo();
fRTSPOutputInfo->Copy(*rtspInfo);
fDoingAnnounce = true;
// set up rtsp socket and client
fClientSocket = new TCPClientSocket(Socket::kNonBlockingSocketType);
fClient = new RTSPClient(fClientSocket, false, RelaySession::sRelayUserAgent);
// set up the outgoing socket
fClientSocket->Set(fOutputInfo.fDestAddr, rtspInfo->fAnnouncePort);
int sndBufSize = 32 * 1024;
int rcvBufSize=1024;
fClientSocket->SetOptions(sndBufSize,rcvBufSize);
// set up the client object
StrPtrLen url;
if ((rtspInfo->fDestURl != NULL) && (strlen(rtspInfo->fDestURl) > 0))
url.Set(rtspInfo->fDestURl);
else
url.Set(sessionSourceInfo->GetSourceURL());
fClient->Set(url);
fClient->SetTransportMode(RTSPClient::kPushMode);
fClient->SetName(rtspInfo->fUserName);
fClient->SetPassword(rtspInfo->fPassword);
UInt32 len;
fOutgoingSDP = sessionSourceInfo->GetAnnounceSDP(fOutputInfo.fDestAddr, &len);
fAnnounceState = kSendingAnnounce;
fCurrentSetup = 0;
fAnnounceTask = new RelayAnnouncer(this); // this will now go and run the async announce
fAnnounceTask->Signal(Task::kStartEvent);
}
}
// Write the Output HTML
// Looks like: Relaying to: 229.49.52.102, Ports: 16898 16900 Time to live: 15
static StrPtrLen sHTMLStart("Relaying to: ");
static StrPtrLen sPorts(", Ports: ");
static StrPtrLen sTimeToLive(" Time to live: ");
static StrPtrLen sHTMLEnd("<BR>");
// First, format the destination addr as a dotted decimal string
char theIPAddrBuf[20];
StrPtrLen theIPAddr(theIPAddrBuf, 20);
struct in_addr theAddr;
theAddr.s_addr = htonl(fOutputInfo.fDestAddr);
SocketUtils::ConvertAddrToString(theAddr, &theIPAddr);
// Begin writing the HTML
fFormatter.Put(sHTMLStart);
fFormatter.Put(theIPAddr);
fFormatter.Put(sPorts);
for (UInt32 y = 0; y < fNumStreams; y++)
{
// Write all the destination ports
fFormatter.Put(fOutputInfo.fPortArray[y]);
fFormatter.PutSpace();
}
if (SocketUtils::IsMulticastIPAddr(inInfo->GetOutputInfo(inWhichOutput)->fDestAddr))
{
// Put the time to live if this is a multicast destination
fFormatter.Put(sTimeToLive);
fFormatter.Put(fOutputInfo.fTimeToLive);
}
fFormatter.Put(sHTMLEnd);
// Setup the StrPtrLen to point to the right stuff
fOutputInfoHTML.Ptr = fFormatter.GetBufPtr();
fOutputInfoHTML.Len = fFormatter.GetCurrentOffset();
OSMutexLocker locker(&sQueueMutex);
sRelayOutputQueue.EnQueue(&fQueueElem);
SetupRelayOutputObject(rtspInfo);
}
RelayOutput::~RelayOutput()
{
OSMutexLocker locker(&sQueueMutex);
sRelayOutputQueue.Remove(&fQueueElem);
if (fClientSocket)
delete fClientSocket;
if (fClient)
delete fClient;
delete [] fStreamCookieArray;
delete fOutgoingSDP;
fOutgoingSDP = NULL;
if (fAnnounceTask != NULL)
fAnnounceTask->fOutput = NULL;
if (fRTSPOutputInfo != NULL)
delete fRTSPOutputInfo;
QTSS_Object outputObject;
UInt32 len = sizeof(QTSS_Object);
for (int x = 0; QTSS_GetValue(fRelaySessionObject, RelaySession::sRelayOutputObject, x, &outputObject, &len) == QTSS_NoErr; x++)
{
Assert(outputObject != NULL);
Assert(len == sizeof(QTSS_Object));
if (outputObject == fRelayOutputObject)
{
(void)QTSS_RemoveValue(fRelaySessionObject, RelaySession::sRelayOutputObject, x);
break;
}
}
}
OS_Error RelayOutput::BindSocket()
{
OS_Error theErr = fOutputSocket.Open();
if (theErr != OS_NoErr)
return theErr;
// We don't care what local port we bind to
theErr = fOutputSocket.Bind(fOutputInfo.fLocalAddr, 0);
if (theErr != OS_NoErr)
return theErr;
// Set the ttl to be the proper value
return fOutputSocket.SetTtl(fOutputInfo.fTimeToLive);
}
Bool16 RelayOutput::Equal(SourceInfo* inInfo)
{
// First check if the Source Info matches this RelaySession
if (!fRelaySession->Equal(inInfo))
return false;
for (UInt32 x = 0; x < inInfo->GetNumOutputs(); x++)
{
if (inInfo->GetOutputInfo(x)->Equal(fOutputInfo))
{
RTSPOutputInfo* rtspOutputInfo = NULL;
if (inInfo->IsRTSPSourceInfo())
{
rtspOutputInfo = ((RTSPSourceInfo*)inInfo)->GetRTSPOutputInfo(x);
if (!rtspOutputInfo->fIsAnnounced)
rtspOutputInfo = NULL;
}
if (fRTSPOutputInfo != NULL) // announced output
{
if (!fRTSPOutputInfo->Equal(rtspOutputInfo)) // doesn't match the output
continue;
}
else if (rtspOutputInfo != NULL)
continue;
// This is a rather special purpose function... here we set this
// flag marking this particular output as a duplicate, because
// we know it is equal to this object.
// (This is used in QTSSReflectorModule.cpp:RereadRelayPrefs)
inInfo->GetOutputInfo(x)->fAlreadySetup = true;
return true;
}
}
return false;
}
QTSS_Error RelayOutput::WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 /*packetLatenessInMSec*/, SInt64* /*timeToSendThisPacketAgain*/, UInt64* packetIDPtr, SInt64* /*arrivalTimeMSec*/, Bool16 /*firstPacket */ )
{
if (!fValid || fDoingAnnounce)
return OS_NoErr; // Not done setting up or we had an error setting up
// we don't use packetLateness becuase relays don't need to worry about TCP flow control induced transmit delay
// Look for the matching streamID
for (UInt32 x = 0; x < fNumStreams; x++)
{
if (inStreamCookie == fStreamCookieArray[x])
{
UInt16 theDestPort = fOutputInfo.fPortArray[x];
Assert((theDestPort & 1) == 0); //this should always be an RTP port (even)
if (inFlags & qtssWriteFlagsIsRTCP)
theDestPort++;
(void)fOutputSocket.SendTo(fOutputInfo.fDestAddr, theDestPort,
inPacket->Ptr, inPacket->Len);
// Update our totals
fTotalPacketsSent++;
fTotalBytesSent += inPacket->Len;
break;
}
}
// If it is time to recalculate statistics, do so
SInt64 curTime = OS::Milliseconds();
if ((fLastUpdateTime + kStatsIntervalInMilSecs) < curTime)
{
// Update packets per second
Float64 packetsPerSec = (Float64)((SInt64)fTotalPacketsSent - (SInt64)fLastPackets);
packetsPerSec *= 1000;
packetsPerSec /= (Float64)(curTime - fLastUpdateTime);
fPacketsPerSecond = (UInt32)packetsPerSec;
// Update bits per second. Win32 doesn't implement UInt64 -> Float64.
Float64 bitsPerSec = (Float64)((SInt64)fTotalBytesSent - (SInt64)fLastBytes);
bitsPerSec *= 1000 * 8;//convert from seconds to milsecs, bytes to bits
bitsPerSec /= (Float64)(curTime - fLastUpdateTime);
fBitsPerSecond = (UInt32)bitsPerSec;
fLastUpdateTime = curTime;
fLastPackets = fTotalPacketsSent;
fLastBytes = fTotalBytesSent;
}
return QTSS_NoErr;
}
SInt64 RelayOutput::RelayAnnouncer::Run()
{
OSMutexLocker locker(RelayOutput::GetQueueMutex());
SInt64 result = -1;
if (fOutput != NULL)
result = fOutput->RunAnnounce();
return result;
}
SInt64 RelayOutput::RunAnnounce()
{
OS_Error err = OS_NoErr;
SInt64 result = 1000;
if (fAnnounceState == kSendingAnnounce)
{
if (fOutgoingSDP == NULL || ::strlen(fOutgoingSDP) == 0)
err = ENOTCONN;
else
{
err = fClient->SendAnnounce(fOutgoingSDP);
if (err == OS_NoErr)
{
delete fOutgoingSDP;
fOutgoingSDP = NULL;
if (fClient->GetStatus() == 200)
fAnnounceState = kSendingSetup;
else
err = ENOTCONN;
}
}
}
while ((fAnnounceState == kSendingSetup) && (err == OS_NoErr))
{
err = fClient->SendUDPSetup(fTrackIDArray[fCurrentSetup], 10000);
if (err == OS_NoErr)
{
if (fClient->GetStatus() == 200)
{
fOutputInfo.fPortArray[fCurrentSetup] = fClient->GetServerPort(); // this got set from the Setup reply
fCurrentSetup++;
if (fCurrentSetup == fNumStreams)
fAnnounceState = kSendingPlay;
}
else
err = ENOTCONN;
}
}
if (fAnnounceState == kSendingPlay)
{
err = fClient->SendPlay(0);
if (err == OS_NoErr)
{
if (fClient->GetStatus() == 200)
fAnnounceState = kDone;
else
err = ENOTCONN;
}
}
if (fAnnounceState == kDone)
{
for (UInt32 index = 0; index < fNumStreams; index++) // source udp ports
{
UInt16 udpPort = fOutputInfo.fPortArray[index];
err = QTSS_SetValue (fRelayOutputObject, sOutputUDPPorts, index, &udpPort, sizeof(udpPort));
Assert(err == QTSS_NoErr);
}
fDoingAnnounce = false;
result = -1; // let the task die
fAnnounceTask = NULL;
}
if ((err == EINPROGRESS) || (err == EAGAIN))
{
// Request an async event
fClientSocket->GetSocket()->SetTask(fAnnounceTask);
fClientSocket->GetSocket()->RequestEvent(fClientSocket->GetEventMask() );
}
else if (err != OS_NoErr)
{
// We encountered some fatal error with the socket. Record this as a connection failure
fValid = false;
result = -1; // let the task die
fAnnounceTask = NULL;
}
if ( (-1 == result) && (NULL != fClientSocket) && (NULL != fClientSocket->GetSocket() ) )
fClientSocket->GetSocket()->SetTask(NULL); //remove fAnnounceTask from event handling code. The task can be safely deleted after detaching from the socket.
return result;
}
void RelayOutput::SetupRelayOutputObject(RTSPOutputInfo* inRTSPInfo)
{
fRelaySessionObject = fRelaySession->GetRelaySessionObject();
QTSS_Error theErr = QTSS_NoErr;
UInt32 outIndex = 0;
theErr = QTSS_LockObject (fRelaySessionObject);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_CreateObjectValue (fRelaySessionObject , RelaySession::sRelayOutputObject, qtssRelayOutputObjectType, &outIndex, &fRelayOutputObject);
Assert(theErr == QTSS_NoErr);
if ((inRTSPInfo == NULL) || !inRTSPInfo->fIsAnnounced) // output type
{
theErr = QTSS_SetValue (fRelayOutputObject, sOutputType, 0, (void*)sUDPDestStr.Ptr, sUDPDestStr.Len);
Assert(theErr == QTSS_NoErr);
}
else
{
theErr = QTSS_SetValue (fRelayOutputObject, sOutputType, 0, (void*)sAnnouncedDestStr.Ptr, sAnnouncedDestStr.Len); // output type
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue (fRelayOutputObject, sOutputRTSPPort, 0, &inRTSPInfo->fAnnouncePort, sizeof(inRTSPInfo->fAnnouncePort)); // rtsp port
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue (fRelayOutputObject, sOutputURL, 0, (fClient->GetURL())->Ptr, (fClient->GetURL())->Len); // output url
Assert(theErr == QTSS_NoErr);
}
char theIPAddrBuf[20];
StrPtrLen theIPAddr(theIPAddrBuf, 20);
struct in_addr theDestAddr; // output destination address
theDestAddr.s_addr = htonl(fOutputInfo.fDestAddr);
SocketUtils::ConvertAddrToString(theDestAddr, &theIPAddr);
theErr = QTSS_SetValue (fRelayOutputObject, sOutputDestAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
Assert(theErr == QTSS_NoErr);
struct in_addr theLocalAddr; // output local address
theLocalAddr.s_addr = htonl(fOutputInfo.fLocalAddr);
SocketUtils::ConvertAddrToString(theLocalAddr, &theIPAddr);
theErr = QTSS_SetValue (fRelayOutputObject, sOutputLocalAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
Assert(theErr == QTSS_NoErr);
for (UInt32 index = 0; index < fNumStreams; index++) // output udp ports
{
UInt16 udpPort = fOutputInfo.fPortArray[index];
theErr = QTSS_SetValue (fRelayOutputObject, sOutputUDPPorts, index, &udpPort, sizeof(udpPort));
Assert(theErr == QTSS_NoErr);
}
theErr = QTSS_SetValue (fRelayOutputObject, sOutputTTL, 0, &(fOutputInfo.fTimeToLive), sizeof(fOutputInfo.fTimeToLive));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputCurPacketsPerSec, &fPacketsPerSecond, sizeof(fPacketsPerSecond));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputCurBitsPerSec, &fBitsPerSecond, sizeof(fBitsPerSecond));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputTotalPacketsSent, &fTotalPacketsSent, sizeof(fTotalPacketsSent));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputTotalBytesSent, &fTotalBytesSent, sizeof(fTotalBytesSent));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_UnlockObject (fRelaySessionObject);
Assert(theErr == QTSS_NoErr);
}

View file

@ -0,0 +1,182 @@
/*
*
* @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: RelayOutput.h
Contains: An implementation of the ReflectorOutput abstract base class,
that just writes data out to UDP sockets.
*/
#ifndef __RELAY_OUTPUT_H__
#define __RELAY_OUTPUT_H__
#include "ReflectorOutput.h"
#include "RelaySession.h"
#include "SourceInfo.h"
#include "RTSPClient.h"
#include "ClientSocket.h"
#include "OSQueue.h"
#include "OSMutex.h"
class RelayAnnouncer;
class RelayOutput : public ReflectorOutput
{
public:
// Call Register in the Relay Module's Register Role
static void Register();
RelayOutput(SourceInfo* inInfo, UInt32 inWhichOutput, RelaySession* inSession, Bool16 isRTSPSourceInfo);
virtual ~RelayOutput();
// Returns true if this output matches one of the Outputs in the SourceInfo.
// Also marks the proper SourceInfo::OutputInfo "fAlreadySetup" flag as true
Bool16 Equal(SourceInfo* inInfo);
// Call this to setup this object's output socket
OS_Error BindSocket();
// Writes the packet directly to a UDP socket
virtual QTSS_Error WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTime, Bool16 firstPacket);
virtual Bool16 IsUDP() { return true; }
virtual Bool16 IsPlaying() {if (!fValid || fDoingAnnounce) return false; return true; }
// ACCESSORS
RelaySession* GetRelaySession() { return fRelaySession; }
StrPtrLen* GetOutputInfoHTML() { return &fOutputInfoHTML; }
UInt32 GetCurPacketsPerSecond() { return fPacketsPerSecond; }
UInt32 GetCurBitsPerSecond() { return fBitsPerSecond; }
UInt64& GetTotalPacketsSent() { return fTotalPacketsSent; }
UInt64& GetTotalBytesSent() { return fTotalBytesSent; }
Bool16 IsValid() { return fValid; }
// Use these functions to iterate over all RelayOutputs
static OSMutex* GetQueueMutex() { return &sQueueMutex; }
static OSQueue* GetOutputQueue(){ return &sRelayOutputQueue; }
void TearDown() {};
SInt64 RunAnnounce();
private:
void SetupRelayOutputObject(RTSPOutputInfo* inRTSPInfo);
class RelayAnnouncer : public Task
{
public:
RelayAnnouncer(RelayOutput* output) : fOutput(output) {this->SetTaskName("RelayAnnouncer");}
virtual SInt64 Run();
RelayOutput* fOutput;
};
enum
{
kMaxHTMLSize = 255, // Note, this may be too short and we don't protect!
kStatsIntervalInMilSecs = 10000 // Update "current" statistics every 10 seconds
};
RelaySession* fRelaySession;
// Relay streams all share this one socket for writing.
UDPSocket fOutputSocket;
UInt32 fNumStreams;
SourceInfo::OutputInfo fOutputInfo;
void** fStreamCookieArray;//Each stream has a cookie
UInt32* fTrackIDArray;
OSQueueElem fQueueElem;
char fHTMLBuf[kMaxHTMLSize];
StrPtrLen fOutputInfoHTML;
ResizeableStringFormatter fFormatter;
// Statistics
UInt32 fPacketsPerSecond;
UInt32 fBitsPerSecond;
SInt64 fLastUpdateTime;
UInt64 fTotalPacketsSent;
UInt64 fTotalBytesSent;
UInt64 fLastPackets;
UInt64 fLastBytes;
TCPClientSocket* fClientSocket;
RTSPClient* fClient;
Bool16 fDoingAnnounce;
Bool16 fValid;
char* fOutgoingSDP;
RelayAnnouncer* fAnnounceTask;
RTSPOutputInfo* fRTSPOutputInfo;
enum // anounce states
{
kSendingAnnounce = 0,
kSendingSetup = 1,
kSendingPlay = 2,
kDone = 3
};
UInt32 fAnnounceState;
UInt32 fCurrentSetup;
// Queue of all current RelayReflectorOutput objects, for use in the
static OSQueue sRelayOutputQueue;
static OSMutex sQueueMutex;
QTSS_Object fRelaySessionObject;
QTSS_Object fRelayOutputObject;
// attributes of the qtssRelayOutputObjectType
static QTSS_ObjectType qtssRelayOutputObjectType;
static QTSS_AttributeID sOutputType;
static QTSS_AttributeID sOutputDestAddr;
static QTSS_AttributeID sOutputLocalAddr;
static QTSS_AttributeID sOutputUDPPorts;
static QTSS_AttributeID sOutputRTSPPort;
static QTSS_AttributeID sOutputURL;
static QTSS_AttributeID sOutputTTL;
static QTSS_AttributeID sOutputCurPacketsPerSec;
static QTSS_AttributeID sOutputCurBitsPerSec;
static QTSS_AttributeID sOutputTotalPacketsSent;
static QTSS_AttributeID sOutputTotalBytesSent;
};
#endif //__RELAY_OUTPUT_H__

View file

@ -0,0 +1,202 @@
/*
*
* @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: RelaySDPSourceInfo.cpp
Contains: Implementation of object defined in .h file
*/
#include "RelaySDPSourceInfo.h"
#include "SDPSourceInfo.h"
#include "MyAssert.h"
#include "StringParser.h"
#include "OSMemory.h"
#ifndef __Win32__
#include <netinet/in.h>
#endif
RelaySDPSourceInfo::~RelaySDPSourceInfo()
{
// Not necessary anymore as the destructor of the base class will take care
// of deleting all allocated memory for fOutputArray and fStreamArray
/*
if (fOutputArray != NULL)
{
for (UInt32 x = 0; x < fNumOutputs; x++)
{
delete [] fOutputArray[x].fPortArray;
fOutputArray[x].fNumPorts = 0;
}
char* theOutputArray = (char*)fOutputArray;
delete [] theOutputArray;
}
if (fStreamArray != NULL)
{
char* theStreamArray = (char*)fStreamArray;
delete [] theStreamArray;
}
*/
}
void RelaySDPSourceInfo::Parse(StrPtrLen* inSDPData)
{
// These are the lines of the SDP file that we are interested in
static StrPtrLen sRelayAddr("a=x-qt-relay-addr");
static StrPtrLen sRelayPort("a=x-qt-relay-port");
Assert(fOutputArray == NULL);
Assert(fStreamArray == NULL);
StrPtrLen sdpLine;
StrPtrLen outputAddrs;
StringParser trackCounter(inSDPData);
UInt32 theDestIPAddr = 0;
UInt16 theDestTtl = 0;
//
// FIRST WALK THROUGH SDP
// The first walk is to count up the number of StreamInfo & OutputInfo
// objects that we will need.
while (true)
{
// grab a line
trackCounter.ConsumeUntil(&sdpLine, StringParser::sEOLMask);
if (sdpLine.NumEqualIgnoreCase(sRelayAddr.Ptr, sRelayAddr.Len))
{
// there is a x-qt-relay-addr line, look for all IP addrs
StringParser relayAddrParser(&sdpLine);
relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask);
// The first IP addr on this line is the destination IP addr of the
// source broadcast.
theDestIPAddr = SDPSourceInfo::GetIPAddr(&relayAddrParser, ' ');
relayAddrParser.ConsumeWhitespace();
// Store this position so we can later go back to it
outputAddrs.Ptr = relayAddrParser.GetCurrentPosition();
outputAddrs.Len = relayAddrParser.GetDataRemaining();
StrPtrLen theTtl;
while (relayAddrParser.GetDataRemaining() > 0)
{
relayAddrParser.ConsumeUntil(&theTtl, ' ');
relayAddrParser.ConsumeWhitespace();
fNumOutputs++;
}
fNumOutputs--;// Don't count the ttl as an output!
StringParser ttlParser(&theTtl);
theDestTtl = (UInt16) ttlParser.ConsumeInteger(NULL);
}
// Each x=qt-relay-port line corresponds to one source stream.
else if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len))
fNumStreams++;
//stop when we reach an empty line.
if (!trackCounter.ExpectEOL())
break;
}
// No relay info in this file!
if ((fNumStreams == 0) || (fNumOutputs == 0))
return;
// x-qt-relay-port lines should always be in pairs (RTP & RTCP)
if ((fNumStreams & 1) != 0)
return;
fNumStreams /= 2;
//
// CONSTRUCT fStreamInfo AND fOutputInfo ARRAYS
fStreamArray = NEW StreamInfo[fNumStreams];
fOutputArray = NEW OutputInfo[fNumOutputs];
//
// FILL IN ARRAYS
// Filling in the output addresses is easy because the outputAddrs
// StrPtrLen points right at the data we want
StringParser theOutputAddrParser(&outputAddrs);
for (UInt32 x = 0; x < fNumOutputs; x++)
{
fOutputArray[x].fDestAddr = SDPSourceInfo::GetIPAddr(&theOutputAddrParser, ' ');
fOutputArray[x].fLocalAddr = INADDR_ANY;
fOutputArray[x].fTimeToLive = theDestTtl;
fOutputArray[x].fPortArray = NEW UInt16[fNumStreams];//Each output has one port per stream
fOutputArray[x].fNumPorts = fNumStreams;
::memset(fOutputArray[x].fPortArray, 0, fNumStreams * sizeof(UInt16));
fOutputArray[x].fAlreadySetup = false;
theOutputAddrParser.ConsumeWhitespace();
Assert(fOutputArray[x].fDestAddr > 0);
}
StringParser sdpParser(inSDPData);
// Now go through and find all the port information on all the x-qt-relay-port lines
for (UInt32 theStreamIndex = 0; theStreamIndex < fNumStreams; )
{
sdpParser.ConsumeUntil(&sdpLine, StringParser::sEOLMask);
// parse through all the x-qt-relay-port lines
if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len))
{
// Begin parsing... find the first port on the line
StringParser relayAddrParser(&sdpLine);
relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask);
// The first port is the source port for this stream
fStreamArray[theStreamIndex].fPort = (UInt16) relayAddrParser.ConsumeInteger(NULL);
if (fStreamArray[theStreamIndex].fPort & 1)
continue; //we only care about RTP ports
// Fill in all the fields we can for this stream
fStreamArray[theStreamIndex].fDestIPAddr = theDestIPAddr;
fStreamArray[theStreamIndex].fTimeToLive = theDestTtl;
fStreamArray[theStreamIndex].fTrackID = theStreamIndex + 1;
// Now fill in all the output ports for this stream
for (UInt32 x = 0; x < fNumOutputs; x++)
{
relayAddrParser.ConsumeWhitespace();
fOutputArray[x].fPortArray[theStreamIndex] = (UInt16) relayAddrParser.ConsumeInteger(NULL);
}
theStreamIndex++;
}
//stop when we reach an empty line.
if (!sdpParser.ExpectEOL())
break;
}
}

View file

@ -0,0 +1,55 @@
/*
*
* @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: RelaySDPSourceInfo.h
Contains: This object takes input SDP data, and uses it to support the SourceInfo
API. It looks for the x-qt-relay lines put in by some broadcasters,
and uses that information to construct OutputInfo objects.
*/
#ifndef __RELAY_SDP_SOURCE_INFO_H__
#define __RELAY_SDP_SOURCE_INFO_H__
#include "StrPtrLen.h"
#include "SourceInfo.h"
class RelaySDPSourceInfo : public SourceInfo
{
public:
// Reads in the SDP data from this file, and builds up the SourceInfo structures
RelaySDPSourceInfo(StrPtrLen* inSDPData) { Parse(inSDPData); }
virtual ~RelaySDPSourceInfo();
private:
void Parse(StrPtrLen* inSDPData);
};
#endif // __SDP_SOURCE_INFO_H__

View file

@ -0,0 +1,269 @@
/*
*
* @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: RelaySession.cpp
Contains: Implementation of object defined in RelaySession.h.
*/
#include "RelaySession.h"
#include "QTSSModuleUtils.h"
#include "SocketUtils.h"
#include "../../revision.h"
static StrPtrLen sUDPSourceStr("udp_source");
static StrPtrLen sRTSPSourceStr("rtsp_source");
static StrPtrLen sAnnouncedSourceStr("announced_source");
static StrPtrLen sEmptyStr("");
static char* sRelaySessionObjectName = "relay_session";
static char* sRelayNameName = "relay_name";
static char* sSourceTypeName = "source_type";
static char* sSourceIPAddrName = "source_ip_addr";
static char* sSourceInIPAddrName = "source_in_ip_addr";
static char* sSourceUDPPortsName = "source_udp_ports";
static char* sSourceRTSPPortName = "source_rtsp_port";
static char* sSourceURLName = "source_url";
static char* sSourceUsernameName = "source_username";
static char* sSourcePasswordName = "source_password";
static char* sSourceTTLName = "source_ttl";
static char* sRelayOutputObjectName = "relay_output";
QTSS_Object RelaySession::relayModuleAttributesObject;
QTSS_ObjectType RelaySession::qtssRelaySessionObjectType;
QTSS_AttributeID RelaySession::sRelaySessionObjectID = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sRelayName = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceType = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceIPAddr = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceInIPAddr = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceUDPPorts = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceRTSPPort = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceURL = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceUsername = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourcePassword = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sSourceTTL = qtssIllegalAttrID;
QTSS_AttributeID RelaySession::sRelayOutputObject = qtssIllegalAttrID;
char RelaySession::sRelayUserAgent[20] = "";
void RelaySession::Register()
{
qtssRelaySessionObjectType = 0;
// create relay session object type
(void)QTSS_CreateObjectType(&qtssRelaySessionObjectType);
// Add the static attributes to the qtssRelaySessionObjectType object
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sRelayNameName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sRelayNameName, &sRelayName); // relay name
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceTypeName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceTypeName, &sSourceType); // source type
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceIPAddrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceIPAddrName, &sSourceIPAddr); // source addr
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceInIPAddrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceInIPAddrName, &sSourceInIPAddr); // interface addr
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceUDPPortsName, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceUDPPortsName, &sSourceUDPPorts); // udp ports
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceRTSPPortName, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceRTSPPortName, &sSourceRTSPPort); // rtsp port
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceURLName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceURLName, &sSourceURL); // url
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceUsernameName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceUsernameName, &sSourceUsername); // username
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourcePasswordName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourcePasswordName, &sSourcePassword); // password
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceTTLName, NULL, qtssAttrDataTypeUInt16);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceTTLName, &sSourceTTL); // ttl
(void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sRelayOutputObjectName, NULL, qtssAttrDataTypeQTSS_Object);
(void)QTSS_IDForAttr(qtssRelaySessionObjectType, sRelayOutputObjectName, &sRelayOutputObject); // relay output
//char* strEnd = NULL;
char* relayStr = "QTSS_Relay/";
//kVersionString is changed now -- it doesn't contain any spaces or the build number
//strEnd = strchr(kVersionString, ' ');
//Assert(strEnd != NULL);
#ifndef __Win32__
//qtss_snprintf(sRelayUserAgent, ::strlen(relayStr) + (strEnd - kVersionString) + 1, "%s/%s", relayStr, kVersionString);
qtss_snprintf(sRelayUserAgent, ::strlen(relayStr) + ::strlen(kVersionString) + 1, "%s%s", relayStr, kVersionString);
#else
//_snprintf(sRelayUserAgent, ::strlen(relayStr) + (strEnd - kVersionString) + 1, "%s/%s", relayStr, kVersionString);
_snprintf(sRelayUserAgent, ::strlen(relayStr) + ::strlen(kVersionString) + 1, "%s%s", relayStr, kVersionString);
#endif
}
void RelaySession::Initialize(QTSS_Object inRelayModuleAttributesObject)
{
ReflectorSession::Initialize();
if (inRelayModuleAttributesObject != NULL)
{
relayModuleAttributesObject = inRelayModuleAttributesObject;
sRelaySessionObjectID = QTSSModuleUtils::CreateAttribute(inRelayModuleAttributesObject, sRelaySessionObjectName, qtssAttrDataTypeQTSS_Object, NULL, 0);
}
}
QTSS_Error RelaySession::SetupRelaySession(SourceInfo* inInfo)
{
QTSS_Error theErr = QTSS_NoErr;
theErr = this->SetupReflectorSession(inInfo, NULL);
if (theErr != QTSS_NoErr)
return theErr;
// create the reflector session object for this session
UInt32 outIndex = 0;
fRelaySessionObject = NULL;
theErr = QTSS_LockObject(relayModuleAttributesObject);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_CreateObjectValue (relayModuleAttributesObject , sRelaySessionObjectID, qtssRelaySessionObjectType, &outIndex, &fRelaySessionObject);
Assert(theErr == QTSS_NoErr);
if (fRelaySessionObject == NULL)
return theErr;
// set the values for all the static attributes in this session
char* relayName = inInfo->Name(); // name of the relay
if (relayName != NULL)
theErr = QTSS_SetValue (fRelaySessionObject, sRelayName, 0, (void*)relayName, ::strlen(relayName));
else
theErr = QTSS_SetValue (fRelaySessionObject, sRelayName, 0, (void*)sEmptyStr.Ptr, sEmptyStr.Len);
Assert(theErr == QTSS_NoErr);
StrPtrLen sourceStr; // type of source
if (inInfo->IsRTSPSourceInfo())
{
if (((RTSPSourceInfo*)inInfo)->IsAnnounce())
sourceStr.Set(sAnnouncedSourceStr.Ptr, sAnnouncedSourceStr.Len);
else
sourceStr.Set(sRTSPSourceStr.Ptr, sRTSPSourceStr.Len);
}
else
sourceStr.Set(sUDPSourceStr.Ptr, sUDPSourceStr.Len);
theErr = QTSS_SetValue (fRelaySessionObject, sSourceType, 0, (void*)sourceStr.Ptr, sourceStr.Len);
Assert(theErr == QTSS_NoErr);
char theIPAddrBuf[20];
StrPtrLen theIPAddr(theIPAddrBuf, 20);
struct in_addr theSrcAddr; // source ip address
theSrcAddr.s_addr = htonl(inInfo->GetStreamInfo(0)->fSrcIPAddr);
SocketUtils::ConvertAddrToString(theSrcAddr, &theIPAddr);
theErr = QTSS_SetValue (fRelaySessionObject, sSourceIPAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
Assert(theErr == QTSS_NoErr);
struct in_addr theDestAddr; // dest (of source) ip address
theDestAddr.s_addr = htonl(inInfo->GetStreamInfo(0)->fDestIPAddr);
SocketUtils::ConvertAddrToString(theDestAddr, &theIPAddr);
theErr = QTSS_SetValue (fRelaySessionObject, sSourceInIPAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
Assert(theErr == QTSS_NoErr);
for (UInt32 index = 0; index < (inInfo->GetNumStreams()); index++) // source udp ports
{
UInt16 udpPort = inInfo->GetStreamInfo(index)->fPort;
theErr = QTSS_SetValue (fRelaySessionObject, sSourceUDPPorts, index, &udpPort, sizeof(udpPort));
Assert(theErr == QTSS_NoErr);
}
if (inInfo->IsRTSPSourceInfo())
{
RTSPSourceInfo* rtspInfo = (RTSPSourceInfo*)inInfo;
if (!rtspInfo->IsAnnounce())
{
UInt16 rtspPort = (UInt16) rtspInfo->GetHostPort();
char* username = rtspInfo->GetUsername();
char* password = rtspInfo->GetPassword();
theErr = QTSS_SetValue (fRelaySessionObject, sSourceRTSPPort, 0, &rtspPort, sizeof(rtspPort)); // source rtsp port
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue (fRelaySessionObject, sSourceUsername, 0, username, sizeof(username)); // source username
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue (fRelaySessionObject, sSourcePassword, 0, password, sizeof(password)); // source password
Assert(theErr == QTSS_NoErr);
}
char* url = rtspInfo->GetSourceURL();
theErr = QTSS_SetValue (fRelaySessionObject, sSourceURL, 0, url, ::strlen(url));
Assert(theErr == QTSS_NoErr); // source url
}
UInt16 ttl = inInfo->GetStreamInfo(0)->fTimeToLive;
theErr = QTSS_SetValue (fRelaySessionObject, sSourceTTL, 0, &ttl, sizeof(ttl)); // source ttl
Assert(theErr == QTSS_NoErr);
theErr = QTSS_UnlockObject(relayModuleAttributesObject);
Assert(theErr == QTSS_NoErr);
return QTSS_NoErr;
}
RelaySession::~RelaySession()
{
QTSS_Object sessionObject;
UInt32 len = sizeof(QTSS_Object);
for (int x = 0; QTSS_GetValue(relayModuleAttributesObject, sRelaySessionObjectID, x, &sessionObject, &len) == QTSS_NoErr; x++)
{
Assert(sessionObject != NULL);
Assert(len == sizeof(QTSS_Object));
if (sessionObject == fRelaySessionObject)
{
(void)QTSS_RemoveValue(relayModuleAttributesObject, sRelaySessionObjectID, x);
break;
}
}
}

View file

@ -0,0 +1,89 @@
/*
*
* @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: RelaySession.h
Contains: Subclass of ReflectorSession. It has two static
attributes (QTSSRelayModule Attributes object
and the ReflectorSession attribute ID)
*/
#include "QTSS.h"
#include "ReflectorSession.h"
#include "StrPtrLen.h"
#include "SourceInfo.h"
#include "RTSPSourceInfo.h"
#ifndef _RELAY_SESSION_
#define _RELAY_SESSION_
class RelaySession : public ReflectorSession
{
public:
// Call Register in the Relay Module's Register Role
static void Register();
//
// Initialize
// Call Initialize in the Relay Module's Initialize Role
static void Initialize(QTSS_Object inAttrObject);
RelaySession(StrPtrLen* inSourceID, SourceInfo* inInfo = NULL):ReflectorSession(inSourceID, inInfo){};
~RelaySession();
QTSS_Error SetupRelaySession(SourceInfo* inInfo);
QTSS_Object GetRelaySessionObject() { return fRelaySessionObject; }
static QTSS_AttributeID sRelayOutputObject;
static char sRelayUserAgent[20];
private:
QTSS_Object fRelaySessionObject;
// gets set in the initialize method
static QTSS_Object relayModuleAttributesObject;
static QTSS_ObjectType qtssRelaySessionObjectType;
static QTSS_AttributeID sRelaySessionObjectID;
static QTSS_AttributeID sRelayName;
static QTSS_AttributeID sSourceType;
static QTSS_AttributeID sSourceIPAddr;
static QTSS_AttributeID sSourceInIPAddr;
static QTSS_AttributeID sSourceUDPPorts;
static QTSS_AttributeID sSourceRTSPPort;
static QTSS_AttributeID sSourceURL;
static QTSS_AttributeID sSourceUsername;
static QTSS_AttributeID sSourcePassword;
static QTSS_AttributeID sSourceTTL;
};
#endif

View file

@ -0,0 +1,153 @@
/*
*
* @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: SequenceNumberMap.cpp
Contains: Implements object defined in SequenceNumberMap.h.
*/
#include <string.h>
#include "MyAssert.h"
#include "OSMemory.h"
#include "SequenceNumberMap.h"
SequenceNumberMap::SequenceNumberMap(UInt32 inSlidingWindowSize)
: fSlidingWindow(NULL),
fWindowSize( (SInt32) inSlidingWindowSize),
fNegativeWindowSize( (SInt32) inSlidingWindowSize - (SInt32) (2 * inSlidingWindowSize)),
fHighestSeqIndex(0),
fHighestSeqNumber(0)
{
Assert(fNegativeWindowSize < 0);
Assert(fWindowSize < 32768);//AddSequenceNumber makes this assumption
}
Bool16 SequenceNumberMap::AddSequenceNumber(UInt16 inSeqNumber)
{
// Returns whether sequence number has already been added.
//Check to see if object has been initialized
if (fSlidingWindow == NULL)
{
fSlidingWindow = NEW Bool16[fWindowSize + 1];
::memset(fSlidingWindow, 0, fWindowSize * sizeof(Bool16));
fHighestSeqIndex = 0;
fHighestSeqNumber = inSeqNumber;
}
// First check to see if this sequence number is so far below the highest sequence number
// we can't even put it in the sliding window.
SInt16 theWindowOffset = inSeqNumber - fHighestSeqNumber;
if (theWindowOffset < fNegativeWindowSize)
return false;//We don't know, but for safety, assume we haven't seen it.
// If this seq # is higher thn the highest previous, set the highest to be this
// new sequence number, and zero out our sliding window as we go.
while (theWindowOffset > 0)
{
fHighestSeqNumber++;
fHighestSeqIndex++;
if (fHighestSeqIndex == fWindowSize)
fHighestSeqIndex = 0;
fSlidingWindow[fHighestSeqIndex] = false;
theWindowOffset--;
}
// Find the right entry in the sliding window for this sequence number, taking
// into account that we may need to wrap.
SInt32 theWindowIndex = fHighestSeqIndex + theWindowOffset;
if (theWindowIndex < 0)
theWindowIndex += fWindowSize;
Assert(theWindowIndex >= 0);
Assert(theWindowIndex < fWindowSize);
// Turn this index on, return whether it was already turned on.
Bool16 alreadyAdded = fSlidingWindow[theWindowIndex];
fSlidingWindow[theWindowIndex] = true;
#if SEQUENCENUMBERMAPTESTING
//if (alreadyAdded)
// qtss_printf("Found a duplicate seq num. Num = %d\n", inSeqNumber);
#endif
return alreadyAdded;
}
#if SEQUENCENUMBERMAPTESTING
void SequenceNumberMap::Test()
{
SequenceNumberMap theMap1;
Bool16 retval = theMap1.AddSequenceNumber(64674);
Assert(retval == false);
retval = theMap1.AddSequenceNumber(64582);
Assert(retval == false);
retval = theMap1.AddSequenceNumber(64777);
Assert(retval == false);
retval = theMap1.AddSequenceNumber(64582);
Assert(retval == TRUE);
retval = theMap1.AddSequenceNumber(64674);
Assert(retval == TRUE);
retval = theMap1.AddSequenceNumber(1);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(65500);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(65500);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(32768);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(1024);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(32757);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(32799);
Assert(retval == FALSE);
retval = theMap1.AddSequenceNumber(32768);
Assert(retval == FALSE);
}
#endif

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: SequenceNumberMap.h
Contains: Data structure for keeping track of duplicate sequence numbers.
Useful for removing duplicate packets from an RTP stream.
*/
#ifndef _SEQUENCE_NUMBER_MAP_H_
#define _SEQUENCE_NUMBER_MAP_H_
#include "OSHeaders.h"
#define SEQUENCENUMBERMAPTESTING 1
class SequenceNumberMap
{
public:
enum
{
kDefaultSlidingWindowSize = 256
};
SequenceNumberMap(UInt32 inSlidingWindowSize = kDefaultSlidingWindowSize);
~SequenceNumberMap() { delete [] fSlidingWindow; }
// Returns whether this sequence number was already added or not.
Bool16 AddSequenceNumber(UInt16 inSeqNumber);
#if SEQUENCENUMBERMAPTESTING
static void Test();
#endif
private:
Bool16* fSlidingWindow;
const SInt32 fWindowSize;
const SInt32 fNegativeWindowSize;
UInt16 fHighestSeqIndex;
UInt16 fHighestSeqNumber;
};
#endif

View file

@ -0,0 +1,284 @@
/*
*
* @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: QTSSSpamDefanseModule.cpp
Contains: Implementation of module described in .h file
*/
#include "QTSSSpamDefenseModule.h"
#include "OSHashTable.h"
#include "OSMutex.h"
#include "QTSSModuleUtils.h"
#include "OSMemory.h"
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_StreamRef sErrorLogStream = NULL;
class IPAddrTableKey;
class IPAddrTableElem
{
public:
IPAddrTableElem(UInt32 inIPAddr) : fIPAddr(inIPAddr), fRefCount(0), fNextHashEntry(NULL) {}
~IPAddrTableElem() {}
UInt32 GetRefCount() { return fRefCount; }
void IncrementRefCount() { fRefCount++; }
void DecrementRefCount() { fRefCount--; }
private:
UInt32 fIPAddr;// this also serves as the hash value
UInt32 fRefCount;
IPAddrTableElem* fNextHashEntry;
friend class IPAddrTableKey;
friend class OSHashTable<IPAddrTableElem, IPAddrTableKey>;
};
class IPAddrTableKey
{
public:
//CONSTRUCTOR / DESTRUCTOR:
IPAddrTableKey(UInt32 inIPAddr) : fIPAddr(inIPAddr) {}
~IPAddrTableKey() {}
private:
//PRIVATE ACCESSORS:
SInt32 GetHashKey() { return fIPAddr; }
//these functions are only used by the hash table itself. This constructor
//will break the "Set" functions.
IPAddrTableKey(IPAddrTableElem *elem) : fIPAddr(elem->fIPAddr) {}
friend int operator ==(const IPAddrTableKey &key1, const IPAddrTableKey &key2)
{
return (key1.fIPAddr == key2.fIPAddr);
}
//data:
UInt32 fIPAddr;
friend class OSHashTable<IPAddrTableElem, IPAddrTableKey>;
};
typedef OSHashTable<IPAddrTableElem, IPAddrTableKey> IPAddrHashTable;
// STATIC DATA
static IPAddrHashTable* sHashTable = NULL;
static OSMutex* sMutex;
static UInt32 sNumConnsPerIP = 0;
static UInt32 sDefaultNumConnsPerIP = 100;
// ATTRIBUTES
static QTSS_AttributeID sIsFirstRequestAttr = qtssIllegalAttrID;
static QTSS_AttributeID sTooManyConnectionsErr = qtssIllegalAttrID;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSSpamDefenseModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error RereadPrefs();
static QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error SessionClosing(QTSS_RTSPSession_Params* inParams);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSSpamDefenseModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSSpamDefenseModuleDispatch);
}
QTSS_Error QTSSSpamDefenseModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPAuthorize_Role:
return Authorize(&inParams->rtspAuthParams);
case QTSS_RTSPSessionClosing_Role:
return SessionClosing(&inParams->rtspSessionClosingParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// The spam defense module has one preference, the number of connections
// to allow per ip addr
static char* sIsFirstRequestName = "QTSSSpamDefenseModuleIsFirstRequest";
// Add text messages attributes
static char* sTooManyConnectionsName = "QTSSSpamDefenseModuleTooManyConnections";
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
(void)QTSS_AddRole(QTSS_RTSPSessionClosing_Role);
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sIsFirstRequestName, NULL, qtssAttrDataTypeBool16);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sIsFirstRequestName, &sIsFirstRequestAttr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sTooManyConnectionsName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sTooManyConnectionsName, &sTooManyConnectionsErr);
// Tell the server our name!
static char* sModuleName = "QTSSSpamDefenseModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sErrorLogStream = inParams->inErrorLogStream;
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
sMutex = NEW OSMutex();
sHashTable = NEW IPAddrHashTable(277);//277 is prime, I think...
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
QTSSModuleUtils::GetAttribute(sPrefs, "num_conns_per_ip_addr", qtssAttrDataTypeUInt32,
&sNumConnsPerIP, &sDefaultNumConnsPerIP, sizeof(sNumConnsPerIP));
return QTSS_NoErr;
}
QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams)
{
static Bool16 sTrue = true;
Bool16* isFirstRequest = NULL;
UInt32* theIPAddr = NULL;
UInt32 theLen = 0;
// Only do anything if this is the first request
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sIsFirstRequestAttr, 0, (void**)&isFirstRequest, &theLen);
if (isFirstRequest != NULL)
return QTSS_NoErr;
// Get the IP address of this client.
(void)QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIPAddr, &theLen);
if ((theIPAddr == NULL) || (theLen != sizeof(UInt32)))
{
return QTSS_NoErr;
}
IPAddrTableKey theKey(*theIPAddr);
// This must be atomic
OSMutexLocker locker(sMutex);
// Check to see if this client currently has a connection open.
IPAddrTableElem* theElem = sHashTable->Map(&theKey);
if (theElem == NULL)
{
// Client doesn't have a connetion open currently. Create a map element,
// and add it into the map.
theElem = NEW IPAddrTableElem(*theIPAddr);
sHashTable->Add(theElem);
}
// Check to see if this client has too many connections open. If it does,
// return an error, otherwise, allow the connection and increment the
// refcount.
if (theElem->GetRefCount() >= sNumConnsPerIP) {
QTSSModuleUtils::LogErrorStr(qtssMessageVerbosity, "Blocking connection from IP address");
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientForbidden,
sTooManyConnectionsErr);
} else
theElem->IncrementRefCount();
// Mark the request so we'll know subsequent ones aren't the first.
// Note that we only do this if we've successfully added this client to our map.
// That way, we only remove it in SessionClosing if we've added it.
(void)QTSS_SetValue(inParams->inRTSPSession, sIsFirstRequestAttr, 0, &sTrue, sizeof(sTrue));
return QTSS_NoErr;
}
QTSS_Error SessionClosing(QTSS_RTSPSession_Params* inParams)
{
UInt32* theIPAddr = NULL;
Bool16* isFirstRequest = NULL;
UInt32 theLen = 0;
// Only remove this session from the map if it has been added in the first place
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sIsFirstRequestAttr, 0, (void**)&isFirstRequest, &theLen);
if (isFirstRequest == NULL)
return QTSS_NoErr;
// Get the IP address of this client.
(void)QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIPAddr, &theLen);
if ((theIPAddr == NULL) || (theLen != sizeof(UInt32)))
{
return QTSS_NoErr;
}
IPAddrTableKey theKey(*theIPAddr);
// This must be atomic
OSMutexLocker locker(sMutex);
// Check to see if this client currently has a connection open.
IPAddrTableElem* theElem = sHashTable->Map(&theKey);
if (theElem == NULL)
return QTSS_NoErr; //this may happen if there is another module denying connections
// Decrement the refcount
if (theElem->GetRefCount() > 0)
theElem->DecrementRefCount();
// If the refcount is 0, remove this from the map, and delete it.
if (theElem->GetRefCount() == 0)
{
sHashTable->Remove(theElem);
delete theElem;
}
return QTSS_NoErr;
}

View file

@ -0,0 +1,49 @@
/*
*
* @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: QTSSSpamDefanseModule.h
Contains: Protects the server against denial-of-service attacks by
only allowing X number of RTSP connections from a certain
IP address
*/
#ifndef __QTSSSPAMDEFENSEMODULE_H__
#define __QTSSSPAMDEFENSEMODULE_H__
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSSpamDefenseModule_Main(void* inPrivateArgs);
}
#endif // __QTSSSPAMDEFENSEMODULE_H__

View file

@ -0,0 +1,148 @@
/*
*
* @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: QTSSWebDebugModule.cpp
Contains: Implements web debug module
*/
#include "QTSSWebDebugModule.h"
#include "OS.h"
#include "OSMemory.h"
#include "StrPtrLen.h"
#include "ev.h"
// STATIC DATA
static QTSS_AttributeID sStateAttr = qtssIllegalAttrID;
static StrPtrLen sRequestHeader("GET /debug HTTP");
#if MEMORY_DEBUGGING
static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: TimeShare/1.0\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n";
static char* sResponseEnd = "</BODY></HTML>";
#endif
// FUNCTION PROTOTYPES
QTSS_Error QTSSWebDebugModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Filter(QTSS_Filter_Params* inParams);
QTSS_Error QTSSWebDebugModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSWebDebugModuleDispatch);
}
QTSS_Error QTSSWebDebugModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_RTSPFilter_Role:
return Filter(&inParams->rtspFilterParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
// Register an attribute
static char* sStateName = "QTSSWebDebugModuleState";
(void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sStateName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sStateName, &sStateAttr);
// Tell the server our name!
static char* sModuleName = "QTSSWebDebugModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Filter(QTSS_Filter_Params* inParams)
{
UInt32 theLen = 0;
char* theFullRequest = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest, &theLen);
if ((theFullRequest == NULL) || (theLen < sRequestHeader.Len))
return QTSS_NoErr;
if (::memcmp(theFullRequest, sRequestHeader.Ptr, sRequestHeader.Len) != 0)
return QTSS_NoErr;
#if MEMORY_DEBUGGING
UInt32* theStateVal = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPRequest, sStateAttr, 0, (void**)&theStateVal, &theLen);
//if ((theStateVal == NULL) || (theLen != sizeof(UInt32)))
//{
Bool16 theFalse = false;
(void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &theFalse, sizeof(theFalse));
// Begin writing the HTTP response. We don't need to worry about flow control
// because we're using the QTSS_RTSPRequestObject for the response, which does buffering
(void)QTSS_Write(inParams->inRTSPRequest, sResponseHeader, ::strlen(sResponseHeader), &theLen, 0);
//QTSS_EventContextRef* theContext = NULL;
//(void)QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesEventCntxt, 0, (void**)&theContext, &theLen);
//Assert(theContext != NULL);
//Assert(theLen == sizeof(QTSS_EventContextRef));
//(void)QTSS_RequestEvent(*theContext, EV_WR);
// UInt32 theValue = 4;
// (void)QTSS_SetValue(inParams->inRTSPRequest, sStateAttr, 0, &theValue, sizeof(theValue));
// return QTSS_NoErr;
//}
//we must hold the tagQueue mutex for the duration of this exercise because
//we don't want any of the values we are reporting to change
OSMutexLocker locker(OSMemory::GetTagQueueMutex());
//write out header and total allocated memory
char buffer[1024];
qtss_sprintf(buffer, "<HTML><TITLE>TimeShare Debug Page</TITLE><BODY>Total dynamic memory allocated: %"_S32BITARG_"<P>List of objects:<BR>", OSMemory::GetAllocatedMemory());
(void)QTSS_Write(inParams->inRTSPRequest, buffer, ::strlen(buffer), &theLen, 0);
//now report the list of tags:
for (OSQueueIter iter(OSMemory::GetTagQueue()); !iter.IsDone(); iter.Next())
{
OSMemory::TagElem* elem = (OSMemory::TagElem*)iter.GetCurrent()->GetEnclosingObject();
Assert(elem != NULL);
if (elem->numObjects > 0)
{
qtss_sprintf(buffer, "Object allocated at: %s, %d. Number of currently allocated objects: %"_S32BITARG_", Total size: %"_S32BITARG_"<BR>", elem->fileName, elem->line, elem->numObjects, elem->totMemory);
(void)QTSS_Write(inParams->inRTSPRequest, buffer, ::strlen(buffer), &theLen, 0);
}
}
(void)QTSS_Write(inParams->inRTSPRequest, sResponseEnd, ::strlen(sResponseEnd), &theLen, 0);
#endif
return QTSS_NoErr;
}

View file

@ -0,0 +1,47 @@
/*
*
* @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: QTSSWebDebugModule.h
Contains: A module that uses the debugging information available in the server
to present a web page containing that information. Uses the Filter
module feature of the QTSS API.
*/
#ifndef __QTSSWEBDEBUGMODULE_H__
#define __QTSSWEBDEBUGMODULE_H__
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSWebDebugModule_Main(void* inPrivateArgs);
}
#endif //__QTSSWEBDEBUGMODULE_H__

View file

@ -0,0 +1,991 @@
/*
*
* @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: QTSSWebStatsModule.cpp
Contains: Implements web stats module
*/
#include <time.h>
#include <stdio.h> /* for qtss_printf */
#include <stdlib.h> /* for getloadavg & other useful stuff */
#include "QTSSWebStatsModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
// STATIC DATA
static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: QTSS/3.0\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n";
static QTSS_ServerObject sServer = NULL;
static QTSS_ModulePrefsObject sAccessLogPrefs = NULL;
static QTSS_ModulePrefsObject sReflectorPrefs = NULL;
static QTSS_ModulePrefsObject sSvrControlPrefs = NULL;
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_PrefsObject sServerPrefs = NULL;
static Bool16 sFalse = false;
static time_t sStartupTime = 0;
static char* sDefaultURL = "";
static StrPtrLen sDefaultURLStr;
static char* sDefaultURLPrefName = "web_stats_url";
static QTSS_Error QTSSWebStatsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error FilterRequest(QTSS_Filter_Params* inParams);
static void SendStats(QTSS_StreamRef inStream, UInt32 refreshInterval, Bool16 displayHelp, StrPtrLen* fieldList);
static char* GetPrefAsString(QTSS_ModulePrefsObject inPrefsObject, char* inPrefName);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSWebStatsModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSWebStatsModuleDispatch);
}
QTSS_Error QTSSWebStatsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RTSPFilter_Role:
return FilterRequest(&inParams->rtspFilterParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
// Tell the server our name!
static char* sModuleName = "QTSSWebStatsModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sAccessLogPrefs = QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName("QTSSAccessLogModule"));
sReflectorPrefs = QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName("QTSSReflectorModule"));
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
// This module may not be present, so be careful...
QTSS_ModuleObject theSvrControlModule = QTSSModuleUtils::GetModuleObjectByName("QTSSSvrControlModule");
if (theSvrControlModule != NULL)
sSvrControlPrefs = QTSSModuleUtils::GetModulePrefsObject(theSvrControlModule);
sServer = inParams->inServer;
sServerPrefs = inParams->inPrefs;
sStartupTime = ::time(NULL);
sDefaultURLStr.Delete();
sDefaultURLStr.Set(QTSSModuleUtils::GetStringAttribute(sPrefs, sDefaultURLPrefName, sDefaultURL));
return QTSS_NoErr;
}
QTSS_Error FilterRequest(QTSS_Filter_Params* inParams)
{
UInt8 sParamStopMask[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-9 //stop unless a '\t', ' ', or '&'
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10-19
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, //30-39
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, 0, 0, 0, 0, 0, 0, 0, 0, //60-69
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, 1 //250-255
};
//check to see if we should handle this request. Invokation is triggered
//by a "GET /" request
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
StrPtrLen theFullRequest;
(void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len);
StringParser fullRequest(&theFullRequest);
StrPtrLen strPtr;
StrPtrLen paramName;
StrPtrLen fieldsList;
fullRequest.ConsumeWord(&strPtr);
if ( strPtr.Equal(StrPtrLen("GET")) ) //it's a "Get" request
{
fullRequest.ConsumeWhitespace();
if ( fullRequest.Expect('/') )
{
UInt32 refreshInterval = 0;
Bool16 displayHelp = false;
OSCharArrayDeleter theWebStatsURL(GetPrefAsString(sPrefs, sDefaultURLPrefName));
StrPtrLen theWebStatsURLPtr(theWebStatsURL.GetObject());
// If there isn't any web stats URL, we can just return at this point
if (theWebStatsURLPtr.Len == 0)
return QTSS_NoErr;
fullRequest.ConsumeUntil(&strPtr, StringParser::sEOLWhitespaceQueryMask);
if ( strPtr.Len != 0 && strPtr.Equal(theWebStatsURLPtr) ) //it's a "stats" request
{
if ( fullRequest.Expect('?') )
{
do {
fullRequest.ConsumeWord(&paramName);
if( paramName.Len != 0)
{
if ( paramName.Equal(StrPtrLen("refresh",strlen("refresh"))) )
{
if (fullRequest.Expect('='))
refreshInterval = fullRequest.ConsumeInteger(NULL);
}
else if ( paramName.Equal(StrPtrLen("help",strlen("help"))) )
{
displayHelp = true;
}
else if ( paramName.Equal(StrPtrLen("fields",strlen("fields"))) )
{
if (fullRequest.Expect('='))
fullRequest.ConsumeUntil(&fieldsList, (UInt8*)sParamStopMask);
}
}
} while ( paramName.Len != 0 && fullRequest.Expect('&') );
}
// Before sending a response, set keep alive to off for this connection
(void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
SendStats(inParams->inRTSPRequest, refreshInterval, displayHelp, (fieldsList.Len != 0) ? &fieldsList : NULL);
}
}
}
return QTSS_NoErr;
}
void SendStats(QTSS_StreamRef inStream, UInt32 refreshInterval, Bool16 displayHelp, StrPtrLen* fieldList)
{
struct FieldIndex {
char* fieldName;
int fieldIndex;
};
const FieldIndex kFieldIndexes[] = {
{"title", 1},
{"dnsname", 2},
{"curtime", 3},
{"", 4},
{"serververs", 5},
{"serverbornon", 6},
{"serverstatus", 7},
{"", 8},
{"", 9},
{"", 10},
{"", 11},
{"", 12},
{"", 13},
{"currtp", 14},
{"currtsp", 15},
{"currtsphttp", 16},
{"curthru", 17},
{"curpkts", 18},
{"totbytes", 19},
{"totconns", 20},
{"", 21},
{"connlimit", 22},
{"thrulimit", 23},
{"moviedir", 24},
{"rtspip", 25},
{"rtspport", 26},
{"rtsptimeout", 27},
{"rtptimeout", 28},
{"secstobuffer", 29},
{"", 30},
{"accesslog", 31},
{"accesslogdir",32},
{"accesslogname", 33},
{"accessrollsize", 34},
{"accessrollinterval", 35},
{"", 36},
{"errorlog", 37},
{"errorlogdir", 38},
{"errorlogname", 39},
{"errorrollsize", 40},
{"errorrollinterval", 41},
{"errorloglevel", 42},
{"", 43},
{"assertbreak", 44},
{"autostart", 45},
{"totbytesupdateinterval", 46},
{"reflectordelay", 47},
{"reflectorbucketsize", 48},
{"historyinterval", 49},
{"outoffiledesc", 50},
{"numudpsockets", 51},
{"apiversion", 52},
{"numreliableudpbuffers", 53},
{"reliableudpwastedbytes", 54},
{"numtaskthreads", 55}
};
const int kMaxFieldNum = 55;
static char* kEmptyStr = "?";
char* thePrefStr = kEmptyStr;
char buffer[1024];
(void)QTSS_Write(inStream, sResponseHeader, ::strlen(sResponseHeader), NULL, 0);
if (refreshInterval > 0)
{
qtss_sprintf(buffer, "<META HTTP-EQUIV=Refresh CONTENT=%"_U32BITARG_">\n", refreshInterval);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
//qtss_sprintf(buffer, "<body text=\"#000000\" bgcolor=\"#C0C0C0\" link=\"#0000FF\" vlink=\"#551A8B\" alink=\"#0000FF\">\n");
//(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
char *theHTML = "<HTML><BODY>\n";
(void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0);
if (displayHelp)
{
#ifndef __MacOSX__
static StrPtrLen sHelpLine1("<P><b>Streaming Server Statistics Help</b></P>\n");
#else
static StrPtrLen sHelpLine1("<P><b>QuickTime Streaming Server Statistics Help</b></P>\n");
#endif
static StrPtrLen sHelpLine2("<P>Example:</P>\n");
static StrPtrLen sHelpLine3("<BLOCKQUOTE><P>http://server/statsURL?help&amp;refresh=15&amp;fields=curtime,cpuload </P>\n");
static StrPtrLen sHelpLine4("\"?\" means that there are options being attached to the stats request.<BR>\n");
static StrPtrLen sHelpLine5("\"&amp;\" separates multiple stats options<BR>\n<BR>\n");
static StrPtrLen sHelpLine6("<P>The three possible parameters to stats are:</P>\n");
static StrPtrLen sHelpLine7("<P>\"help\" -- shows the help information you're reading right now.</P>\n");
static StrPtrLen sHelpLine8("<P>\"refresh=&#91;n&#93;\" -- tells the browser to automatically update the page every &#91;n&#93; seconds.</P>\n");
static StrPtrLen sHelpLine9("<P>\"fields=&#91;fieldList&#93;\" -- show only the fields specified in comma delimited &#91;fieldList&#93;</P>\n");
static StrPtrLen sHelpLine10("<BLOCKQUOTE>The following fields are available for use with the \"fields\" option:</P><BLOCKQUOTE><DL>\n");
(void)QTSS_Write(inStream, sHelpLine1.Ptr, sHelpLine1.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine2.Ptr, sHelpLine2.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine3.Ptr, sHelpLine3.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine4.Ptr, sHelpLine4.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine5.Ptr, sHelpLine5.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine6.Ptr, sHelpLine6.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine7.Ptr, sHelpLine7.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine8.Ptr, sHelpLine8.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine9.Ptr, sHelpLine9.Len, NULL, 0);
(void)QTSS_Write(inStream, sHelpLine10.Ptr, sHelpLine10.Len, NULL, 0);
for (short i = 0; i < kMaxFieldNum; i++)
{
qtss_sprintf(buffer, "<DT><I>%s</I></DT>\n", kFieldIndexes[i].fieldName);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
static StrPtrLen sHelpLine11("</DL></BLOCKQUOTE></BLOCKQUOTE></BLOCKQUOTE><BR><P><HR>");
(void)QTSS_Write(inStream, sHelpLine11.Ptr, sHelpLine11.Len, NULL, 0);
}
StringParser fieldNamesParser(fieldList);
StrPtrLen fieldName;
int fieldNum = 0;
do
{
if (fieldList != NULL)
{
fieldNum = 0;
fieldNamesParser.ConsumeWord(&fieldName);
for (short i = 0; i < kMaxFieldNum; i++)
{
if ( fieldName.Equal(StrPtrLen(kFieldIndexes[i].fieldName, ::strlen(kFieldIndexes[i].fieldName))) )
{
fieldNum = kFieldIndexes[i].fieldIndex;
break;
}
}
}
else
{
fieldNum++;
if ( fieldNum > kMaxFieldNum )
fieldNum = 0;
}
UInt32 theLen = 0;
switch (fieldNum)
{
case 1:
{
#if __MacOSX__
static StrPtrLen sStatsLine1("<TITLE>QuickTime Streaming Server Stats</TITLE><BR>\n");
(void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0);
#else
static StrPtrLen sStatsLine1("<TITLE>Streaming Server Stats</TITLE><BR>\n");
(void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0);
#endif
#if __MacOSX__
static StrPtrLen sStatsLine2("<center><h1>QuickTime Streaming Server Statistics</h1></center>\n");
(void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0);
#else
static StrPtrLen sStatsLine2("<center><h1>Streaming Server Statistics</h1></center>\n");
(void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0);
#endif
}
break;
case 2:
{
StrPtrLen theDNS;
(void)QTSS_GetValuePtr(sServer, qtssSvrDefaultDNSName, 0, (void**)&theDNS.Ptr, &theDNS.Len);
if ( theDNS.Ptr == NULL )
{ // no DNS, try for the IP address only.
(void)QTSS_GetValuePtr(sServer, qtssSvrDefaultIPAddr, 0, (void**)&theDNS.Ptr, &theDNS.Len);
}
if ( theDNS.Ptr != NULL )
{
qtss_sprintf(buffer, "<b>DNS Name (default): </b> %s<BR>\n", theDNS.Ptr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 3:
{
char uptimebuffer[1024];
time_t curTime = ::time(NULL);
qtss_sprintf(uptimebuffer, "<b>Current Time: </b> %s<BR>\n", qtss_ctime(&curTime, buffer, sizeof(buffer)));
(void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0);
time_t upTime = curTime - sStartupTime;
#define kDaySeconds (24 * 60 * 60)
#define kHourSeconds (60 * 60)
#define kMinuteSeconds 60
UInt32 upTimeDays = upTime / kDaySeconds;
UInt32 upTimeHours = (upTime % kDaySeconds) / kHourSeconds;
UInt32 upTimeMinutes = (upTime % kHourSeconds) / kMinuteSeconds;
UInt32 upTimeSeconds = (upTime % kMinuteSeconds);
qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "<b>Up Time Total Seconds: </b> %"_U32BITARG_"<BR>\n", upTime);
uptimebuffer[sizeof(uptimebuffer) -1] = 0;
(void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0);
qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "<b>Up Time: </b> %"_U32BITARG_" days %"_U32BITARG_" hours %"_U32BITARG_" minutes %"_U32BITARG_" seconds <BR>\n", upTimeDays, upTimeHours,upTimeMinutes, upTimeSeconds);
uptimebuffer[sizeof(uptimebuffer) -1] = 0;
(void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0);
}
break;
case 4:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 5:
{
StrPtrLen theVersion;
(void)QTSS_GetValuePtr(sServer, qtssSvrRTSPServerHeader, 0, (void**)&theVersion.Ptr, &theVersion.Len);
Assert(theVersion.Ptr != NULL);
if (theVersion.Len > 7) //Skip the "Server:" text
theVersion.Ptr += 7;
qtss_sprintf(buffer, "<b>Server Version: </b>%s<BR>\n", theVersion.Ptr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 6:
{
StrPtrLen theBuildDate;
(void)QTSS_GetValuePtr(sServer, qtssSvrServerBuildDate, 0, (void**)&theBuildDate.Ptr, &theBuildDate.Len);
Assert(theBuildDate.Ptr != NULL);
qtss_sprintf(buffer, "<b>Server Build Date: </b> %s<BR>\n", theBuildDate.Ptr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 7:
{
char statusBuffer[1024];
const char* states[] = { "Starting Up",
"Running",
"Refusing Connections",
"Fatal Error",
"Shutting Down"
};
QTSS_ServerState theState = qtssRunningState;
theLen = sizeof(theState);
(void)QTSS_GetValue(sServer, qtssSvrState, 0, &theState, &theLen);
if (theState == qtssRunningState)
{ qtss_snprintf(statusBuffer, sizeof(statusBuffer), "<b>Status: </b> %s since %s<BR>", states[theState], qtss_ctime(&sStartupTime,buffer,sizeof(buffer)));
}
else
qtss_snprintf(statusBuffer,sizeof(statusBuffer), "<b>Status: </b> %s<BR>", states[theState]);
(void)QTSS_Write(inStream, statusBuffer, ::strlen(statusBuffer), NULL, 0);
}
break;
case 8:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 9:
{
//NOOP
}
break;
case 10:
{
//NOOP
}
break;
case 11:
{
//NOOP
}
break;
case 12:
{
/*
struct vm_statistics vmStats = {};
if (vm_statistics (current_task (), &vmStats) != KERN_SUCCESS)
memset (&stats, '\0', sizeof (vmStats)) ;
*/
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 13:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
//**********************************
case 14:
{
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Current RTP Connections: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 15:
{
(void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Current RTSP Connections: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 16:
{
(void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Current RTSP over HTTP Connections: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 17:
{
UInt32 curBandwidth = 0;
theLen = sizeof(curBandwidth);
(void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen);
qtss_sprintf(buffer, "<b>Current Throughput: </b> %"_U32BITARG_" kbits<BR>\n", curBandwidth/1024);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 18:
{
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Current Packets Per Second: </b> %s <BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 19:
{
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalBytes, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Total Bytes Served: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 20:
{
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalConn, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Total Connections: </b> %s<BR>", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 21:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
//**************************************
case 22:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumConnections, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Maximum Connections: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 23:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumBandwidth, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Maximum Throughput: </b> %s Kbits<BR>\n",thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 24:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMovieFolder, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Movie Folder Path: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 25:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPIPAddr, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>RTSP IP Address: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 26:
{
static StrPtrLen sRTSPPortsStart("<b>RTSP Ports: </b> ");
(void)QTSS_Write(inStream, sRTSPPortsStart.Ptr, sRTSPPortsStart.Len, NULL, 0);
StrPtrLen thePort;
for (UInt32 theIndex = 0; true; theIndex++)
{
QTSS_Error theErr = QTSS_GetValuePtr(sServer, qtssSvrRTSPPorts, theIndex, (void**)&thePort.Ptr, &thePort.Len);
if (theErr != QTSS_NoErr)
break;
Assert(thePort.Ptr != NULL);
char temp[20];
qtss_sprintf(temp, "%u ", *(UInt16*)thePort.Ptr);
(void)QTSS_Write(inStream, temp, ::strlen(temp), NULL, 0);
}
static StrPtrLen sRTSPPortsEnd("<BR>\n");
(void)QTSS_Write(inStream, sRTSPPortsEnd.Ptr, sRTSPPortsEnd.Len, NULL, 0);
}
break;
case 27:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPTimeout, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>RTP Timeout: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 28:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTPTimeout, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>RTP Timeout: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 29:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 30:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 31:
{
if ( sAccessLogPrefs != NULL )
{
thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logging");
qtss_sprintf(buffer, "<b>Access Logging: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 32:
{
if ( sAccessLogPrefs != NULL )
{
thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_dir");
qtss_sprintf(buffer, "<b>Access Log Directory: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 33:
{
if ( sAccessLogPrefs != NULL )
{
thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_name");
qtss_sprintf(buffer, "<b>Access Log Name: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 34:
{
if ( sAccessLogPrefs != NULL )
{
thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_size");
qtss_sprintf(buffer, "<b>Access Log Roll Size: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 35:
{
if ( sAccessLogPrefs != NULL )
{
thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_interval");
qtss_sprintf(buffer, "<b>Access Log Roll Interval (days): </b> %s<BR>", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 36:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 37:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogEnabled, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Error Logging: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 38:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogDir, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Error Log Directory: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 39:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogName, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Error Log Name: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 40:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaxErrorLogSize, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Error Log Roll Size: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 41:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorRollInterval, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Error Log Roll Interval (days): </b> %s<BR>\n",thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 42:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogVerbosity, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Error Log Verbosity: </b> %s<BR>", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 43:
{
(void)QTSS_Write(inStream, "<P><HR>", ::strlen("<P><HR>"), NULL, 0);
}
break;
case 44:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsBreakOnAssert, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Break On Assert: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 45:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsAutoRestart, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>AutoStart: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 46:
{
(void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsTotalBytesUpdate, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Total Bytes Update Interval: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 47:
{
if (sReflectorPrefs != NULL)
{
thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_delay");
qtss_sprintf(buffer, "<b>Reflector Delay Time: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 48:
{
if (sReflectorPrefs != NULL)
{
thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_bucket_size");
qtss_sprintf(buffer, "<b>Reflector Bucket Size: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 49:
{
if ( sSvrControlPrefs != NULL)
{
thePrefStr = GetPrefAsString(sSvrControlPrefs, "history_update_interval");
qtss_sprintf(buffer, "<b>History Update Interval: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
}
break;
case 50:
{
Bool16 isOutOfDescriptors = false;
theLen = sizeof(isOutOfDescriptors);
(void)QTSS_GetValue(sServer, qtssSvrIsOutOfDescriptors, 0, &isOutOfDescriptors, &theLen);
qtss_sprintf(buffer, "<b>Out of file descriptors: </b> %d<BR>\n", isOutOfDescriptors);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 51:
{
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrNumUDPSockets, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Number of UDP sockets: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 52:
{
UInt32 apiVersion = 0;
UInt32 size = sizeof(UInt32);
(void)QTSS_GetValue(sServer, qtssServerAPIVersion, 0, &apiVersion, &size);
qtss_sprintf(buffer, "<b>API version: </b> %d.%d<BR>\n", (int)( (UInt32) (apiVersion & (UInt32) 0xFFFF0000L) >> 16), (int)(apiVersion &(UInt32) 0x0000FFFFL));
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 53:
{
UInt32 reliableUDPBuffers = 0;
UInt32 blahSize = sizeof(reliableUDPBuffers);
(void)QTSS_GetValue(sServer, qtssSvrNumReliableUDPBuffers, 0, &reliableUDPBuffers, &blahSize);
qtss_sprintf(buffer, "<b>Num Reliable UDP Retransmit Buffers: </b> %"_U32BITARG_"<BR>\n", reliableUDPBuffers);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 54:
{
UInt32 wastedBufSpace = 0;
UInt32 blahSize2 = sizeof(wastedBufSpace);
(void)QTSS_GetValue(sServer, qtssSvrReliableUDPWastageInBytes, 0, &wastedBufSpace, &blahSize2);
qtss_sprintf(buffer, "<b>Amount of buffer space being wasted in UDP Retrans buffers: </b> %"_U32BITARG_"<BR>\n", wastedBufSpace);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
case 55:
{
(void)QTSS_GetValueAsString(sServer, qtssSvrNumThreads, 0, &thePrefStr);
qtss_sprintf(buffer, "<b>Number of Task Threads: </b> %s<BR>\n", thePrefStr);
(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
}
break;
default:
break;
} //switch fieldNum
if (fieldList != NULL && !fieldNamesParser.Expect(','))
fieldNum = 0;
if (thePrefStr != kEmptyStr)
delete [] thePrefStr;
thePrefStr = kEmptyStr;
} while (fieldNum != 0);
theHTML = "</BODY></HTML>\n";
(void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0);
}
char* GetPrefAsString(QTSS_ModulePrefsObject inPrefsObject, char* inPrefName)
{
static StrPtrLen sEmpty("");
//
// Get the attribute ID of this pref.
QTSS_AttributeID theID = qtssIllegalAttrID;
if(inPrefsObject != NULL)
theID = QTSSModuleUtils::GetAttrID(inPrefsObject, inPrefName);
char* theString = NULL;
if(inPrefsObject != NULL)
(void)QTSS_GetValueAsString(inPrefsObject, theID, 0, &theString);
if (theString == NULL)
theString = sEmpty.GetAsCString();
return theString;
}

View file

@ -0,0 +1,46 @@
/*
*
* @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: QTSSWebStatsModule.h
Contains: A module that uses the stats information available in the server
to present a web page containing that information. Uses the Filter
module feature of QTSS API.
*/
#ifndef __QTSSWEBSTATSMODULE_H__
#define __QTSSWEBSTATSMODULE_H__
#include "QTSS.h"
extern "C"
{
EXPORT QTSS_Error QTSSWebStatsModule_Main(void* inPrivateArgs);
}
#endif // __QTSSWEBSTATSMODULE_H__

2024
APIStubLib/QTSS.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,215 @@
/*
*
* @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: QTSSRTSPProtocol.h
Contains: Constant & Enum definitions for RTSP protocol type parts
of QTSS API.
*/
#ifndef QTSS_RTSPPROTOCOL_H
#define QTSS_RTSPPROTOCOL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "OSHeaders.h"
enum
{
qtssDescribeMethod = 0,
qtssSetupMethod = 1,
qtssTeardownMethod = 2,
qtssNumVIPMethods = 3,
qtssPlayMethod = 3,
qtssPauseMethod = 4,
qtssOptionsMethod = 5,
qtssAnnounceMethod = 6,
qtssGetParameterMethod = 7,
qtssSetParameterMethod = 8,
qtssRedirectMethod = 9,
qtssRecordMethod = 10,
qtssNumMethods = 11,
qtssIllegalMethod = 11
};
typedef UInt32 QTSS_RTSPMethod;
enum
{
//These are the common request headers (optimized)
qtssAcceptHeader = 0,
qtssCSeqHeader = 1,
qtssUserAgentHeader = 2,
qtssTransportHeader = 3,
qtssSessionHeader = 4,
qtssRangeHeader = 5,
qtssNumVIPHeaders = 6,
//Other request headers
qtssAcceptEncodingHeader = 6,
qtssAcceptLanguageHeader = 7,
qtssAuthorizationHeader = 8,
qtssBandwidthHeader = 9,
qtssBlockSizeHeader = 10,
qtssCacheControlHeader = 11,
qtssConferenceHeader = 12,
qtssConnectionHeader = 13,
qtssContentBaseHeader = 14,
qtssContentEncodingHeader = 15,
qtssContentLanguageHeader = 16,
qtssContentLengthHeader = 17,
qtssContentLocationHeader = 18,
qtssContentTypeHeader = 19,
qtssDateHeader = 20,
qtssExpiresHeader = 21,
qtssFromHeader = 22,
qtssHostHeader = 23,
qtssIfMatchHeader = 24,
qtssIfModifiedSinceHeader = 25,
qtssLastModifiedHeader = 26,
qtssLocationHeader = 27,
qtssProxyAuthenticateHeader = 28,
qtssProxyRequireHeader = 29,
qtssRefererHeader = 30,
qtssRetryAfterHeader = 31,
qtssRequireHeader = 32,
qtssRTPInfoHeader = 33,
qtssScaleHeader = 34,
qtssSpeedHeader = 35,
qtssTimestampHeader = 36,
qtssVaryHeader = 37,
qtssViaHeader = 38,
qtssNumRequestHeaders = 39,
//Additional response headers
qtssAllowHeader = 39,
qtssPublicHeader = 40,
qtssServerHeader = 41,
qtssUnsupportedHeader = 42,
qtssWWWAuthenticateHeader = 43,
qtssSameAsLastHeader = 44,
//Newly added headers
qtssExtensionHeaders = 45,
qtssXRetransmitHeader = 45,
qtssXAcceptRetransmitHeader = 46,
qtssXRTPMetaInfoHeader = 47,
qtssXTransportOptionsHeader = 48,
qtssXPacketRangeHeader = 49,
qtssXPreBufferHeader = 50,
qtssXDynamicRateHeader = 51,
qtssXAcceptDynamicRateHeader= 52,
// QT Player random data request
qtssXRandomDataSizeHeader = 53,
// 3gpp release 6
qtss3GPPLinkCharHeader = 54,
qtss3GPPAdaptationHeader = 55,
qtss3GPPQOEFeedback = 56,
qtss3GPPQOEMetrics = 57,
// 3gpp annex g
qtssXPreDecBufSizeHeader = 58,
qtssXInitPredecBufPeriodHeader = 59,
qtssXInitPostDecBufPeriodHeader = 60,
qtss3GPPVideoPostDecBufSizeHeader = 61,
qtssNumHeaders = 62,
qtssIllegalHeader = 62
};
typedef UInt32 QTSS_RTSPHeader;
enum
{
qtssContinue = 0, //100
qtssSuccessOK = 1, //200
qtssSuccessCreated = 2, //201
qtssSuccessAccepted = 3, //202
qtssSuccessNoContent = 4, //203
qtssSuccessPartialContent = 5, //204
qtssSuccessLowOnStorage = 6, //250
qtssMultipleChoices = 7, //300
qtssRedirectPermMoved = 8, //301
qtssRedirectTempMoved = 9, //302
qtssRedirectSeeOther = 10, //303
qtssRedirectNotModified = 11, //304
qtssUseProxy = 12, //305
qtssClientBadRequest = 13, //400
qtssClientUnAuthorized = 14, //401
qtssPaymentRequired = 15, //402
qtssClientForbidden = 16, //403
qtssClientNotFound = 17, //404
qtssClientMethodNotAllowed = 18, //405
qtssNotAcceptable = 19, //406
qtssProxyAuthenticationRequired = 20, //407
qtssRequestTimeout = 21, //408
qtssClientConflict = 22, //409
qtssGone = 23, //410
qtssLengthRequired = 24, //411
qtssPreconditionFailed = 25, //412
qtssRequestEntityTooLarge = 26, //413
qtssRequestURITooLarge = 27, //414
qtssUnsupportedMediaType = 28, //415
qtssClientParameterNotUnderstood = 29, //451
qtssClientConferenceNotFound = 30, //452
qtssClientNotEnoughBandwidth = 31, //453
qtssClientSessionNotFound = 32, //454
qtssClientMethodNotValidInState = 33, //455
qtssClientHeaderFieldNotValid = 34, //456
qtssClientInvalidRange = 35, //457
qtssClientReadOnlyParameter = 36, //458
qtssClientAggregateOptionNotAllowed = 37, //459
qtssClientAggregateOptionAllowed = 38, //460
qtssClientUnsupportedTransport = 39, //461
qtssClientDestinationUnreachable = 40, //462
qtssServerInternal = 41, //500
qtssServerNotImplemented = 42, //501
qtssServerBadGateway = 43, //502
qtssServerUnavailable = 44, //503
qtssServerGatewayTimeout = 45, //505
qtssRTSPVersionNotSupported = 46, //504
qtssServerOptionNotSupported = 47, //551
qtssNumStatusCodes = 48
};
typedef UInt32 QTSS_RTSPStatusCode;
#ifdef __cplusplus
}
#endif
#endif

404
APIStubLib/QTSS_Private.cpp Normal file
View file

@ -0,0 +1,404 @@
/*
*
* @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: QTSS_Private.c
Contains: Code for stub library and stub callback functions.
*/
#include <stdlib.h>
#include "SafeStdLib.h"
#ifndef __Win32__
#include <sys/types.h>
#include <sys/uio.h>
#endif
#include "QTSS.h"
#include "QTSS_Private.h"
static QTSS_CallbacksPtr sCallbacks = NULL;
static QTSS_StreamRef sErrorLogStream = NULL;
QTSS_Error _stublibrary_main(void* inPrivateArgs, QTSS_DispatchFuncPtr inDispatchFunc)
{
QTSS_PrivateArgsPtr theArgs = (QTSS_PrivateArgsPtr)inPrivateArgs;
// Setup
sCallbacks = theArgs->inCallbacks;
sErrorLogStream = theArgs->inErrorLogStream;
// Send requested information back to the server
theArgs->outStubLibraryVersion = QTSS_API_VERSION;
theArgs->outDispatchFunction = inDispatchFunc;
return QTSS_NoErr;
}
// STUB FUNCTION DEFINITIONS
void* QTSS_New(FourCharCode inMemoryIdentifier, UInt32 inSize)
{
return (void *) ((QTSS_CallbackPtrProcPtr) sCallbacks->addr [kNewCallback]) (inMemoryIdentifier, inSize);
}
void QTSS_Delete(void* inMemory)
{
(sCallbacks->addr [kDeleteCallback]) (inMemory);
}
SInt64 QTSS_Milliseconds(void)
{
SInt64 outMilliseconds = 0;
(sCallbacks->addr [kMillisecondsCallback]) (&outMilliseconds);
return outMilliseconds;
}
time_t QTSS_MilliSecsTo1970Secs(SInt64 inQTSS_MilliSeconds)
{
time_t outSeconds = 0;
(sCallbacks->addr [kConvertToUnixTimeCallback]) (&inQTSS_MilliSeconds, &outSeconds);
return outSeconds;
}
// STARTUP ROUTINES
QTSS_Error QTSS_AddRole(QTSS_Role inRole)
{
return (sCallbacks->addr [kAddRoleCallback]) (inRole);
}
// DICTIONARY ROUTINES
QTSS_Error QTSS_CreateObjectType(QTSS_ObjectType* outType)
{
return (sCallbacks->addr [kCreateObjectTypeCallback]) (outType);
}
QTSS_Error QTSS_AddAttribute(QTSS_ObjectType inType, const char* inTag, void* inUnused)
{
return (sCallbacks->addr [kAddAttributeCallback]) (inType, inTag, inUnused);
}
QTSS_Error QTSS_AddStaticAttribute(QTSS_ObjectType inObjectType, char* inAttrName, void* inUnused, QTSS_AttrDataType inAttrDataType)
{
return (sCallbacks->addr [kAddStaticAttributeCallback]) (inObjectType, inAttrName, inUnused, inAttrDataType);
}
QTSS_Error QTSS_AddInstanceAttribute(QTSS_Object inObject, char* inAttrName, void* inUnused, QTSS_AttrDataType inAttrDataType)
{
return (sCallbacks->addr [kAddInstanceAttributeCallback]) (inObject, inAttrName, inUnused, inAttrDataType);
}
QTSS_Error QTSS_RemoveInstanceAttribute(QTSS_Object inObject, QTSS_AttributeID inID)
{
return (sCallbacks->addr [kRemoveInstanceAttributeCallback]) (inObject, inID);
}
QTSS_Error QTSS_IDForAttr(QTSS_ObjectType inType, const char* inTag, QTSS_AttributeID* outID)
{
return (sCallbacks->addr [kIDForTagCallback]) (inType, inTag, outID);
}
QTSS_Error QTSS_GetAttrInfoByIndex(QTSS_Object inObject, UInt32 inIndex, QTSS_Object* outAttrInfoObject)
{
return (sCallbacks->addr [kGetAttrInfoByIndexCallback]) (inObject, inIndex, outAttrInfoObject);
}
QTSS_Error QTSS_GetAttrInfoByID(QTSS_Object inObject, QTSS_AttributeID inAttrID, QTSS_Object* outAttrInfoObject)
{
return (sCallbacks->addr [kGetAttrInfoByIDCallback]) (inObject, inAttrID, outAttrInfoObject);
}
QTSS_Error QTSS_GetAttrInfoByName(QTSS_Object inObject, char* inAttrName, QTSS_Object* outAttrInfoObject)
{
return (sCallbacks->addr [kGetAttrInfoByNameCallback]) (inObject, inAttrName, outAttrInfoObject);
}
QTSS_Error QTSS_GetValuePtr (QTSS_Object inDictionary, QTSS_AttributeID inID, UInt32 inIndex, void** outBuffer, UInt32* outLen)
{
return (sCallbacks->addr [kGetAttributePtrByIDCallback]) (inDictionary, inID, inIndex, outBuffer, outLen);
}
QTSS_Error QTSS_GetValue (QTSS_Object inDictionary, QTSS_AttributeID inID, UInt32 inIndex, void* ioBuffer, UInt32* ioLen)
{
return (sCallbacks->addr [kGetAttributeByIDCallback]) (inDictionary, inID, inIndex, ioBuffer, ioLen);
}
QTSS_Error QTSS_GetValueAsString (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex, char** outString)
{
return (sCallbacks->addr [kGetValueAsStringCallback]) (inObject, inID, inIndex, outString);
}
QTSS_Error QTSS_TypeStringToType(const char* inTypeString, QTSS_AttrDataType* outType)
{
return (sCallbacks->addr [kTypeStringToTypeCallback]) (inTypeString, outType);
}
QTSS_Error QTSS_TypeToTypeString(const QTSS_AttrDataType inType, char** outTypeString)
{
return (sCallbacks->addr [kTypeToTypeStringCallback]) (inType, outTypeString);
}
QTSS_Error QTSS_StringToValue(const char* inValueAsString, const QTSS_AttrDataType inType, void* ioBuffer, UInt32* ioBufSize)
{
return (sCallbacks->addr [kStringToValueCallback]) (inValueAsString, inType, ioBuffer, ioBufSize);
}
QTSS_Error QTSS_ValueToString(const void* inValue, const UInt32 inValueLen, const QTSS_AttrDataType inType, char** outString)
{
return (sCallbacks->addr [kValueToStringCallback]) (inValue, inValueLen, inType, outString);
}
QTSS_Error QTSS_SetValue (QTSS_Object inDictionary, QTSS_AttributeID inID,UInt32 inIndex, const void* inBuffer, UInt32 inLen)
{
return (sCallbacks->addr [kSetAttributeByIDCallback]) (inDictionary, inID, inIndex, inBuffer, inLen);
}
QTSS_Error QTSS_SetValuePtr (QTSS_Object inDictionary, QTSS_AttributeID inID, const void* inBuffer, UInt32 inLen)
{
return (sCallbacks->addr [kSetAttributePtrCallback]) (inDictionary, inID, inBuffer, inLen);
}
QTSS_Error QTSS_CreateObjectValue (QTSS_Object inDictionary, QTSS_AttributeID inID, QTSS_ObjectType inType, UInt32* outIndex, QTSS_Object* outCreatedObject)
{
return (sCallbacks->addr [kCreateObjectValueCallback]) (inDictionary, inID, inType, outIndex, outCreatedObject);
}
QTSS_Error QTSS_GetNumValues (QTSS_Object inObject, QTSS_AttributeID inID, UInt32* outNumValues)
{
return (sCallbacks->addr [kGetNumValuesCallback]) (inObject, inID, outNumValues);
}
QTSS_Error QTSS_GetNumAttributes (QTSS_Object inObject, UInt32* outNumValues)
{
return (sCallbacks->addr [kGetNumAttributesCallback]) (inObject, outNumValues);
}
QTSS_Error QTSS_RemoveValue (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex)
{
return (sCallbacks->addr [kRemoveValueCallback]) (inObject, inID, inIndex);
}
// STREAM ROUTINES
QTSS_Error QTSS_Write(QTSS_StreamRef inStream, const void* inBuffer, UInt32 inLen, UInt32* outLenWritten, UInt32 inFlags)
{
return (sCallbacks->addr [kWriteCallback]) (inStream, inBuffer, inLen, outLenWritten, inFlags);
}
QTSS_Error QTSS_WriteV(QTSS_StreamRef inStream, iovec* inVec, UInt32 inNumVectors, UInt32 inTotalLength, UInt32* outLenWritten)
{
return (sCallbacks->addr [kWriteVCallback]) (inStream, inVec, inNumVectors, inTotalLength, outLenWritten);
}
QTSS_Error QTSS_Flush(QTSS_StreamRef inStream)
{
return (sCallbacks->addr [kFlushCallback]) (inStream);
}
QTSS_Error QTSS_Read(QTSS_StreamRef inRef, void* ioBuffer, UInt32 inBufLen, UInt32* outLengthRead)
{
return (sCallbacks->addr [kReadCallback]) (inRef, ioBuffer, inBufLen, outLengthRead);
}
QTSS_Error QTSS_Seek(QTSS_StreamRef inRef, UInt64 inNewPosition)
{
return (sCallbacks->addr [kSeekCallback]) (inRef, inNewPosition);
}
QTSS_Error QTSS_Advise(QTSS_StreamRef inRef, UInt64 inPosition, UInt32 inAdviseSize)
{
return (sCallbacks->addr [kAdviseCallback]) (inRef, inPosition, inAdviseSize);
}
// SERVICE ROUTINES
QTSS_Error QTSS_AddService(const char* inServiceName, QTSS_ServiceFunctionPtr inFunctionPtr)
{
return (sCallbacks->addr [kAddServiceCallback]) (inServiceName, inFunctionPtr);
}
QTSS_Error QTSS_IDForService(const char* inTag, QTSS_ServiceID* outID)
{
return (sCallbacks->addr [kIDForServiceCallback]) (inTag, outID);
}
QTSS_Error QTSS_DoService(QTSS_ServiceID inID, QTSS_ServiceFunctionArgsPtr inArgs)
{
return (sCallbacks->addr [kDoServiceCallback]) (inID, inArgs);
}
// RTSP ROUTINES
QTSS_Error QTSS_SendRTSPHeaders(QTSS_RTSPRequestObject inRef)
{
return (sCallbacks->addr [kSendRTSPHeadersCallback]) (inRef);
}
QTSS_Error QTSS_AppendRTSPHeader(QTSS_RTSPRequestObject inRef, QTSS_RTSPHeader inHeader, const char* inValue, UInt32 inValueLen)
{
return (sCallbacks->addr [kAppendRTSPHeadersCallback]) (inRef, inHeader, inValue, inValueLen);
}
QTSS_Error QTSS_SendStandardRTSPResponse(QTSS_RTSPRequestObject inRTSPRequest, QTSS_Object inRTPInfo, UInt32 inFlags)
{
return (sCallbacks->addr [kSendStandardRTSPCallback]) (inRTSPRequest, inRTPInfo, inFlags);
}
// RTP ROUTINES
QTSS_Error QTSS_AddRTPStream(QTSS_ClientSessionObject inClientSession, QTSS_RTSPRequestObject inRTSPRequest, QTSS_RTPStreamObject* outStream, QTSS_AddStreamFlags inFlags)
{
return (sCallbacks->addr [kAddRTPStreamCallback]) (inClientSession, inRTSPRequest, outStream, inFlags);
}
QTSS_Error QTSS_Play(QTSS_ClientSessionObject inClientSession, QTSS_RTSPRequestObject inRTSPRequest, QTSS_PlayFlags inPlayFlags)
{
return (sCallbacks->addr [kPlayCallback]) (inClientSession, inRTSPRequest, inPlayFlags);
}
QTSS_Error QTSS_Pause(QTSS_ClientSessionObject inClientSession)
{
return (sCallbacks->addr [kPauseCallback]) (inClientSession);
}
QTSS_Error QTSS_Teardown(QTSS_ClientSessionObject inClientSession)
{
return (sCallbacks->addr [kTeardownCallback]) (inClientSession);
}
QTSS_Error QTSS_RefreshTimeOut(QTSS_ClientSessionObject inClientSession)
{
return (sCallbacks->addr [kRefreshTimeOutCallback]) (inClientSession);
}
// FILE SYSTEM ROUTINES
QTSS_Error QTSS_OpenFileObject(char* inPath, QTSS_OpenFileFlags inFlags, QTSS_Object* outFileObject)
{
return (sCallbacks->addr [kOpenFileObjectCallback]) (inPath, inFlags, outFileObject);
}
QTSS_Error QTSS_CloseFileObject(QTSS_Object inFileObject)
{
return (sCallbacks->addr [kCloseFileObjectCallback]) (inFileObject);
}
// SOCKET ROUTINES
QTSS_Error QTSS_CreateStreamFromSocket(int inFileDesc, QTSS_StreamRef* outStream)
{
return (sCallbacks->addr [kCreateSocketStreamCallback]) (inFileDesc, outStream);
}
QTSS_Error QTSS_DestroySocketStream(QTSS_StreamRef inStream)
{
return (sCallbacks->addr [kDestroySocketStreamCallback]) (inStream);
}
// ASYNC I/O STREAM ROUTINES
QTSS_Error QTSS_RequestEvent(QTSS_StreamRef inStream, QTSS_EventType inEventMask)
{
return (sCallbacks->addr [kRequestEventCallback]) (inStream, inEventMask);
}
QTSS_Error QTSS_SignalStream(QTSS_StreamRef inStream)
{
return (sCallbacks->addr [kSignalStreamCallback]) (inStream);
}
QTSS_Error QTSS_SetIdleTimer(SInt64 inIdleMsec)
{
return (sCallbacks->addr [kSetIdleTimerCallback]) (inIdleMsec);
}
QTSS_Error QTSS_SetIntervalRoleTimer(SInt64 inIdleMsec)
{
return (sCallbacks->addr [kSetIntervalRoleTimerCallback]) (inIdleMsec);
}
QTSS_Error QTSS_RequestGlobalLock()
{
return (sCallbacks->addr [kRequestGlobalLockCallback]) ();
}
// SYNCH GLOBAL MULTIPLE READERS/SINGLE WRITER ROUTINES
Bool16 QTSS_IsGlobalLocked()
{
return (Bool16) (sCallbacks->addr [kIsGlobalLockedCallback]) ();
}
QTSS_Error QTSS_GlobalUnLock()
{
return (sCallbacks->addr [kUnlockGlobalLock]) ();
}
QTSS_Error QTSS_LockObject(QTSS_Object inObject)
{
return (sCallbacks->addr [kLockObjectCallback]) (inObject);
}
QTSS_Error QTSS_UnlockObject(QTSS_Object inObject)
{
return (sCallbacks->addr [kUnlockObjectCallback]) (inObject);
}
// AUTHENTICATION AND AUTHORIZATION ROUTINE
QTSS_Error QTSS_Authenticate( const char* inAuthUserName,
const char* inAuthResourceLocalPath,
const char* inAuthMoviesDir,
QTSS_ActionFlags inAuthRequestAction,
QTSS_AuthScheme inAuthScheme,
QTSS_RTSPRequestObject ioAuthRequestObject)
{
return (sCallbacks->addr [kAuthenticateCallback]) (inAuthUserName, inAuthResourceLocalPath, inAuthMoviesDir, inAuthRequestAction, inAuthScheme, ioAuthRequestObject);
}
QTSS_Error QTSS_Authorize(QTSS_RTSPRequestObject inAuthRequestObject, char** outAuthRealm, Bool16* outAuthUserAllowed)
{
return (sCallbacks->addr [kAuthorizeCallback]) (inAuthRequestObject, outAuthRealm, outAuthUserAllowed);
}
void QTSS_LockStdLib()
{
(sCallbacks->addr [kLockStdLibCallback]) ();
}
void QTSS_UnlockStdLib()
{
(sCallbacks->addr [kUnlockStdLibCallback]) ();
}

158
APIStubLib/QTSS_Private.h Normal file
View file

@ -0,0 +1,158 @@
/*
*
* @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: QTSS_Private.h
Contains: Implementation-specific structures and typedefs used by the
implementation of QTSS API in the Darwin Streaming Server
*/
#ifndef QTSS_PRIVATE_H
#define QTSS_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "OSHeaders.h"
#include "QTSS.h"
class QTSSModule;
class Task;
typedef QTSS_Error (*QTSS_CallbackProcPtr)(...);
typedef void* (*QTSS_CallbackPtrProcPtr)(...);
enum
{
// Indexes for each callback routine. Addresses of the callback routines get
// placed in an array.
// IMPORTANT: When adding new callbacks, add only to the end of the list and increment the
// kLastCallback value. Inserting or changing the index order will break dynamic modules
// built with another release.
kNewCallback = 0,
kDeleteCallback = 1,
kMillisecondsCallback = 2,
kConvertToUnixTimeCallback = 3,
kAddRoleCallback = 4,
kAddAttributeCallback = 5,
kIDForTagCallback = 6,
kGetAttributePtrByIDCallback = 7,
kGetAttributeByIDCallback = 8,
kSetAttributeByIDCallback = 9,
kWriteCallback = 10,
kWriteVCallback = 11,
kFlushCallback = 12,
kAddServiceCallback = 13,
kIDForServiceCallback = 14,
kDoServiceCallback = 15,
kSendRTSPHeadersCallback = 16,
kAppendRTSPHeadersCallback = 17,
kSendStandardRTSPCallback = 18,
kAddRTPStreamCallback = 19,
kPlayCallback = 20,
kPauseCallback = 21,
kTeardownCallback = 22,
kRequestEventCallback = 23,
kSetIdleTimerCallback = 24,
kOpenFileObjectCallback = 25,
kCloseFileObjectCallback = 26,
kReadCallback = 27,
kSeekCallback = 28,
kAdviseCallback = 29,
kGetNumValuesCallback = 30,
kGetNumAttributesCallback = 31,
kSignalStreamCallback = 32,
kCreateSocketStreamCallback = 33,
kDestroySocketStreamCallback = 34,
kAddStaticAttributeCallback = 35,
kAddInstanceAttributeCallback = 36,
kRemoveInstanceAttributeCallback= 37,
kGetAttrInfoByIndexCallback = 38,
kGetAttrInfoByNameCallback = 39,
kGetAttrInfoByIDCallback = 40,
kGetValueAsStringCallback = 41,
kTypeToTypeStringCallback = 42,
kTypeStringToTypeCallback = 43,
kStringToValueCallback = 44,
kValueToStringCallback = 45,
kRemoveValueCallback = 46,
kRequestGlobalLockCallback = 47,
kIsGlobalLockedCallback = 48,
kUnlockGlobalLock = 49,
kAuthenticateCallback = 50,
kAuthorizeCallback = 51,
kRefreshTimeOutCallback = 52,
kCreateObjectValueCallback = 53,
kCreateObjectTypeCallback = 54,
kLockObjectCallback = 55,
kUnlockObjectCallback = 56,
kSetAttributePtrCallback = 57,
kSetIntervalRoleTimerCallback = 58,
kLockStdLibCallback = 59,
kUnlockStdLibCallback = 60,
kLastCallback = 61
};
typedef struct {
// Callback function pointer array
QTSS_CallbackProcPtr addr [kLastCallback];
} QTSS_Callbacks, *QTSS_CallbacksPtr;
typedef struct
{
UInt32 inServerAPIVersion;
QTSS_CallbacksPtr inCallbacks;
QTSS_StreamRef inErrorLogStream;
UInt32 outStubLibraryVersion;
QTSS_DispatchFuncPtr outDispatchFunction;
} QTSS_PrivateArgs, *QTSS_PrivateArgsPtr;
typedef struct
{
QTSSModule* curModule; // this structure is setup in each thread
QTSS_Role curRole; // before invoking a module in a role. Sometimes
Task* curTask; // this info. helps callback implementation
Bool16 eventRequested;
Bool16 globalLockRequested; // request event with global lock.
Bool16 isGlobalLocked;
SInt64 idleTime; // If a module has requested idle time.
} QTSS_ModuleState, *QTSS_ModuleStatePtr;
QTSS_StreamRef GetErrorLogStream();
#ifdef __cplusplus
}
#endif
#endif

367
APPLE_LICENSE Normal file
View file

@ -0,0 +1,367 @@
APPLE PUBLIC SOURCE LICENSE
Version 2.0 - August 6, 2003
Please read this License carefully before downloading this software.
By downloading or using this software, you are agreeing to be bound by
the terms of this License. If you do not or cannot agree to the terms
of this License, please do not download or use the software.
1. General; Definitions. This License applies to any program or other
work which Apple Computer, Inc. ("Apple") makes publicly available and
which contains a notice placed by Apple identifying such program or
work as "Original Code" and stating that it is subject to the terms of
this Apple Public Source License version 2.0 ("License"). As used in
this License:
1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
the grantor of rights, (i) claims of patents that are now or hereafter
acquired, owned by or assigned to Apple and (ii) that cover subject
matter contained in the Original Code, but only to the extent
necessary to use, reproduce and/or distribute the Original Code
without infringement; and (b) in the case where You are the grantor of
rights, (i) claims of patents that are now or hereafter acquired,
owned by or assigned to You and (ii) that cover subject matter in Your
Modifications, taken alone or in combination with Original Code.
1.2 "Contributor" means any person or entity that creates or
contributes to the creation of Modifications.
1.3 "Covered Code" means the Original Code, Modifications, the
combination of Original Code and any Modifications, and/or any
respective portions thereof.
1.4 "Externally Deploy" means: (a) to sublicense, distribute or
otherwise make Covered Code available, directly or indirectly, to
anyone other than You; and/or (b) to use Covered Code, alone or as
part of a Larger Work, in any way to provide a service, including but
not limited to delivery of content, through electronic communication
with a client other than You.
1.5 "Larger Work" means a work which combines Covered Code or portions
thereof with code not governed by the terms of this License.
1.6 "Modifications" mean any addition to, deletion from, and/or change
to, the substance and/or structure of the Original Code, any previous
Modifications, the combination of Original Code and any previous
Modifications, and/or any respective portions thereof. When code is
released as a series of files, a Modification is: (a) any addition to
or deletion from the contents of a file containing Covered Code;
and/or (b) any new file or other representation of computer program
statements that contains any part of Covered Code.
1.7 "Original Code" means (a) the Source Code of a program or other
work as originally made available by Apple under this License,
including the Source Code of any updates or upgrades to such programs
or works made available by Apple under this License, and that has been
expressly identified by Apple as such in the header file(s) of such
work; and (b) the object code compiled from such Source Code and
originally made available by Apple under this License.
1.8 "Source Code" means the human readable form of a program or other
work that is suitable for making modifications to it, including all
modules it contains, plus any associated interface definition files,
scripts used to control compilation and installation of an executable
(object code).
1.9 "You" or "Your" means an individual or a legal entity exercising
rights under this License. For legal entities, "You" or "Your"
includes any entity which controls, is controlled by, or is under
common control with, You, where "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of fifty percent
(50%) or more of the outstanding shares or beneficial ownership of
such entity.
2. Permitted Uses; Conditions & Restrictions. Subject to the terms
and conditions of this License, Apple hereby grants You, effective on
the date You accept this License and download the Original Code, a
world-wide, royalty-free, non-exclusive license, to the extent of
Apple's Applicable Patent Rights and copyrights covering the Original
Code, to do the following:
2.1 Unmodified Code. You may use, reproduce, display, perform,
internally distribute within Your organization, and Externally Deploy
verbatim, unmodified copies of the Original Code, for commercial or
non-commercial purposes, provided that in each instance:
(a) You must retain and reproduce in all copies of Original Code the
copyright and other proprietary notices and disclaimers of Apple as
they appear in the Original Code, and keep intact all notices in the
Original Code that refer to this License; and
(b) You must include a copy of this License with every copy of Source
Code of Covered Code and documentation You distribute or Externally
Deploy, and You may not offer or impose any terms on such Source Code
that alter or restrict this License or the recipients' rights
hereunder, except as permitted under Section 6.
2.2 Modified Code. You may modify Covered Code and use, reproduce,
display, perform, internally distribute within Your organization, and
Externally Deploy Your Modifications and Covered Code, for commercial
or non-commercial purposes, provided that in each instance You also
meet all of these conditions:
(a) You must satisfy all the conditions of Section 2.1 with respect to
the Source Code of the Covered Code;
(b) You must duplicate, to the extent it does not already exist, the
notice in Exhibit A in each file of the Source Code of all Your
Modifications, and cause the modified files to carry prominent notices
stating that You changed the files and the date of any change; and
(c) If You Externally Deploy Your Modifications, You must make
Source Code of all Your Externally Deployed Modifications either
available to those to whom You have Externally Deployed Your
Modifications, or publicly available. Source Code of Your Externally
Deployed Modifications must be released under the terms set forth in
this License, including the license grants set forth in Section 3
below, for as long as you Externally Deploy the Covered Code or twelve
(12) months from the date of initial External Deployment, whichever is
longer. You should preferably distribute the Source Code of Your
Externally Deployed Modifications electronically (e.g. download from a
web site).
2.3 Distribution of Executable Versions. In addition, if You
Externally Deploy Covered Code (Original Code and/or Modifications) in
object code, executable form only, You must include a prominent
notice, in the code itself as well as in related documentation,
stating that Source Code of the Covered Code is available under the
terms of this License with information on how and where to obtain such
Source Code.
2.4 Third Party Rights. You expressly acknowledge and agree that
although Apple and each Contributor grants the licenses to their
respective portions of the Covered Code set forth herein, no
assurances are provided by Apple or any Contributor that the Covered
Code does not infringe the patent or other intellectual property
rights of any other entity. Apple and each Contributor disclaim any
liability to You for claims brought by any other entity based on
infringement of intellectual property rights or otherwise. As a
condition to exercising the rights and licenses granted hereunder, You
hereby assume sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party patent
license is required to allow You to distribute the Covered Code, it is
Your responsibility to acquire that license before distributing the
Covered Code.
3. Your Grants. In consideration of, and as a condition to, the
licenses granted to You under this License, You hereby grant to any
person or entity receiving or distributing Covered Code under this
License a non-exclusive, royalty-free, perpetual, irrevocable license,
under Your Applicable Patent Rights and other intellectual property
rights (other than patent) owned or controlled by You, to use,
reproduce, display, perform, modify, sublicense, distribute and
Externally Deploy Your Modifications of the same scope and extent as
Apple's licenses under Sections 2.1 and 2.2 above.
4. Larger Works. You may create a Larger Work by combining Covered
Code with other code not governed by the terms of this License and
distribute the Larger Work as a single product. In each such instance,
You must make sure the requirements of this License are fulfilled for
the Covered Code or any portion thereof.
5. Limitations on Patent License. Except as expressly stated in
Section 2, no other patent rights, express or implied, are granted by
Apple herein. Modifications and/or Larger Works may require additional
patent licenses from Apple which Apple may grant in its sole
discretion.
6. Additional Terms. You may choose to offer, and to charge a fee for,
warranty, support, indemnity or liability obligations and/or other
rights consistent with the scope of the license granted herein
("Additional Terms") to one or more recipients of Covered Code.
However, You may do so only on Your own behalf and as Your sole
responsibility, and not on behalf of Apple or any Contributor. You
must obtain the recipient's agreement that any such Additional Terms
are offered by You alone, and You hereby agree to indemnify, defend
and hold Apple and every Contributor harmless for any liability
incurred by or claims asserted against Apple or such Contributor by
reason of any such Additional Terms.
7. Versions of the License. Apple may publish revised and/or new
versions of this License from time to time. Each version will be given
a distinguishing version number. Once Original Code has been published
under a particular version of this License, You may continue to use it
under the terms of that version. You may also choose to use such
Original Code under the terms of any subsequent version of this
License published by Apple. No one other than Apple has the right to
modify the terms applicable to Covered Code created under this
License.
8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
part pre-release, untested, or not fully tested works. The Covered
Code may contain errors that could cause failures or loss of data, and
may be incomplete or contain inaccuracies. You expressly acknowledge
and agree that use of the Covered Code, or any portion thereof, is at
Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
You acknowledge that the Covered Code is not intended for use in the
operation of nuclear facilities, aircraft navigation, communication
systems, or air traffic control machines in which case the failure of
the Covered Code could lead to death, personal injury, or severe
physical or environmental damage.
9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
TO YOU. In no event shall Apple's total liability to You for all
damages (other than as may be required by applicable law) under this
License exceed the amount of fifty dollars ($50.00).
10. Trademarks. This License does not grant any rights to use the
trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
"QuickTime", "QuickTime Streaming Server" or any other trademarks,
service marks, logos or trade names belonging to Apple (collectively
"Apple Marks") or to any trademark, service mark, logo or trade name
belonging to any Contributor. You agree not to use any Apple Marks in
or as part of the name of products derived from the Original Code or
to endorse or promote products derived from the Original Code other
than as expressly permitted by and in strict compliance at all times
with Apple's third party trademark usage guidelines which are posted
at http://www.apple.com/legal/guidelinesfor3rdparties.html.
11. Ownership. Subject to the licenses granted under this License,
each Contributor retains all rights, title and interest in and to any
Modifications made by such Contributor. Apple retains all rights,
title and interest in and to the Original Code and any Modifications
made by or on behalf of Apple ("Apple Modifications"), and such Apple
Modifications will not be automatically subject to this License. Apple
may, at its sole discretion, choose to license such Apple
Modifications under this License, or on different terms from those
contained in this License or may choose not to license them at all.
12. Termination.
12.1 Termination. This License and the rights granted hereunder will
terminate:
(a) automatically without notice from Apple if You fail to comply with
any term(s) of this License and fail to cure such breach within 30
days of becoming aware of such breach;
(b) immediately in the event of the circumstances described in Section
13.5(b); or
(c) automatically without notice from Apple if You, at any time during
the term of this License, commence an action for patent infringement
against Apple; provided that Apple did not first commence
an action for patent infringement against You in that instance.
12.2 Effect of Termination. Upon termination, You agree to immediately
stop any further use, reproduction, modification, sublicensing and
distribution of the Covered Code. All sublicenses to the Covered Code
which have been properly granted prior to termination shall survive
any termination of this License. Provisions which, by their nature,
should remain in effect beyond the termination of this License shall
survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
12.2 and 13. No party will be liable to any other for compensation,
indemnity or damages of any sort solely as a result of terminating
this License in accordance with its terms, and termination of this
License will be without prejudice to any other right or remedy of
any party.
13. Miscellaneous.
13.1 Government End Users. The Covered Code is a "commercial item" as
defined in FAR 2.101. Government software and technical data rights in
the Covered Code include only those rights customarily provided to the
public as defined in this License. This customary commercial license
in technical data and software is provided in accordance with FAR
12.211 (Technical Data) and 12.212 (Computer Software) and, for
Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
Commercial Items) and 227.7202-3 (Rights in Commercial Computer
Software or Computer Software Documentation). Accordingly, all U.S.
Government End Users acquire Covered Code with only those rights set
forth herein.
13.2 Relationship of Parties. This License will not be construed as
creating an agency, partnership, joint venture or any other form of
legal association between or among You, Apple or any Contributor, and
You will not represent to the contrary, whether expressly, by
implication, appearance or otherwise.
13.3 Independent Development. Nothing in this License will impair
Apple's right to acquire, license, develop, have others develop for
it, market and/or distribute technology or products that perform the
same or similar functions as, or otherwise compete with,
Modifications, Larger Works, technology or products that You may
develop, produce, market or distribute.
13.4 Waiver; Construction. Failure by Apple or any Contributor to
enforce any provision of this License will not be deemed a waiver of
future enforcement of that or any other provision. Any law or
regulation which provides that the language of a contract shall be
construed against the drafter will not apply to this License.
13.5 Severability. (a) If for any reason a court of competent
jurisdiction finds any provision of this License, or portion thereof,
to be unenforceable, that provision of the License will be enforced to
the maximum extent permissible so as to effect the economic benefits
and intent of the parties, and the remainder of this License will
continue in full force and effect. (b) Notwithstanding the foregoing,
if applicable law prohibits or restricts You from fully and/or
specifically complying with Sections 2 and/or 3 or prevents the
enforceability of either of those Sections, this License will
immediately terminate and You must immediately discontinue any use of
the Covered Code and destroy all copies of it that are in your
possession or control.
13.6 Dispute Resolution. Any litigation or other dispute resolution
between You and Apple relating to this License shall take place in the
Northern District of California, and You and Apple hereby consent to
the personal jurisdiction of, and venue in, the state and federal
courts within that District with respect to this License. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded.
13.7 Entire Agreement; Governing Law. This License constitutes the
entire agreement between the parties with respect to the subject
matter hereof. This License shall be governed by the laws of the
United States and the State of California, except that body of
California law concerning conflicts of law.
Where You are located in the province of Quebec, Canada, the following
clause applies: The parties hereby confirm that they have requested
that this License and all related documents be drafted in English. Les
parties ont exige que le present contrat et tous les documents
connexes soient rediges en anglais.
EXHIBIT A.
"Portions Copyright (c) 1999-2003 Apple Computer, 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."

139
AtomicLib/README Normal file
View file

@ -0,0 +1,139 @@
/* --- Version 4.0 --- 12-Feb-1999 --- */
/*
* Include the following routines :
* atomic_or()
* scaledtimestamp()
*
* Updated the copyright.
*/
/*
* unsigned int atomic_or(unsigned int *area, unsigned int mask)
*
* Atomically or the mask into *area.
* Returns the old value.
*/
/*
* long long scaledtimestamp(double scale)
*
* Read the PPC timebase. Convert the time base value based on
* scale.
*
* Caveat: scale can not be 0, NaN, Inf. It's upto the caller
* to validate scale before calling this.
*/
/* --- Version 3.0 --- 23-Oct-1998 --- */
/*
* Made the headers c++ friendly
* Build a static library with dynamic code generation.
* If you make a copy of the libatomic.a do not forget to run "ranlib".
*/
/* --- Version 2.0 --- 23-Oct-1998 --- */
/*
* Added routines described in timestamp.h
* Test program for these is in hmi.c
*/
/* --- Version 1.0 --- 12-Oct-1998 --- */
/*
* void spin_lock_init(spin_lock_t)
*
* Initialize a spin lock.
* These locks should be cache aligned and a multiple of cache size.
*/
/*
* void spin_lock_unlock(spin_lock_t)
*
* Unconditionally release lock.
*/
/*
* unsigned int spin_lock_lock(spin_lock_t)
*
* Try to acquire spin-lock. Return success (1).
*/
/*
* unsigned int spin_lock_bit(spin_lock_t, unsigned int bits)
*
* Try to acquire spin-lock. The second parameter is the bit mask to
* test and set. multiple bits may be set.
* Return success (1).
*/
/*
* unsigned int spin_unlock_bit(spin_lock_t, unsigned int bits)
*
* Release bit based spin-lock. The second parameter is the bit mask to
* clear. Multiple bits may be cleared.
*/
/*
* unsigned int spin_lock_try(spin_lock_t)
*
* Try to acquire spin-lock. Return success (1) or failure (0).
*/
/*
* unsigned int spin_lock_held(spin_lock_t)
*
* Return 1 if lock is held
* N.B. Racy, of course.
*/
/*
* unsigned int compare_and_store(unsigned int oval,
* unsigned int nval, unsigned int *area)
*
* Compare oval to area if equal, store nval, and return true
* else return false and no store
* This is an atomic operation
*/
/*
* unsigned int atomic_add(unsigned int *area, int val)
*
* Atomically add the second parameter to the first.
* Returns the result.
*/
/*
* unsigned int atomic_sub(unsigned int *area, int val)
*
* Atomically subtract the second parameter from the first.
* Returns the result.
*/
/*
* void queue_atomic(unsigned int * anchor,
* unsigned int * elem, unsigned int disp)
*
* Atomically inserts the element at the head of the list
* anchor is the pointer to the first element
* element is the pointer to the element to insert
* disp is the displacement into the element to the chain pointer
*/
/*
* void queue_atomic_list(unsigned int * anchor,
* unsigned int * first, unsigned int * last,
* unsigned int disp)
*
* Atomically inserts the list of elements at the head of the list
* anchor is the pointer to the first element
* first is the pointer to the first element to insert
* last is the pointer to the last element to insert
* disp is the displacement into the element to the chain pointer
*/
/*
* unsigned int *dequeue_atomic(unsigned int *anchor, unsigned int disp)
*
* Atomically removes the first element in a list and returns it.
* anchor is the pointer to the first element
* disp is the displacement into the element to the chain pointer
* Returns element if found, 0 if empty.
*/

97
AtomicLib/atomic.h Normal file
View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
*
* @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@
*/
/*
*
* History:
* 11-Feb-1999 Umesh Vaishampayan (umeshv@apple.com)
* Added atomic_or().
*
* 26-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
* Made the header c++ friendly.
*
* 12-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
* Changed simple_ to spin_ so as to coexist with cthreads till
* the merge to the system framework.
*
* 8-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
* Created from the kernel code to be in a dynamic shared library.
* Kernel code created by: Bill Angell (angell@apple.com)
*/
#ifndef _ATOMIC_H_
#define _ATOMIC_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Locking routines */
struct spin_lock { /* sizeof cache line */
unsigned int lock_data;
unsigned int pad[7];
};
typedef struct spin_lock *spin_lock_t;
extern void spin_lock_init(spin_lock_t);
extern void spin_lock_unlock(spin_lock_t);
extern unsigned int spin_lock_lock(spin_lock_t);
extern unsigned int spin_lock_bit(spin_lock_t, unsigned int bits);
extern unsigned int spin_unlock_bit(spin_lock_t, unsigned int bits);
extern unsigned int spin_lock_try(spin_lock_t);
extern unsigned int spin_lock_held(spin_lock_t);
/* Other atomic routines */
extern unsigned int compare_and_store(unsigned int oval,
unsigned int nval, unsigned int *area);
extern unsigned int atomic_add(unsigned int *area, int val);
extern unsigned int atomic_or(unsigned int *area, unsigned int mask);
extern unsigned int atomic_sub(unsigned int *area, int val);
extern void queue_atomic(unsigned int *anchor,
unsigned int *elem, unsigned int disp);
extern void queue_atomic_list(unsigned int *anchor,
unsigned int *first, unsigned int *last,
unsigned int disp);
extern unsigned int *dequeue_atomic(unsigned int *anchor, unsigned int disp);
#ifdef __cplusplus
}
#endif
#endif /* _ATOMIC_H_ */

Some files were not shown because too many files have changed in this diff Show more