/*
 *
 * @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:       RTSPRequestStream.cpp

    Contains:   Implementation of RTSPRequestStream class. 
                    
    

*/


#include "RTSPRequestStream.h"
#include "StringParser.h"
#include "OSMemory.h"
#include "base64.h"
#include "OSArrayObjectDeleter.h"
#include "OS.h"

#include <errno.h>

#define READ_DEBUGGING 0

RTSPRequestStream::RTSPRequestStream(TCPSocket* sock)
:   fSocket(sock),
    fRetreatBytes(0), 
    fRetreatBytesRead(0),
    fCurOffset(0),
    fEncodedBytesRemaining(0),
    fRequest(fRequestBuffer, 0),
    fRequestPtr(NULL),
    fDecode(false),
    fPrintRTSP(false)
{}

void RTSPRequestStream::SnarfRetreat( RTSPRequestStream &fromRequest )
{
    // Simplest thing to do is to just completely blow away everything in this current
    // stream, and replace it with the retreat bytes from the other stream.
    fRequestPtr = NULL;
    Assert(fRetreatBytes < kRequestBufferSizeInBytes);
    fRetreatBytes = fromRequest.fRetreatBytes;
    fEncodedBytesRemaining = fCurOffset = fRequest.Len = 0;
    ::memcpy(&fRequestBuffer[0], fromRequest.fRequest.Ptr + fromRequest.fRequest.Len, fromRequest.fRetreatBytes);
}

QTSS_Error RTSPRequestStream::ReadRequest()
{
    while (true)
    {
        UInt32 newOffset = 0;
        
        //If this is the case, we already HAVE a request on this session, and we now are done
        //with the request and want to move onto the next one. The first thing we should do
        //is check whether there is any lingering data in the stream. If there is, the parent
        //session believes that is part of a new request
        if (fRequestPtr != NULL)
        {
            fRequestPtr = NULL;//flag that we no longer have a complete request
            
            // Take all the retreated leftover data and move it to the beginning of the buffer
            if ((fRetreatBytes > 0) && (fRequest.Len > 0))
                ::memmove(fRequest.Ptr, fRequest.Ptr + fRequest.Len + fRetreatBytesRead, fRetreatBytes);

            // if we are decoding, we need to also move over the remaining encoded bytes
            // to the right position in the fRequestBuffer
            if (fEncodedBytesRemaining > 0)
            {
                //Assert(fEncodedBytesRemaining < 4);
                
                // The right position is at fRetreatBytes offset in the request buffer. The reason for this is:
                //  1) We need to find a place in the request buffer where we know we have enough space to store
                //  fEncodedBytesRemaining. fRetreatBytes + fEncodedBytesRemaining will always be less than
                //  kRequestBufferSize because all this data must have been in the same request buffer, together, at one point.
                //
                //  2) We need to make sure that there is always more data in the RequestBuffer than in the decoded
                //  request buffer, otherwise we could overrun the decoded request buffer (we bounds check on the encoded
                //  buffer, not the decoded buffer). Leaving fRetreatBytes as empty space in the request buffer ensures
                //  that this principle is maintained. 
                ::memmove(&fRequestBuffer[fRetreatBytes], &fRequestBuffer[fCurOffset - fEncodedBytesRemaining], fEncodedBytesRemaining);
                fCurOffset = fRetreatBytes + fEncodedBytesRemaining;
                Assert(fCurOffset < kRequestBufferSizeInBytes);
            }
            else
                fCurOffset = fRetreatBytes;
                
            newOffset = fRequest.Len = fRetreatBytes;
            fRetreatBytes = fRetreatBytesRead = 0;
        }

        // We don't have any new data, so try and get some
        if (newOffset == 0)
        {
            if (fRetreatBytes > 0)
            {
                // This will be true if we've just snarfed another input stream, in which case the encoded data
                // is copied into our request buffer, and its length is tracked in fRetreatBytes.
                // If this is true, just fall through and decode the data.
                newOffset = fRetreatBytes;
                fRetreatBytes = 0;
                Assert(fEncodedBytesRemaining == 0);
            }
            else
            {
                // We don't have any new data, get some from the socket...
                QTSS_Error sockErr = fSocket->Read(&fRequestBuffer[fCurOffset], 
                                                    (kRequestBufferSizeInBytes - fCurOffset) - 1, &newOffset);
                //assume the client is dead if we get an error back
                if (sockErr == EAGAIN)
                    return QTSS_NoErr;
                if (sockErr != QTSS_NoErr)
                {
                    Assert(!fSocket->IsConnected());
                    return sockErr;
                }
            }   

            if (fDecode)
            {
                // If we need to decode this data, do it now.
                Assert(fCurOffset >= fEncodedBytesRemaining);
                QTSS_Error decodeErr = this->DecodeIncomingData(&fRequestBuffer[fCurOffset - fEncodedBytesRemaining],
                                                                    newOffset + fEncodedBytesRemaining);
                // If the above function returns an error, it is because we've
                // encountered some non-base64 data in the stream. We can process
                // everything up until that point, but all data after this point will
                // be ignored.
                if (decodeErr == QTSS_NoErr)
                    Assert(fEncodedBytesRemaining < 4);
            }
            else
                fRequest.Len += newOffset;
            Assert(fRequest.Len < kRequestBufferSizeInBytes);
            fCurOffset += newOffset;
        }
        Assert(newOffset > 0);

        // See if this is an interleaved data packet
        if ('$' == *(fRequest.Ptr))
        {   
            if (fRequest.Len < 4)
                continue;
            UInt16* dataLenP = (UInt16*)fRequest.Ptr;
            UInt32 interleavedPacketLen = ntohs(dataLenP[1]) + 4;
            if (interleavedPacketLen > fRequest.Len)
                continue;
                
            //put back any data that is not part of the header
            fRetreatBytes += fRequest.Len - interleavedPacketLen;
            fRequest.Len = interleavedPacketLen;
        
            fRequestPtr = &fRequest;
            fIsDataPacket = true;
            return QTSS_RequestArrived;
        }
        fIsDataPacket = false;

        if (fPrintRTSP)
        {
            DateBuffer theDate;
            DateTranslator::UpdateDateBuffer(&theDate, 0); // get the current GMT date and time
			qtss_printf("\n\n#C->S:\n#time: ms=%lu date=%s\n", (UInt32) OS::StartTimeMilli_Int(), theDate.GetDateBuffer());

            if (fSocket != NULL)    
            {
                UInt16 serverPort = fSocket->GetLocalPort();
                UInt16 clientPort = fSocket->GetRemotePort();    
                StrPtrLen* theLocalAddrStr = fSocket->GetLocalAddrStr();
                StrPtrLen* theRemoteAddrStr = fSocket->GetRemoteAddrStr();
                if (theLocalAddrStr != NULL)
                {	qtss_printf("#server: ip="); theLocalAddrStr->PrintStr(); qtss_printf(" port=%u\n" , serverPort );
                }
                else
              	{	qtss_printf("#server: ip=NULL port=%u\n" , serverPort );
              	}
               	
                if (theRemoteAddrStr != NULL)
                {	qtss_printf("#client: ip="); theRemoteAddrStr->PrintStr(); qtss_printf(" port=%u\n" , clientPort );
                }
            	else
            	{	qtss_printf("#client: ip=NULL port=%u\n" , clientPort );
            	}

            }

			StrPtrLen str(fRequest);
			str.PrintStrEOL("\n\r\n", "\n");// print the request but stop on \n\r\n and add a \n afterwards.
        }
        
        //use a StringParser object to search for a double EOL, which signifies the end of
        //the header.
        Bool16 weAreDone = false;
        StringParser headerParser(&fRequest);
        
        UInt16 lcount = 0;
        while (headerParser.GetThruEOL(NULL))
        {
            lcount++;
            if (headerParser.ExpectEOL())
            {
                //The legal end-of-header sequences are \r\r, \r\n\r\n, & \n\n. NOT \r\n\r!
                //If the packets arrive just a certain way, we could get here with the latter
                //combo, and not wait for a final \n.
                if ((headerParser.GetDataParsedLen() > 2) &&
                    (memcmp(headerParser.GetCurrentPosition() - 3, "\r\n\r", 3) == 0))
                    continue;
                weAreDone = true;
                break;
            }
            else if (lcount == 1) {
                // if this request is actually a ShoutCast password it will be 
                // in the form of "xxxxxx\r" where "xxxxx" is the password.
                // If we get a 1st request line ending in \r with no blanks we will
                // assume that this is the end of the request.
                UInt16 flag = 0;
                UInt16 i = 0;
                for (i=0; i<fRequest.Len; i++)
                {
                    if (fRequest.Ptr[i] == ' ')
                        flag++;
                }
                if (flag == 0)
                {
                    weAreDone = true;
                    break;
                }
            }
        }
        
        //weAreDone means we have gotten a full request
        if (weAreDone)
        {
            //put back any data that is not part of the header
            fRequest.Len -= headerParser.GetDataRemaining();
            fRetreatBytes += headerParser.GetDataRemaining();
            
            fRequestPtr = &fRequest;
            return QTSS_RequestArrived;
        }
        
        //check for a full buffer
        if (fCurOffset == kRequestBufferSizeInBytes - 1)
        {
            fRequestPtr = &fRequest;
            return E2BIG;
        }
    }
}

QTSS_Error RTSPRequestStream::Read(void* ioBuffer, UInt32 inBufLen, UInt32* outLengthRead)
{
    UInt32 theLengthRead = 0;
    UInt8* theIoBuffer = (UInt8*)ioBuffer;
    
    //
    // If there are retreat bytes available, read them first.
    if (fRetreatBytes > 0)
    {
        theLengthRead = fRetreatBytes;
        if (inBufLen < theLengthRead)
            theLengthRead = inBufLen;
        
        ::memcpy(theIoBuffer, fRequest.Ptr + fRequest.Len + fRetreatBytesRead, theLengthRead);
        
        //
        // We should not update fRequest.Len even though we've read some of the retreat bytes.
        // fRequest.Len always refers to the length of the request header. Instead, we
        // have a separate variable, fRetreatBytesRead
        fRetreatBytes -= theLengthRead;
        fRetreatBytesRead += theLengthRead;
#if READ_DEBUGGING
        qtss_printf("In RTSPRequestStream::Read: Got %d Retreat Bytes\n",theLengthRead);
#endif  
    }

    //
    // If there is still space available in ioBuffer, continue. Otherwise, we can return now
    if (theLengthRead == inBufLen)
    {
        if (outLengthRead != NULL)
            *outLengthRead = theLengthRead;
        return QTSS_NoErr;
    }
    
    //
    // Read data directly from the socket and place it in our buffer
    UInt32 theNewOffset = 0;
    QTSS_Error theErr = fSocket->Read(&theIoBuffer[theLengthRead], inBufLen - theLengthRead, &theNewOffset);
#if READ_DEBUGGING
    qtss_printf("In RTSPRequestStream::Read: Got %d bytes off Socket\n",theNewOffset);
#endif  
    if (outLengthRead != NULL)
        *outLengthRead = theNewOffset + theLengthRead;
        
    return theErr;
}

QTSS_Error RTSPRequestStream::DecodeIncomingData(char* inSrcData, UInt32 inSrcDataLen)
{
    Assert(fRetreatBytes == 0);
    
    if (fRequest.Ptr == &fRequestBuffer[0])
    {
        fRequest.Ptr = NEW char[kRequestBufferSizeInBytes];
        fRequest.Len = 0;
    }
    
    // We always decode up through the last chunk of 4.
    fEncodedBytesRemaining = inSrcDataLen & 3;
    
    // Let our friendly Base64Decode function know this by NULL terminating at that point
    UInt32 bytesToDecode = inSrcDataLen - fEncodedBytesRemaining;
    char endChar = inSrcData[bytesToDecode];
    inSrcData[bytesToDecode] = '\0';
    
    UInt32 encodedBytesConsumed = 0;
    
    // Loop until the whole load is decoded
    while (encodedBytesConsumed < bytesToDecode)
    {
        Assert((encodedBytesConsumed & 3) == 0);
        Assert((bytesToDecode & 3) == 0);

        UInt32 bytesDecoded = Base64decode(fRequest.Ptr + fRequest.Len, inSrcData + encodedBytesConsumed);

        // If bytesDecoded is 0, we will end up being in an endless loop. The
        // base64 must be corrupt, so let's just return an error and abort
        if (bytesDecoded == 0)
        {
            //Assert(0);
            return QTSS_BadArgument;
        }
        
        fRequest.Len += bytesDecoded;

        // Assuming the stream is valid, the # of encoded bytes we just consumed is
        // 4/3rds of the number of decoded bytes returned by the decode function,
        // rounded up to the nearest multiple of 4.
        encodedBytesConsumed += (bytesDecoded / 3) * 4;
        if ((bytesDecoded % 3) > 0)
            encodedBytesConsumed += 4;
        
    }
    
    // Make sure to replace the sacred endChar
    inSrcData[bytesToDecode] = endChar;

    Assert(fRequest.Len < kRequestBufferSizeInBytes);
    Assert(encodedBytesConsumed == bytesToDecode);
    
    return QTSS_NoErr;
}

