/*--
Copyright (c) 1995-1998 Microsoft Corporation
Module Name: CONNECT.CPP
Author: Arul Menezes
Abstract: All the constant data used by the HTTP daemon
--*/
#include "pch.h"
#pragma hdrstop
#include "httpd.h"
const char* rgWkday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0};
const char* rgMonth[] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0};
const char cszServerID[] = "UPnP/1.0";
const char cszProductID[] = "UPnP Device Host/1.0";
// const char cszRespStatus[] = "HTTP/1.0 %d %s\r\n";
const char cszRespStatus[] = "HTTP/1.0 ";
// const char cszRespServer[] = "Server: %s\r\n";
const char cszRespServer2[] = "Server: ";
// const char cszRespType[] = "Content-Type: %s\r\n";
const char cszRespType2[] = "Content-Type: ";
// const char cszRespLength[] = "Content-Length: %d\r\n";
const char cszRespLength2[] = "Content-Length: ";
// const char cszRespWWWAuthBasic[] = "WWW-Authenticate: Basic\r\n";
const char cszRespWWWAuthBasic2[] = "WWW-Authenticate: Basic";
const char cszRespWWWAuthNTLM[] = "WWW-Authenticate: NTLM";
// const char cszDateFmt[] = "%3s, %02d %3s %04d %02d:%02d:%02d GMT\r\n";
const char cszRespDate[] = "Date: ";
// const char cszRespLocation[]="Location: %s\r\n";
const char cszRespLocation2[]="Location: ";
// const char cszConnection[] = "Connection: %s\r\n";
const char cszConnection2[] = "Connection: ";
const char cszKeepAlive[] = "keep-alive";
const char cszClose[] = "close";
const char cszCRLF[] = "\r\n";
const char cszRespLastMod[] = "Last-Modified: ";
#define APPENDCRLF(psz) { *psz++ = '\r'; *psz++ = '\n'; }
// The order of items in this table must match that in enum RESPONSESTATUS
// This is not international safe! In new version, we fill the table on
// httpd initialization with string from the rc file.
/*
STATUSINFO rgStatus[STATUS_MAX] = {
{ 200, "OK", NULL },
{ 302, "Object Moved", "
Object MovedObject Moved
This object has moved to "},
{ 304, "Not Modified", NULL }, // body not allowed for 304
{ 400, "Bad Request", "The request was not understood" },
{ 401, "Unauthorized", "Access denied" },
{ 403, "Forbidden", "Access denied" },
{ 404, "Object Not Found", "The requested URL was not found" },
{ 500, "Internal Server Error", "The server encountered an error" },
{ 501, "Not Implemented", "This method is not implemented" },
};
*/
STATUSINFO rgStatus[STATUS_MAX] = {
{ 200, "OK", NULL}, // no body!
{ 301, "Object Moved", NULL},
{ 304, "Not Modified", NULL}, // body not allowed for 304
{ 400, "Bad Request", NULL},
{ 401, "Unauthorized", NULL},
{ 403, "Forbidden", NULL},
{ 404, "Object Not Found", NULL},
{ 500, "Internal Server Error", NULL},
{ 501, "Not Implemented", NULL},
{ 505, "HTTP Version Not Supported", NULL},
};
#define LCID_USA MAKELANGID(LAND_ENGLISH, SUBLANG_ENGLISH_US);
int SendData(SOCKET sock, const char FAR *pszBuffData, int cbBuffLen, int flags)
{
// assumption - sock is a blocking one
char FAR *pszTempBuff ;
int nBytesSent = 0;
int retVal = cbBuffLen ;
pszTempBuff = (char FAR*)pszBuffData ;
while ( cbBuffLen > 0 )
{
if((nBytesSent = send(sock, pszTempBuff, cbBuffLen, flags)) == SOCKET_ERROR)
{
retVal = SOCKET_ERROR;
TraceTag(ttidWebServer, "Send RESPONSE Data failed! - %d",WSAGetLastError());
break;
}
if( nBytesSent <= 0 )
{ // XXX it may be that nBytesSent is zero. There is no simple solution other than
// doing a select until the socket becomes writeable. For now, we want to make
// sure that we make progress in the loop and ensure termination, so we will
// just break out if this happens.
retVal = SOCKET_ERROR;
TraceTag(ttidWebServer, "Send RESPONSE Data failed! zero bytes sent - %d",WSAGetLastError());
break;
}
cbBuffLen -= nBytesSent;
pszTempBuff += nBytesSent;
}
return retVal;
}
void CHttpResponse::SendHeaders(PCSTR pszExtraHeaders, PCSTR pszNewRespStatus)
{
DEBUG_CODE_INIT;
CHAR szBuf[HEADERBUFSIZE];
PSTR pszBuf = szBuf;
PSTR pszTrav;
int iLen;
DWORD cbNeeded ;
PSTR pszFilterHeaders = NULL;
if ( g_pVars->m_fFilters &&
! m_pRequest->CallFilter(SF_NOTIFY_SEND_RESPONSE))
myleave(97);
// For HTTP 0.9, we don't send headers back.
if (m_pRequest->m_dwVersion <= MAKELONG(9, 0))
myleave(0);
// We do calculation up front to see if we need a allocate a buffer or not.
// Certain may be set by an ISAPI Extension, Filter, or ASP
// and we must do the check to make sure their sum plus http headers server
// uses will be less than buffer we created.
cbNeeded = m_pRequest->m_bufRespHeaders.Count() +
MyStrlenA(pszExtraHeaders) +
MyStrlenA(pszNewRespStatus) +
MyStrlenA(m_pszRedirect) +
MyStrlenA(m_pRequest->m_pszNTLMOutBuf) +
((m_pRequest->m_pFInfo && m_rs == STATUS_UNAUTHORIZED) ?
MyStrlenA(m_pRequest->m_pFInfo->m_pszDenyHeader) : 0);
// If what we need for extra + 1000 bytes (for the regular Http headers) is
// > our buf, allocate a new one
if (cbNeeded + 1000 > HEADERBUFSIZE)
{
if (!(pszBuf = MyRgAllocNZ(CHAR,cbNeeded+1000)))
myleave(98);
}
pszTrav = pszBuf;
// if (pszNewRespStatus)
// iLen = sprintf(szBuf,"HTTP/1.0 %s\r\n",pszNewRespStatus);
// else
// iLen = sprintf(szBuf, cszRespStatus, rgStatus[m_rs].dwStatusNumber, rgStatus[m_rs].pszStatusText);
// the status line
// ServerSupportFunction on SEND_RESPONSE_HEADER can override the server value
pszTrav = strcpyEx(pszTrav,cszRespStatus);
if (pszNewRespStatus)
{
pszTrav = strcpyEx(pszTrav,pszNewRespStatus);
}
else // copy the number and value over
{
_itoa(rgStatus[m_rs].dwStatusNumber,pszTrav,10);
pszTrav = strchr(pszTrav,'\0');
*pszTrav++ = ' ';
pszTrav = strcpyEx(pszTrav,rgStatus[m_rs].pszStatusText);
}
APPENDCRLF(pszTrav);
// GENERAL HEADERS
// the Date line
SYSTEMTIME st;
GetSystemTime(&st); // NOTE: Must be GMT!
// iLen += sprintf(szBuf+iLen, cszDateFmt, rgWkday[st.wDayOfWeek], st.wDay, rgMonth[st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
pszTrav = strcpyEx(pszTrav, cszRespDate);
pszTrav = WriteHTTPDate(pszTrav,&st,TRUE);
// APPENDCRLF(pszTrav);
// Connection: header
if (m_connhdr != CONN_NONE)
{
// iLen += sprintf(szBuf+iLen, cszConnection, (m_connhdr==CONN_KEEP ? cszKeepAlive : cszClose));
pszTrav = strcpyEx(pszTrav,cszConnection2);
pszTrav = strcpyEx(pszTrav,(m_connhdr==CONN_KEEP ? cszKeepAlive : cszClose));
APPENDCRLF(pszTrav);
}
// RESPONSE HEADERS
// the server line
// iLen += sprintf(szBuf+iLen, cszRespServer, cszServerID);
pszTrav = strcpyEx(pszTrav,cszRespServer2);
pszTrav = strcpyEx(pszTrav,g_pVars->m_pszServerID);
APPENDCRLF(pszTrav);
// the Location header (for redirects)
if (m_pszRedirect)
{
DEBUGCHK(m_rs == STATUS_MOVED);
// iLen += sprintf(szBuf+iLen, cszRespLocation, m_pszRedirect);
pszTrav = strcpyEx(pszTrav,cszRespLocation2);
pszTrav = strcpyEx(pszTrav,m_pszRedirect);
APPENDCRLF(pszTrav);
}
// the WWW-Authenticate line
if (m_rs == STATUS_UNAUTHORIZED)
{
// In the middle of an NTLM authentication session
if (g_pVars->m_fNTLMAuth && m_pRequest->m_pszNTLMOutBuf)
{
// iLen += sprintf(szBuf+iLen,"%s %s\r\n",cszRespWWWAuthNTLM,m_pRequest->m_pszNTLMOutBuf);
pszTrav = strcpyEx(pszTrav,cszRespWWWAuthNTLM);
*pszTrav++ = ' ';
pszTrav = strcpyEx(pszTrav,m_pRequest->m_pszNTLMOutBuf);
APPENDCRLF(pszTrav);
}
// If both schemes are enabled, send both back to client and let the browser decide which to use
else if (g_pVars->m_fBasicAuth && g_pVars->m_fNTLMAuth)
{
// iLen += sprintf(szBuf+iLen,"%s\r\n%s",cszRespWWWAuthNTLM,cszRespWWWAuthBasic);
pszTrav = strcpyEx(pszTrav,cszRespWWWAuthNTLM);
APPENDCRLF(pszTrav);
pszTrav = strcpyEx(pszTrav,cszRespWWWAuthBasic2);
APPENDCRLF(pszTrav);
}
else if (g_pVars->m_fBasicAuth)
{
// iLen += sprintf(szBuf+iLen, cszRespWWWAuthBasic);
pszTrav = strcpyEx(pszTrav,cszRespWWWAuthBasic2);
APPENDCRLF(pszTrav);
}
else if (g_pVars->m_fNTLMAuth)
{
// iLen += sprintf(szBuf+iLen,"%s\r\n",cszRespWWWAuthNTLM);
pszTrav = strcpyEx(pszTrav,cszRespWWWAuthNTLM);
APPENDCRLF(pszTrav);
}
}
// ENTITY HEADERS
// the Last-Modified line
if (m_hFile)
{
FILETIME ft;
SYSTEMTIME st;
if ( GetFileTime(m_hFile, NULL, NULL, &ft) && // gets filetime in GMT
FileTimeToSystemTime(&ft, &st) ) // converts GMT filetime to GMT systemtime
{
// iLen += sprintf(szBuf+iLen, cszDateFmt, rgWkday[st.wDayOfWeek], st.wDay, rgMonth[st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
pszTrav = strcpyEx(pszTrav, cszRespLastMod);
pszTrav = WriteHTTPDate(pszTrav,&st,TRUE);
}
}
// the Content-Type line
if (m_pszType)
{
// iLen += sprintf(szBuf+iLen, cszRespType, m_pszType);
pszTrav = strcpyEx(pszTrav,cszRespType2);
pszTrav = strcpyEx(pszTrav,m_pszType);
APPENDCRLF(pszTrav);
}
// the Content-Length line
if (m_dwLength)
{
// If we have a file that is 0 length, it is set to -1.
if (m_dwLength == -1)
m_dwLength = 0;
// iLen += sprintf(szBuf+iLen, cszRespLength, m_dwLength);
pszTrav = strcpyEx(pszTrav,cszRespLength2);
_itoa(m_dwLength,pszTrav,10);
pszTrav = strchr(pszTrav,'\0');
APPENDCRLF(pszTrav);
}
if ((m_rs == STATUS_UNAUTHORIZED) && m_pRequest->m_pFInfo && m_pRequest->m_pFInfo->m_pszDenyHeader)
{
pszTrav = strcpyEx(pszTrav,m_pRequest->m_pFInfo->m_pszDenyHeader);
// It's the script's responsibility to add any \r\n to the headers.
}
if (m_pRequest->m_bufRespHeaders.Data())
{
memcpy(pszTrav,m_pRequest->m_bufRespHeaders.Data(),m_pRequest->m_bufRespHeaders.Count());
pszTrav += m_pRequest->m_bufRespHeaders.Count();
}
// APPENDCRLF(pszTrav);
if (pszExtraHeaders)
{
pszTrav = strcpyEx(pszTrav,pszExtraHeaders);
// It's the script's responsibility to add any \r\n to the headers.
}
else
{
APPENDCRLF(pszTrav); // the double CRLF signifies that header data is at an end
}
*pszTrav = 0;
pszFilterHeaders = pszBuf; // pointer may be modified, don't loose original pszBuf ptr
iLen = (int)((INT_PTR)(pszTrav - pszBuf));
if (g_pVars->m_fFilters &&
! m_pRequest->CallFilter(SF_NOTIFY_SEND_RAW_DATA, &pszFilterHeaders, &iLen))
myleave(99);
iLen = SendData(m_socket,pszFilterHeaders,iLen,0);
done:
TraceTag(ttidWebServer, "Sending RESPONSE HEADER<<%s>>", pszFilterHeaders);
if (pszBuf != szBuf)
MyFree(pszBuf);
}
void CHttpResponse::SendBody(void)
{
if (TOK_HEAD == m_pRequest->m_idMethod)
return;
if (m_hFile)
SendFile(m_socket, m_hFile, m_pRequest);
else if (m_pszBody)
{
PSTR pszSendBuf = (PSTR) m_pszBody;
int cbSendBuf = m_dwLength;
if (g_pVars->m_fFilters &&
! m_pRequest->CallFilter(SF_NOTIFY_SEND_RAW_DATA, &pszSendBuf, &cbSendBuf))
{
;
}
else
{ // send if there's no filters or if filter call succeeded
SendData(m_socket, pszSendBuf, cbSendBuf, 0);
}
TraceTag(ttidWebServer, "Sending RESPONSE BODY<<%s>> len=%d", pszSendBuf, cbSendBuf);
}
}
void SendFile(SOCKET sock, HANDLE hFile, CHttpRequest *pRequest)
{
BYTE bBuf[4096];
DWORD dwRead;
PBYTE pszSendBuf;
int cbSendBuf;
int nBytesSent = 0;
while (ReadFile(hFile, bBuf, sizeof(bBuf), &dwRead, 0) && dwRead)
{
pszSendBuf = bBuf;
cbSendBuf = dwRead;
if (g_pVars->m_fFilters &&
! pRequest->CallFilter(SF_NOTIFY_SEND_RAW_DATA, (PSTR*) &pszSendBuf, &cbSendBuf))
{
;
}
else
{ // send if there's no filters or if filter call succeeded
if((nBytesSent = SendData(sock, (PSTR) pszSendBuf, cbSendBuf, 0)) == SOCKET_ERROR)
{
TraceTag(ttidWebServer, "Send RESPONSE Data failed! - %d",WSAGetLastError());
break;
}
else
{
TraceTag(ttidWebServer, "Sending RESPONSE Data - Bytes to be sent = %d bytes sent = %d", dwRead, nBytesSent );
}
}
}
return;
}
// This function used to display the message "Object moved
void CHttpResponse::SendRedirect(PCSTR pszRedirect, BOOL fFromFilter)
{
if (!fFromFilter && m_pRequest->FilterNoResponse())
return;
DEBUGCHK(!m_hFile);
DEBUGCHK(m_rs==STATUS_MOVED);
// create a body
PSTR pszBody = MySzAllocA(strlen(rgStatus[m_rs].pszStatusBody)+2*strlen(pszRedirect)+30);
DWORD dwLen = 0;
if (pszBody)
{
// originally used this string set -- "Moved Permanently", "Object MovedObject Moved
This object has moved to %s" },
// sprintf(pszBody, rgStatus[m_rs].pszStatusBody, pszRedirect, pszRedirect);
PSTR pszTrav = strcpyEx(pszBody,rgStatus[m_rs].pszStatusBody);
pszTrav = strcpyEx(pszTrav,"");
pszTrav = strcpyEx(pszTrav,pszRedirect);
pszTrav = strcpyEx(pszTrav,"");
SetBody(pszBody, cszTextHtml);
}
// set redirect header & send all headers, then the synthetic body
m_pszRedirect = pszRedirect;
SendHeaders(NULL,NULL);
SendBody();
MyFree(pszBody);
}
// Read strings of the bodies to send on web server errors ("Object not found",
// "Server Error",...) using load string. These strings are in wide character
// format, so we first convert them to regular strings, marching along
// pszBodyCodes (which is a buffer size BODYSTRINGSIZE). After the conversion,
// we set each rgStatus[i].pszStatusBody to the pointer in the buffer.
void InitializeResponseCodes(PSTR pszStatusBodyBuf)
{
PSTR pszTrav = pszStatusBodyBuf;
UINT i;
int iLen;
WCHAR wszBuf[256];
for (i = 0; i < ARRAYSIZEOF(rgStatus); i++)
{
// no bodies for these
if (i == STATUS_OK || i == STATUS_NOTMODIFIED)
continue;
LoadString(g_hInst,RESBASE_body + i,wszBuf,celems(wszBuf));
iLen = MyW2A(wszBuf, pszTrav, BODYSTRINGSIZE - (int)((INT_PTR)((pszTrav - pszStatusBodyBuf))));
if (!iLen)
return;
rgStatus[i].pszStatusBody = pszTrav;
pszTrav += iLen;
}
}