//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*--
Module Name: BUFFIO.H
Abstract: Buffer handling class & socket IO helpers
--*/


// returned from the socket IO functions
typedef enum {
	INPUT_OK = 0,
	INPUT_TIMEOUT = 1,
	INPUT_ERROR = 2,
	INPUT_NOCHANGE = 3
	//INPUT_EOF = 2,
}
HRINPUT;

class CHttpRequest;		// forward declaration
typedef struct _SSL_INFO SSL_INFO;

int MySelect(SOCKET sock, DWORD dwMillisecs);

class CBuffer {
public:
	PSTR	m_pszBuf;
	int	    m_iSize;
	int	    m_iNextOut;
	int	    m_iNextIn;
	int     m_iNextInFollow;   // next place to read, needed by raw read filters.  
	int     m_iNextDecrypt;    // marks the edge of unencrypted data during an SSL connection
	int     m_iNextUnparsedChunk; // Next place to parse a header on chunked upload.
	CHAR    m_chSaved;	// Used by the parser

	// Handles case where we get multiple HTTP requests on one packet.
	// Handle 1st HTTP request 1st and save info about where 2nd request begins
	// for when we handle it.
	int     m_iNextRequestBegin;    // where does it begin in m_pszBuf?
	int     m_iNextRequestSize;     // number of bytes of next request(s) we've read off wire already.
	CHAR    m_chNextRequestSaved;   // 1st char in next request (byte set to \0 so ISAPIs won't read it)

	// There's cases (like ISAPI read filter or ReadClient) where we can mess
	// up the state of the HTTP request IF m_iNextRequestBegin != 0.  If person
	// writes their ISAPI correctly it won't be an issue, but if they don't data could
	// end up coming in out of order during a keep-alive, so in this case we'll 
	// ignore anything we have.  Not ideal, but ideally clients shouldn't be sending 
	// multiple HTTP requests to in one packet to an HTTP 1.0 server in 1st place.
	void    InvalidateNextRequestIfAlreadyRead() { m_iNextRequestBegin = 0; }

	// Functions
	BOOL AllocMem(DWORD dwLen);
	BOOL NextToken(PSTR* ppszTok, int* piLen, BOOL fWS, BOOL fColon=FALSE);

	CBuffer() {
		memset(this, 0, sizeof(*this));
	}

	~CBuffer() {
		MyFree(m_pszBuf);
	}

	//  Used to reset a buffer through the course of 1 session, uses 
	//  same allocated mem block. (don't change m_iSize, leave m_iNextRequestXXX alone)
	void Reset() {
		m_iNextDecrypt = m_iNextInFollow = m_iNextOut = m_iNextIn = m_iNextUnparsedChunk = m_chSaved = 0;
	}

	// accessors
	DWORD Count() { return m_iNextIn - m_iNextOut; } 	
	BOOL  HasPostData()    { return (m_iNextIn > m_iNextOut);  }
	PBYTE Data()  { return (PBYTE)(m_pszBuf + m_iNextOut); }
	DWORD UnaccessedCount() { return m_iNextIn - m_iNextInFollow; }  // this is data that hasn't been modified yet, for filters
	DWORD AvailableBufferSize()  { return m_iSize - m_iNextInFollow; }  // size of buffer, used by filters
	PBYTE FilterRawData()  { return (PBYTE)(m_pszBuf + m_iNextInFollow); }
	PSTR Headers() { return m_pszBuf; }  // Http headers are at beginning of buf
	DWORD GetINextOut()  { return m_iNextOut; }
	BOOL TrimWhiteSpace(BOOL *pfAbort);
	PBYTE GetNextChunkToParse() { return (PBYTE)(m_pszBuf + m_iNextUnparsedChunk); }


	// input functions
	HRINPUT RecvToBuf(SOCKET sock, DWORD dwLength, BOOL fFromFilter, BOOL fFirstPostRead, CHttpRequest *pRequest,BOOL fSSLRenegotiate);
	HRINPUT RecvHeaders(SOCKET sock, CHttpRequest *pRequest) {
		return RecvToBuf(sock, (DWORD)-1, FALSE,FALSE,pRequest,FALSE);
	};

	HRINPUT RecvBody(SOCKET sock, DWORD dwLength, BOOL fFromFilter, BOOL fFirstPostRead, CHttpRequest *pRequest, BOOL fSSLRenegotiate) { 
		DEBUGCHK(m_pszBuf && m_iSize);
		return RecvToBuf(sock, dwLength, fFromFilter,fFirstPostRead,pRequest,fSSLRenegotiate);
	};
	BOOL NextTokenWS(PSTR* ppszTok, int* piLen)  { return NextToken(ppszTok, piLen, TRUE); }
	BOOL NextTokenEOL(PSTR* ppszTok, int* piLen) { return NextToken(ppszTok, piLen, FALSE); }
	BOOL NextTokenHeaderName(PSTR* ppszTok, int* piLen) { return NextToken(ppszTok, piLen, FALSE, TRUE); }
	BOOL NextLine();
	BOOL AddHeader(PCSTR pszName, PCSTR pszValue, BOOL fAddColon=FALSE);

	BOOL AppendCHAR(const CHAR cAppend) {
		return AppendData((char *)&cAppend,sizeof(CHAR));
	}

	BOOL EncodeURL(PCSTR pszData);
	BOOL ParseInitialChunkedUpload(CHttpRequest *pRequest, SOCKET sock, DWORD *pdwBytesInNextChunk);

#if defined (DEBUG)
	void WriteBufferToDebugOut(BOOL fPrintAll);
	void BufferConsistencyChecks(void) {
		DEBUGCHK(m_iNextIn >= m_iNextOut);
		DEBUGCHK(m_iNextIn <= m_iSize);
		DEBUGCHK(m_iNextIn >= m_iNextDecrypt);
		DEBUGCHK(m_iNextIn >= m_iNextInFollow);
	}
#else
	void WriteBufferToDebugOut(BOOL fPrintAll) {; }
	void BufferConsistencyChecks(void) { ; }
#endif
	
	// output functions
	BOOL AppendData(PCSTR pszData, int iLen);
	BOOL SendBuffer(CHttpRequest *pRequest);
	BOOL FilterDataUpdate(PVOID pvData, DWORD cbData, BOOL fModifiedPointer);
};
