//
// 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: ISAPI.CPP
Abstract: ISAPI handling code
--*/

#include "httpd.h"

// Prefix for GetServerVariable for generic HTTP header retrieval, so "HTTPD_TEST" gets HTTP header TEST.
const char cszHTTP[] = "HTTP_";
const DWORD dwHTTP   = CONSTSIZEOF(cszHTTP);


// This function is used when looking through HTTP headers for HTTP_VARIABLE.  We
// can't use strcmp because headers (for most part) have words separated by "-",
// the pszVar format has vars separated by "_".  IE pszVar=HTTP_ACCEPT_LANGUAGE
// should return for us HTTP header "ACCEPT-LANGUAGE"
BOOL GetVariableStrCmp(PSTR pszHeader, PSTR pszVar, DWORD dwLen) 
{
    BOOL fRet = FALSE;

    for (DWORD i = 0; i < dwLen; i++)  
    {
        if ( (tolower(*pszHeader) != tolower(*pszVar)) &&
             (*pszVar != '_' && *pszHeader != '-'))  
        {
             goto done;
        }
        pszHeader++;
        pszVar++;
    }
    fRet = (*pszHeader == ':');

done:
    return fRet;
}

BOOL CHttpRequest::GetServerVariable(PSTR pszVar, PVOID pvOutBuf, PDWORD pdwOutSize, BOOL fFromFilter) 
{
    DWORD dwLen;
    char szBuf[4096];
    PSTR pszRet = (PSTR)-1;
    PSTR pszTrav = NULL;
    CHAR chSave;
    HRESULT hr = S_OK;

    if (!pdwOutSize) 
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if(0==_stricmp(pszVar, "AUTH_TYPE"))
    {
        pszRet = m_pszAuthType;
    }
    else if (0 == _stricmp(pszVar, "AUTH_USER"))
    {
        pszRet = m_pszRemoteUser;
    }
    else if (0 == _stricmp(pszVar, "AUTH_PASSWORD"))
    {
        pszRet = m_pszPassword;
    }
    else if(0==_stricmp(pszVar, "CONTENT_LENGTH")) 
    {
        hr = StringCbPrintfA(szBuf, sizeof(szBuf), "%d", m_dwContentLength);
        ASSERT(SUCCEEDED(hr));
        pszRet = szBuf;
    }
    else if(0==_stricmp(pszVar, "CONTENT_TYPE"))
    {
        pszRet = m_pszContentType;
    }
    else if(0==_stricmp(pszVar, "PATH_INFO"))
    {
        pszRet = m_pszPathInfo;    
    }
    else if(0==_stricmp(pszVar, "PATH_TRANSLATED"))
    {
        pszRet = m_pszPathTranslated;        
    }
    else if(0==_stricmp(pszVar, "QUERY_STRING"))
    {
        pszRet = m_pszQueryString;
    }
    else if(0==_stricmp(pszVar, "REMOTE_ADDR"))  
    {
        if (! GetRemoteAddress(m_socket, szBuf,FALSE,sizeof(szBuf)))
        {
            return FALSE; // WSA layer calls SetLastError() in this case
        }

        pszRet = szBuf;
    }
    else if (0==_stricmp(pszVar, "REMOTE_HOST"))  
    {
        if (! GetRemoteAddress(m_socket, szBuf,TRUE,sizeof(szBuf)))
        {
            return FALSE;  // WSA layer calls SetLastError() in this case
        }

        pszRet = szBuf;
    }
    else if(0==_stricmp(pszVar, "REMOTE_USER"))
    {
        pszRet = m_pszRemoteUser;
    }
    else if(0==_stricmp(pszVar, "UNMAPPED_REMOTE_USER"))
    {
        pszRet = m_pszRemoteUser;
    }
    else if(0==_stricmp(pszVar, "REQUEST_METHOD"))
    {
        pszRet = m_pszMethod;
    }
    else if (0==_stricmp(pszVar, "URL"))  
    {
        pszRet = m_pszURL;
    }
    else if (0==_stricmp(pszVar, "SCRIPT_NAME")) 
    {
        if (fFromFilter)
        {
            pszRet = NULL;
        }
        else
        {
            pszRet = m_pszURL;    
        }
    }
    else if (0==_stricmp(pszVar, "SERVER_NAME")) 
    {
        pszRet = m_pszHost;
    }
    else if(0==_stricmp(pszVar, "SERVER_PORT")) 
    {
        SOCKADDR_STORAGE  sockAddr;
        int               iSockLen = sizeof(sockAddr);

        if ((0 != getsockname(m_socket,(PSOCKADDR) &sockAddr,&iSockLen)) || (sockAddr.ss_family != AF_INET && sockAddr.ss_family != AF_INET6)) 
        {
            // we can't handle anything other than TCP/IP ports currently, no way to communicate AF back to ISAPI.
            // Set this error instead of default ERROR_INVALID_INDEX.
            SetLastError(WSAESOCKTNOSUPPORT);
            return FALSE; 
        }

        hr = StringCbPrintfA(szBuf, sizeof(szBuf), "%d", htons(GetSocketPort((PSOCKADDR)&sockAddr)));
        ASSERT(SUCCEEDED(hr));
        pszRet = szBuf;
    }
    else if (0 == _stricmp(pszVar,"LOCAL_ADDR")) 
    {
        SOCKADDR_STORAGE sockAddr;
        int              iSockLen = sizeof(sockAddr);

        if (0 != getsockname(m_socket,(PSOCKADDR) &sockAddr,&iSockLen)) 
        {
            SetLastError(WSAESOCKTNOSUPPORT);
            return FALSE; 
        }
        if (0 != getnameinfo((PSOCKADDR)&sockAddr,iSockLen,szBuf,sizeof(szBuf),NULL,0,NI_NUMERICHOST)) 
        {
            SetLastError(WSAESOCKTNOSUPPORT);
            return FALSE; 
        }
        pszRet = szBuf;
    }
    else if(0==_stricmp(pszVar, "SERVER_PORT_SECURE"))     
    {
        hr = StringCbPrintfA(szBuf, sizeof(szBuf), "%d", m_fIsSecurePort ? 1 : 0);
        ASSERT(SUCCEEDED(hr));
        pszRet = szBuf;
    }

    // HTTP_VERSION is version info as received from the client
    else if(0==_stricmp(pszVar, "HTTP_VERSION")) 
    {
        hr = StringCbPrintfA(szBuf, sizeof(szBuf), cszHTTPVER, HIWORD(m_dwVersion), LOWORD(m_dwVersion));
        ASSERT(SUCCEEDED(hr));
        pszRet = szBuf;
    }

    // SERVER_PROTOCOL is the version of http server supports, currently 1.1
    else if(0==_stricmp(pszVar, "SERVER_PROTOCOL")) 
    {
        hr = StringCbCopyA(szBuf,sizeof(szBuf),"HTTP/1.1");
        ASSERT(SUCCEEDED(hr));
        pszRet = szBuf;
    }
    else if(0==_stricmp(pszVar, "SERVER_SOFTWARE"))
    {
        pszRet = (PSTR)m_pWebsite->m_szServerID;
    }
    else if(0==_stricmp(pszVar, "ALL_HTTP"))
    {
        pszRet = 0;
    }

    // ALL_RAW return http headers, other than the simple request line.
    // The way our buffer is set up, we can have POST data immediatly following
    // header data.  So the client doesn't get confused, we have to set a \0 to it.
    else if (0 == _stricmp(pszVar, "ALL_RAW")) 
    {
        pszRet = m_bufRequest.Headers();    

        if (pszRet) 
        {
            // skip past simple request line.
            pszRet = strstr(pszRet,"\r\n") + 2;

            // If there's unaccessed data, buffer has POST data in it
            if (m_bufRequest.HasPostData()) 
            {
                pszTrav = strstr(pszRet,("\r\n\r\n")) + 4;
                chSave = *pszTrav; *pszTrav = 0;            
            }
        }
    }
    else if(0==_stricmp(pszVar, "HTTP_ACCEPT"))
    {
        pszRet = m_pszAccept;
    }
    else if (0 == _stricmp(pszVar, "HTTPS")) 
    {
        hr = StringCbCopyA(szBuf,sizeof(szBuf),m_fIsSecurePort ? "on" : "off");
        ASSERT(SUCCEEDED(hr));
        pszRet = szBuf;                
    }
    else if (0 == _stricmp(pszVar, "HTTPS_KEYSIZE") || 0 == _stricmp(pszVar,"CERT_KEYSIZE"))  
    {
        // Don't return any HTTPS_ data unless we have a secure connection.
        // Note that if someone is writing a custom filter (m_fIsSecurePort=TRUE, 
        // m_fSkipSSLProcessing=TRUE) then no way for GetServerVariables to access this data.
        if (m_fHandleSSL) 
        {
            hr = StringCbPrintfA(szBuf,sizeof(szBuf),"%d",m_SSLInfo.m_dwSessionKeySize);
            ASSERT(SUCCEEDED(hr));
            pszRet = szBuf;
        }
        else
        {
            pszRet = NULL;
        }
    }
    else if (0 == _stricmp(pszVar, "HTTPS_SECRETKEYSIZE") || 0 == _stricmp(pszVar,"CERT_SECRETKEYSIZE")) 
    {
        if (m_fHandleSSL) 
        {
            hr = StringCbPrintfA(szBuf,sizeof(szBuf),"%d",m_SSLInfo.m_dwSSLPrivKeySize);
            ASSERT(SUCCEEDED(hr));
            pszRet = szBuf;
        }
        else
        {
            pszRet = NULL;
        }
    }
    else if (0 == _stricmp(pszVar, "HTTPS_SERVER_ISSUER") || 0 == _stricmp(pszVar,"CERT_SERVER_ISSUER")) 
    {
        if (m_fHandleSSL)
        {
            pszRet = g_pVars->m_pszSSLIssuer;
        }
        else
        {
            pszRet = NULL;
        }
    }
    else if (0 == _stricmp(pszVar, "HTTPS_SERVER_SUBJECT") || 0 == _stricmp(pszVar,"CERT_SERVER_SUBJECT")) 
    {
        if (m_fHandleSSL)
        {
            pszRet = g_pVars->m_pszSSLSubject;
        }
        else
        {
            pszRet = NULL;
        }
    }
    else if (0 == _stricmp(pszVar, "CERT_SUBJECT") || 0 == _stricmp(pszVar,"CERT_ISSUER")) 
    {
        if (m_fHandleSSL && m_SSLInfo.m_pClientCertContext) 
        {
            DWORD dwFlags = (0 == _stricmp(pszVar, "CERT_SUBJECT")) ? 0 : CERT_NAME_ISSUER_FLAG;

            if (pCertGetNameStringA(m_SSLInfo.m_pClientCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,dwFlags,0,szBuf,sizeof(szBuf)))
            {
                pszRet = szBuf;
            }
            else
            {
                pszRet = NULL;
            }
        }
        else
        {
            pszRet = NULL;
        }
    }
    else if (0 == _stricmp(pszVar,"CERT_FLAGS")) 
    {
        if (!m_SSLInfo.m_pClientCertContext)
        {
            szBuf[0] = 0;
        }
        else
        {
           
            if(_ultoa_s(m_SSLInfo.m_dwCertFlags,szBuf,sizeof(szBuf),10)) {
                hr = E_FAIL;
            }
        }

        pszRet = szBuf;
    }
    else if (0 == _stricmp(pszVar,"CERT_SERIALNUMBER")) 
    {
        // Buffer size (cert bytes * 2 + # of '-' + NULL)
        if (m_fHandleSSL && m_SSLInfo.m_pClientCertContext && m_SSLInfo.m_pClientCertContext->pCertInfo)  
        {
            int cbSerialNum = m_SSLInfo.m_pClientCertContext->pCertInfo->SerialNumber.cbData;
            int cbRequired  = cbSerialNum*3;
            PSTR pSerialNumber = (PSTR)m_SSLInfo.m_pClientCertContext->pCertInfo->SerialNumber.pbData;

            DEBUGCHK(pszTrav == NULL);  // we shouldn't have to worry about reseting chSave.

            if ((DWORD)cbRequired > *pdwOutSize || NULL == pvOutBuf) 
            {
                *pdwOutSize = cbRequired;
                SetLastError(ERROR_INSUFFICIENT_BUFFER);
                return FALSE;
            }
            *pdwOutSize = cbRequired;

            if (pSerialNumber) 
            {
                // Serial number is in reverse byte order.  Write directly to pvOutBuf.
                int i = 0;
                pszRet = (PSTR) pvOutBuf;

                for (int iSerialByte = cbSerialNum - 1; iSerialByte >= 0; iSerialByte--) 
                {
                    pszRet[i++] = g_szHexDigits[((BYTE)(*(pSerialNumber + iSerialByte))) >> 4];
                    pszRet[i++] = g_szHexDigits[((BYTE)(*(pSerialNumber + iSerialByte))) & 0xf];
                    pszRet[i++] = '-';
                }
                DEBUGCHK(i == cbRequired);
                pszRet[i-1] = 0;
                
                return TRUE;
            }
        }
        pszRet = NULL;
    }
    else if (0==_strnicmp(pszVar,cszHTTP,dwHTTP)) 
    {
        PSTR  pszStart  = pszVar + dwHTTP;
        PSTR  pszEnd    = strchr(pszStart,'\0');
        DWORD dwLen2    = pszEnd - pszStart;
        DWORD dwOutLen  = 0;
        PSTR  pszHeader = m_bufRequest.Headers();

        if (pszHeader && (dwLen2 > 1)) 
        {
            do 
            {
                if (GetVariableStrCmp(pszHeader,pszStart,dwLen2))  
                {
                    pszHeader += dwLen2+1;  // skip past header + ':'
                    while ( (pszHeader)[0] != '\0' && IsNonCRLFSpace((pszHeader)[0])) 
                    { 
                        ++(pszHeader);
                    }
                    pszRet = pszHeader;
                    pszTrav = strstr(pszHeader,"\r\n");
                    chSave = '\r';
                    *pszTrav = 0;
                    break;
                }
                pszHeader = strstr(pszHeader,"\r\n")+2;
                if (*pszHeader == '\r')  
                {
                    DEBUGCHK(*(pszHeader+1) == '\n');
                    break;
                }                
            } while (1);
        }
    }    
    // end of pseudo-case stmnt

    if((PSTR)(-1) == pszRet) 
    {
        // unknown var
        SetLastError(ERROR_INVALID_INDEX);
        return FALSE;
    }
    // no such header/value. return empty (not NULL!) string
    if(!pszRet || FAILED(hr))
    {
        pszRet = (PSTR)cszEmpty;
    }

    dwLen = strlen(pszRet)+1;

    if(dwLen > *pdwOutSize || NULL == pvOutBuf)  
    {
        *pdwOutSize = dwLen;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        if (pszTrav)
        {
            *pszTrav = chSave;
        }
        return FALSE;
    }
    *pdwOutSize = dwLen;

    memcpy(pvOutBuf, pszRet, dwLen);
    if (pszTrav)
    {
        *pszTrav = chSave;    
    }
    return TRUE;
}



