/*
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * 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.
 * 
 * @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
}

