📄 wincgi.cpp
字号:
--- */
bool WaitForChild( const char *pszCmdLine, PIHTTP &tPIHTTP,
PROCDESC tProcess )
{
int iRet = PIPlatform_waitForProcess( (int)tProcess, 0, iKillAfter );
switch( iRet )
{
case PIAPI_COMPLETED: return true;
case PIAPI_TIMEOUT:
HTTPCore_logError( &tPIHTTP, "WinCGI: CGI \
process did \
not terminate after %d seconds.\nCGI program command line is '%s'.\n\
The process will be terminated.", iKillAfter, pszCmdLine );
# if WIN32
::TerminateProcess( tProcess, (UINT)-1 );
# elif POSIX
::kill( (pid_t)tProcess, SIGTERM );
# endif
return false;
default:
HTTPCore_logError( &tPIHTTP, "WinCGI: Error waiting for \
CGI process to terminate.\nCGI program command line is '%s'",
pszCmdLine );
return false;
};
}
/* ---
Write output from one buffer into another. Transfer data until no more
data can be read.
Returns 0 on success.
--- */
int WriteBufferToBuffer( PIIOBuffer &tInBuffer, PIIOBuffer &tOutBuffer )
{
int iLen;
const char *pBuffer = PIIOBuffer_read( &tInBuffer, &iLen );
while( pBuffer )
{
if ( PIIOBuffer_write( &tOutBuffer, pBuffer, iLen, PIIOBUF_NONE )
<=0 )
{
assert( 0 );
return -1;
};
pBuffer = PIIOBuffer_read( &tInBuffer, &iLen );
};
return 0;
};
/* ---
Write output from one buffer into another.
iReadLen gives number of bytes to transfer, must be > 0
Returns 0 on success.
--- */
int WriteBufferToBuffer( PIIOBuffer &tInBuffer, PIIOBuffer &tOutBuffer,
int iReadLen, PIString &sResult )
{
assert( iReadLen>0 );
if ( iReadLen<=0 )
{ return 0; };
enum { BUF_SIZE=4096 };
char szBuffer[BUF_SIZE];
int iAttemptToRead = (iReadLen >= BUF_SIZE) ? BUF_SIZE : iReadLen;
int iInKey = 0;
int iValLen = 0;
int iStdinLen = iReadLen;
int iLen;
iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer, iAttemptToRead );
while( iLen>0 )
{
if ( PIIOBuffer_write( &tOutBuffer, szBuffer, iLen,
PIIOBUF_NONE )<=0 )
{
assert( 0 );
return -1;
};
/* --- Scan the buffer for [Form Huge] literals --- */
if (iStdinLen > 8192)
{
int iScanned = 0;
enum { KEYSIZE=255 };
char szKeyBuf[KEYSIZE+1];
int i;
for (i=0; i<iLen; i++)
{
if (szBuffer[i] == '=')
{
strncpy(szKeyBuf, &szBuffer[iScanned], i-iScanned+1);
szKeyBuf[i-iScanned+1]='\0';
sResult.Concatenate(szKeyBuf);
sprintf( szKeyBuf, "%lu", iStdinLen-iReadLen+i+1 );
sResult.Concatenate( szKeyBuf );
sResult.Concatenate(' ');
iScanned = i;
iInKey = 0;
}
if (szBuffer[i] == '&')
{
sprintf( szKeyBuf, "%lu", iValLen+i-iScanned-1 );
sResult.Concatenate( szKeyBuf );
#if WIN32
sResult.Concatenate("\r\n");
#else
sResult.Concatenate("\n");
#endif
iScanned = i+1;
iInKey = 1;
iValLen = 0;
}
}
if (iInKey)
{
strncpy(szKeyBuf, &szBuffer[iScanned], i-iScanned+1);
szKeyBuf[i-iScanned+1]='\0';
sResult.Concatenate(szKeyBuf);
}
else
{
if (iReadLen - iLen)
{
iValLen += i-iScanned;
}
else
{
sprintf( szKeyBuf, "%lu", iValLen+i-iScanned-1 );
sResult.Concatenate( szKeyBuf );
#if WIN32
sResult.Concatenate("\r\n");
#else
sResult.Concatenate("\n");
#endif
}
};
}
iReadLen -= iLen;
if ( !iReadLen )
{
return 0;
};
iAttemptToRead = (iReadLen >= BUF_SIZE) ? BUF_SIZE : iReadLen;
iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer,
iAttemptToRead );
};
/* ---
Done
--- */
return iLen;
};
/* ---
Send input data to the CGI program using a copy of the pipe
IO object. Return non-zero on success, 0 on error.
--- */
int SendCGIInput( PIIOBuffer &tIn, PIIOBuffer &tOut, int iLen, PIString &sResult )
{
int iRet = 1; /* initially success */
/* ---
Write input to child stdin and close CGI side of pipe
--- */
if ( WriteBufferToBuffer( tIn, tOut, iLen, sResult ) )
{
iRet = 0;
goto SendCGIInput_error;
};
if ( iSendCRLF )
{
/* --- Write CRLF to the CGI program --- */
if ( PIIOBuffer_writeLn( &tOut, "", 0, PIIOBUF_NONE )<=0 )
{
iRet = 0;
goto SendCGIInput_error;
};
};
/* --- flush --- */
if ( !PIIOBuffer_flush( &tOut ) )
{
iRet = 0;
goto SendCGIInput_error;
};
/* --- done --- */
SendCGIInput_error:
if ( !iRet )
{
HTTPCore_logDebug( DBG_MED, "WinCGI: SendCGIInput() failed." );
};
return iRet;
};
/* ---
Execute the CGI program and examine the output.
--- */
bool DoExecCGI(
PIFInfo *pFile,
PIHTTP &tPIHTTP,
PROCDESC &tProcess )
{
/* ---
Normally the callee to DoExecCGI would generate an internal redirect
to handle an error code. The flag bIgnoreStatus can be set to
true to tell the callee that a redirect should not happen, because
the protocol headers have already been sent to the client. This
happens for full response
--- */
bool bIgnoreStatus = false;
PIDB *pQ = tPIHTTP.pRequestDB;
PIDB *pR = tPIHTTP.pResponseDB;
PIDB *pC = tPIHTTP.pConnectionDB;
PIDB *pH = tPIHTTP.pHostDB;
PIIOBuffer &tB = *( tPIHTTP.pBuffer );
/* --- sanity check --- */
assert( pFile && pQ && pR );
if ( !( pFile && pQ && pR ) )
{
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
};
/* ---
Make CGI context data
--- */
CGIContextData tContext;
/* ---
Determine whether or not there is any input to be sent to the
CGI program
--- */
const char *pContentLength = (const char *)PIDB_lookup( pQ,
PIDBTYPE_RFC822, KEY_HTTP_CONTENTLENGTH, 0 );
int iStdinLen = pContentLength ? atoi( pContentLength ) : 0;
int iDoStdin = ( iStdinLen > 0 );
/* ---
Set filenames
--- */
enum { BUFSIZE=1023 };
char szFileBuf[BUFSIZE+1];
if ( pDataFile ) {
int iLen = Pi3Expression_write( pDataFile, &tPIHTTP, &tContext,
szFileBuf, BUFSIZE );
if ( iLen>0 )
{
szFileBuf[iLen] = '\0';
Pi3String *pTmp = Pi3String_new( tContext.sDataFile );
HTTPCore_relativeToAbsolutePath( pH, szFileBuf, pTmp );
tContext.sDataFile = Pi3String_getPtr( pTmp );
Pi3String_delete( pTmp );
};
};
if ( pStdinFile ) {
int iLen = Pi3Expression_write( pStdinFile, &tPIHTTP, &tContext,
szFileBuf, BUFSIZE );
if ( iLen>0 )
{
szFileBuf[iLen] = '\0';
Pi3String *pTmp = Pi3String_new( tContext.sInputFile );
HTTPCore_relativeToAbsolutePath( pH, szFileBuf, pTmp );
tContext.sInputFile = Pi3String_getPtr( pTmp );
Pi3String_delete( pTmp );
};
};
if ( pStdoutFile ) {
int iLen = Pi3Expression_write( pStdoutFile, &tPIHTTP, &tContext,
szFileBuf, BUFSIZE );
if ( iLen>0 )
{
szFileBuf[iLen] = '\0';
Pi3String *pTmp = Pi3String_new( tContext.sOutputFile );
HTTPCore_relativeToAbsolutePath( pH, szFileBuf, pTmp );
tContext.sOutputFile = Pi3String_getPtr( pTmp );
Pi3String_delete( pTmp );
/* --- mark output file as a temporary file --- */
PIDB_add( pR, PIDBTYPE_STRING, KEY_INT_TEMPORARYFILE,
(void *)(const char *)tContext.sOutputFile, 0 );
};
};
/* ---
See if this is an 'ISINDEX' query, and if it is, set the
ISINDEX value appropriately
An ISINDEX query will not have a '=' encoded in the query string.
--- */
const char *pQS = (const char *)PIDB_lookup( pQ, PIDBTYPE_STRING,
KEY_HTTP_QUERYSTRING, 0 );
Pi3String *pEncodedQS = Pi3String_new( pQS );
Pi3String *pDecodedQS = Pi3String_new( 0 );
HTTPUtil_urlDecode( pEncodedQS, pDecodedQS );
const char *pDQS = Pi3String_getPtr( pDecodedQS );
int i;
for( i=0; pDQS[i] && pDQS[i]!='='; i++ );
if ( !pDQS[i] )
{
/* --- '=' not found, therefore this is an ISINDEX query --- */
/* ---
Only include up to the first punctuation character, to
keep out 'special' parameters being passed to exec(). Thi
is a security message
--- */
for( i=0; pDQS[i] && !ispunct(pDQS[i]); i++ );
PIString sTmp( pDQS, i );
tContext.sIsIndexValue = " ";
tContext.sIsIndexValue.Concatenate( sTmp );
};
Pi3String_delete( pEncodedQS );
Pi3String_delete( pDecodedQS );
/* ---
Write extra headers to context
--- */
if ( iExtraHeaders )
{
switch (AddClientRequestHeaders( &tPIHTTP, pQ, szFileBuf, BUFSIZE+1 )) {
case 0:
tContext.sExtraHeaders = szFileBuf;
break;
case -1:
HTTPCore_logError( &tPIHTTP,
"WinCGI: Internal error getting data of extra request headers." );
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
case 1:
HTTPCore_logError( &tPIHTTP,
"WinCGI: Buffer exhausted when getting data of extra request headers" );
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
}
};
/* ---
Write accept header to context
--- */
if ( iExtraHeaders )
{
switch (AddClientAcceptHeader( &tPIHTTP, pQ, szFileBuf, BUFSIZE+1 )) {
case 0:
tContext.sAcceptHeader = szFileBuf;
break;
case -1:
HTTPCore_logError( &tPIHTTP,
"WinCGI: Internal error getting data of accept header." );
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
case 1:
HTTPCore_logError( &tPIHTTP,
"WinCGI: Buffer exhausted when getting data of accept header." );
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
}
};
FILEDESC tStdout_Parent = INVALID_DESC;
FILEDESC tStdout_CGI = INVALID_DESC;
FILEDESC tStdin_Parent = INVALID_DESC;
FILEDESC tStdin_CGI = INVALID_DESC;
FILEDESC tStderr_Parent = INVALID_DESC;
FILEDESC tStderr_CGI = INVALID_DESC;
/*
** Define a macro to make sure these handles are all closed
** if we return in the middle of these functions
*/
#define CLOSE_ALL_HANDLES \
{ \
CloseIfValidDesc( tStdin_Parent ); \
CloseIfValidDesc( tStdin_CGI ); \
CloseIfValidDesc( tStdout_Parent ); \
CloseIfValidDesc( tStdout_CGI ); \
CloseIfValidDesc( tStderr_Parent ); \
CloseIfValidDesc( tStderr_CGI ); \
}
tContext.sExePath = PIFInfo_getPath( pFile );
const char *pDirectory = PIFInfo_getPathRoot( pFile );
/* ---
Find the appropriate command line pattern
--- */
Pi3Expression *pCommandPattern = pDefaultCommandLine;
const char *pExtn = PIFInfo_getExtension( pFile );
for( DblListIterator k( lCommandLineByExt ); !k.BadIndex(); k++ )
{
FileExtToCommandLineMap *pMap =
(FileExtToCommandLineMap *)k.Current();
if ( pMap->sFileExtension==pExtn )
{
pCommandPattern = pMap->pExpr;
break;
};
};
/* ---
Write input to input file
--- */
PIPLATFORM_FD tFd = PIFile_open( tContext.sInputFile, "w" );
if ( tFd==PIPLATFORM_FD_INVALID )
{
HTTPCore_logError( &tPIHTTP,
"WinCGI: Failed to open input file, path is '%s'.",
((const char *)tContext.sInputFile) );
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
};
/* --- mark input file as a temporary file --- */
PIDB_add( pR, PIDBTYPE_STRING, KEY_INT_TEMPORARYFILE,
(void *)(const char *)tContext.sInputFile, 0 );
if ( iDoStdin && iStdinLen>0 )
{
const char *pArgs[3];
pArgs[0] = (const char *)-1;
pArgs[1] = (const char *)tFd;
pArgs[2] = (const char *)0; /* handle is a pipe - no? */
PIObject *pChildIO = PIObject_copy( pPipeIO, 3, pArgs );
PIIOBuffer *pCGIBuffer = 0;
int iResult = 0;
if ( pChildIO )
{
pCGIBuffer = PIIOBuffer_new( pChildIO );
iResult = SendCGIInput( tB, *pCGIBuffer, iStdinLen, tContext.sFormHuge );
if ( !iResult )
{
HTTPCore_logError( &tPIHTTP,
"WinCGI: Failed to write input file, path is '%s'.",
((const char *)tContext.sInputFile) );
tPIHTTP.iStatus = ST_INTERNALERROR;
PIIOBuffer_delete( pCGIBuffer );
PIObject_delete( pChildIO, 0, 0 );
PIFile_close( tFd );
return bIgnoreStatus;
};
/*
** Tricky! The flushed buffer points to the begin of the old data
** (We expect a flushed buffer from SendCGIInput)
**
** We currently support only up to 8192 Bytes (PIIOBuffer size)
** for [Form Literal] or [Form External], then [Form Huge] has been
** used by SendCGIInput() already!
*/
char *pBuf;
if ( iStdinLen <= PIIOBuffer_getOutputBuffer( pCGIBuffer, &pBuf ))
{
/* ---
Write accept header to context
--- */
if (AddClientFormData( &tPIHTTP, pQ, pBuf, iStdinLen,
tContext.sFormLiteral, tContext.sFormExternal))
{
HTTPCore_logError( &tPIHTTP,
"WinCGI: Internal error getting data of request form literals." );
tPIHTTP.iStatus = ST_INTERNALERROR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -