//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2000. // // File: linkhits.cxx // // Contents: classes to insert links among query hits in HTML format // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include #include "webdbg.hxx" #include "whmsg.h" #include "linkhits.hxx" #include "whtmplat.hxx" extern int _cdecl ComparePositions(const void* pPos1, const void* pPos2); const WCHAR VPathWebHitsFile[]=L"CiWebHitsFile"; const WCHAR CommandLineVarName[]=L"QUERY_STRING"; const WCHAR RestrictionVarName[]=L"CiRestriction"; const WCHAR IDQFilenameVarName[]=L"CiQueryFile"; const WCHAR WTXFilenameVarName[]=L"CiTemplateFile"; const WCHAR HiliteTypeVarName[]=L"CiHiliteType"; const WCHAR ColorVarName[]=L"CiHiliteColor"; const WCHAR BoldVarName[]=L"CiBold"; const WCHAR ItalicVarName[]=L"CiItalic"; const WCHAR MaxLineLength[]=L"CiMaxLineLength"; const WCHAR LocaleVar[]=L"CiLocale"; const WCHAR BeginHiliteVar[]=L"CiBeginHilite"; const WCHAR EndHiliteVar[]=L"CiEndHilite"; const WCHAR NullHTWFile[]=L"null.htw"; const WCHAR CodepageVar[]=L"CiCodepage"; const WCHAR DialectVar[]=L"CiDialect"; const WCHAR ParaTag[]=L"

\n"; const WCHAR HRule[]=L"


\n"; const WCHAR Red24BitMask[]=L"#FF0000"; const WCHAR Blue24BitMask[]=L"#0000FF"; const WCHAR Green24BitMask[]=L"#00FF00"; const WCHAR Black24BitMask[]=L"#000000"; const WCHAR Yellow24BitMask[]=L"#FFFF00"; // // List of replacable parameters in the htx file for output generation. // const WCHAR wcsParamCiUrl[] = L"CIURL"; const WCHAR wcsParamRestriction[] = L"CIRESTRICTION"; const WCHAR wcsCharSet[] = L"CICHARSET"; const WCHAR wcsLocale[] = L"CILOCALE"; const WCHAR wcsCodepage[] = L"CICODEPAGE"; const WCHAR wcsUserParamPrefix[] = L"CIUSERPARAM"; const ULONG cwcUserParamPrefix = (sizeof(wcsUserParamPrefix)/sizeof(WCHAR))-1; const WCHAR * awcsUserParamNames[] = { L"CIUSERPARAM1", L"CIUSERPARAM2", L"CIUSERPARAM3", L"CIUSERPARAM4", L"CIUSERPARAM5", L"CIUSERPARAM6", L"CIUSERPARAM7", L"CIUSERPARAM8", L"CIUSERPARAM9", L"CIUSERPARAM10", }; //+------------------------------------------------------------------------- // // Member: CInternalQuery::CinternalQuery, public constructor // // Arguments: [rGetEnvVars] - object that contains CGI env. variables // [rPList] - property list of properties to query // [lcid] - LCID (locale identifier) // // Synopsis: rGetEnvVars yields the textual restriction. // GetStringDbRestriction then converts it into a DbRestriction. // //-------------------------------------------------------------------------- CInternalQuery::CInternalQuery( CGetEnvVars& rGetEnvVars, CEmptyPropertyList& rPList, LCID lcid ): _pDbRestriction(0), _pISearch(0) { TRY { _pDbRestriction = GetStringDbRestriction( rGetEnvVars.GetRestriction(), rGetEnvVars.GetDialect(), &rPList, lcid); } CATCH( CException, e) { webDebugOut(( DEB_ERROR, "WEBHITS: failed to get DbRestriction\n" )); delete _pDbRestriction; RETHROW(); } END_CATCH } void CInternalQuery::CreateISearch( WCHAR const * pwszPath ) { Win4Assert( 0 == _pISearch ); SCODE sc = MakeISearch( &_pISearch, _pDbRestriction, pwszPath ); if ( FAILED( sc ) ) THROW( CException( sc ) ); } //CreateISearch //+------------------------------------------------------------------------- // // Member: CURLUnescaper::CURLUnescaper public constructor // //-------------------------------------------------------------------------- CURLUnescaper::CURLUnescaper( UINT codePage ) : _codePage(codePage) { } //+--------------------------------------------------------------------------- // // Member: CURLUnescaper::UnescapeAndConvertToUnicode // // Synopsis: Converts the given multi-byte string into a unicode string // based on the code page. Decodes URL escape sequences along // the way. // // Arguments: [pszMbStr] - smart array pointer to input string // [cch] - length of input string // [xwcsBuffer] - // // Returns: Number of characters in target buffer, excluding the // terminating NULL. // // History: 24 Nov 1997 AlanW Created // //---------------------------------------------------------------------------- ULONG CURLUnescaper::UnescapeAndConvertToUnicode( char const * pszMbStr, ULONG cch, XArray & xwcsBuffer ) { if ( xwcsBuffer.Get() == 0 || cch+1 > xwcsBuffer.Count() ) { delete [] xwcsBuffer.Acquire(); xwcsBuffer.Init( cch+1 ); } DecodeURLEscapes( (BYTE *)pszMbStr, cch, xwcsBuffer.Get(), _codePage ); return cch; } //+------------------------------------------------------------------------- // // Member: CCollectVar::CCollectVar, public constructor // // Arguments: [rUnescaper] - reference to an unescaper object - it will // be used to unescape variables stored in // the local buffer // [webServer] - web server object from which variables // are read. // //-------------------------------------------------------------------------- CCollectVar::CCollectVar( CURLUnescaper& rUnescaper, CWebServer & webServer ): _rUnescaper(rUnescaper), _webServer( webServer ), _xwcsBuffer(DEFAULT_BUFF_SIZE), _cwcVarSize(0), _xszBuffer(DEFAULT_BUFF_SIZE), _cbVarSize(0) { } //+------------------------------------------------------------------------- // // Member: CCollectVar::GetEnvVar // // Arguments: [pwcsVariableName] - the name of the variable to be retrieved // // Synopsis: Returns TRUE if the specified variable was retrieved and // FALSE otherwise. // //-------------------------------------------------------------------------- BOOL CCollectVar::GetEnvVar( CHAR const * pszVariableName) { // // Prime to indicate that the WIDE-CHAR form of the string is // not valid anymore. // _cwcVarSize = 0; _cbVarSize = 0; // // Retrieve the variable as an ascii string // webDebugOut(( DEB_ITRACE, "WEBHITS: Getting environment variable\n" )); ULONG cb = _xszBuffer.Count(); if ( ! _webServer.GetCGIVariable( pszVariableName, _xszBuffer.GetPointer(), & cb ) ) { if ( cb > _xszBuffer.Count() ) { // // have to re-size and incur the cost of the memory allocation // delete[] _xszBuffer.Acquire(); _xszBuffer.Init( cb ); if ( ! _webServer.GetCGIVariable( pszVariableName, _xszBuffer.GetPointer(), & cb ) ) return FALSE; } else { return FALSE; } } _cbVarSize = cb; return TRUE; } //+------------------------------------------------------------------------- // // Member: CCollectVar::UnescapeAndConvertToUnicode // // Synopsis: Unescapes the variable currently stored in _xszBuffer. // //-------------------------------------------------------------------------- inline void CCollectVar::UnescapeAndConvertToUnicode() { // // Convert the multi-byte string into a UNICODE string. // _cwcVarSize = _rUnescaper.UnescapeAndConvertToUnicode( _xszBuffer.Get(), _cbVarSize, _xwcsBuffer ); } //+------------------------------------------------------------------------- // // Member: CSmartByteArray::CSmartByteArray - public constructor // // Synopsis: allocates memory for the buffer //-------------------------------------------------------------------------- CSmartByteArray::CSmartByteArray(): _xszBuffer(DEFAULT_BUFFER_SIZE) { } //+------------------------------------------------------------------------- // // Member: CSmartByteArray::CopyTo // // Arguments: [pwcText] - pointer to text to be copied // [cwcToCopy] - the number of characters to be copied // // Synopsis: copies the text pointed to by [pwcText] to the buffer, // enlarging it if necessary. A L'\0' is appended to the end // automatically, since this method is used to copy non-null // terminated strings. // //-------------------------------------------------------------------------- void CSmartByteArray::CopyTo(char * pszText, ULONG cbToCopy) { // // if a buffer larger than the default one is being allocated, then // re-size it // if ( cbToCopy >= _xszBuffer.Count() ) { delete[] _xszBuffer.Acquire(); ULONG cbNew = max(2*_xszBuffer.Count(), cbToCopy+1); _xszBuffer.Init(cbNew); } RtlCopyMemory(_xszBuffer.GetPointer(),pszText,cbToCopy); // insert null terminator _xszBuffer[cbToCopy] = 0; } //+------------------------------------------------------------------------- // // Member: CQueryStringParser::CQueryStringParser, public constructor // // Arguments: [pwszQUERY_STRING] - buffer in which the *ESCAPED* // QUERY_STRING is stored // [rUnescaper] - ref. to unescaper object // // Synopsis: The QUERY_STRING must be passed in *ESCAPED* form, otherwise // it's difficult to say for example whether an ampersand delimits // consecutive variables or is part of the restriction // //-------------------------------------------------------------------------- CQueryStringParser::CQueryStringParser( char * pszQUERY_STRING, CURLUnescaper& rUnescaper): _pszQS(pszQUERY_STRING), _pszQSEnd(_pszQS+strlen(_pszQS)), _pBeg(_pszQS), _pEnd(_pszQSEnd), _isNull(TRUE), _rUnescaper(rUnescaper) { } //+------------------------------------------------------------------------- // // Member: CQueryStringParser::FindVarEnd // // Arguments: [pwc] - pointer to character at which to begin 'searching' // for the end of the current variable assignment // // Synopsis: returns a pointer to the end of the variable assignment inside // of which pwc points - this is the next '&' if there are more // variable assignments following, or the terminating L'\0' // of QUERY_STRING if this is the last one. // //-------------------------------------------------------------------------- CHAR* CQueryStringParser::FindVarEnd(CHAR* psz) { if (!psz) return NULL; while( (psz < _pszQSEnd) && *psz != '&') psz++; return psz; } //+------------------------------------------------------------------------- // // Member: CQueryStringParser::EatChar // // Arguments: [pwc] - pointer to the character that is to be eaten // // Synopsis: If [pwc] points to the character before the null terminator // of the string, returns NULL. Otherwise, returns [pwc]+1. // //-------------------------------------------------------------------------- CHAR* CQueryStringParser::EatChar(char * psz) { if (!psz) return NULL; psz++; if (*psz) return psz; else return NULL; } //+------------------------------------------------------------------------- // // Member: CQueryStringParser::EatVariableName // // Arguments: [pwc] - pointer to the character at which to start eating // // Synopsis: Eat text until a '\0', whitespace, or '=' is encountered. If // a '\0' is encountered, return NULL, otherwise return a pointer // to the first non-blob character, where a blob character is // defined as anything except for whitespace, '\0', and '='. // //-------------------------------------------------------------------------- char * CQueryStringParser::EatVariableName(char * psz) { if (!psz) return NULL; // // Note: this limits us to Ascii for variable names, which isn't // really a problem since we define all the variables. // while ( (*psz) && !isspace(*psz) && ('=' != *psz)) psz++; if (*psz) return psz; return NULL; } //EatVariableName //+------------------------------------------------------------------------- // // Member: CQueryStringParser::ValidateArgument // // Arguments: [pwc] - pointer to the character string to validate // // Synopsis: Returns the pointer if valid or 0 otherwise. // //-------------------------------------------------------------------------- char * CQueryStringParser::ValidateArgument(char * psz) { if ( ( 0 == psz ) || ( 0 == *psz ) ) return 0; return psz; } //+------------------------------------------------------------------------- // // Member: CQueryStringParser::NextVar // // Synopsis: Extract the next command-line variable contained // in QUERY_STRING. Returns TRUE if the next variable was // successfully extracted, and FALSE otherwise // //-------------------------------------------------------------------------- BOOL CQueryStringParser::NextVar() { // // set the NULL flag // _isNull = TRUE; // we've hit the end of the buffer if (!_pBeg) return FALSE; // find the end of the variable assignment within the buffer _pEnd = FindVarEnd(_pBeg); if (!_pEnd || _pEnd == _pBeg) { THROW( CException(MSG_WEBHITS_QS_FORMAT_INVALID)); } // // copy the chunk of the QUERY_STRING into the temporary buffer for // unescaping // _smartBuffer.CopyTo(_pBeg, (ULONG)(_pEnd-_pBeg)); // L'\0' appended //_rUnescaper.Unescape(_smartBuffer.GetXPtr(), _pEnd-_pBeg); // move to the variable name CHAR* pTemp1 = ValidateArgument( _smartBuffer.GetPointer() ); if (!pTemp1) { THROW( CException(MSG_WEBHITS_QS_FORMAT_INVALID)); } CHAR* pTemp2 = EatVariableName(pTemp1); if (!pTemp2) { THROW( CException(MSG_WEBHITS_QS_FORMAT_INVALID)); } // // Convert the variable name into a Unicode string. // ULONG cbToConvert = (ULONG)(pTemp2-pTemp1) ; _rUnescaper.UnescapeAndConvertToUnicode( pTemp1, cbToConvert, _xwszVarName ); // move to the equal sign pTemp1=ValidateArgument(pTemp2); if (!pTemp1 || (*pTemp1 != L'=')) { THROW( CException(MSG_WEBHITS_QS_FORMAT_INVALID)); } pTemp1 = EatChar(pTemp1); // // if not a null value keep on eating away // if (ValidateArgument(pTemp1)) { // move to the variable value pTemp1 = ValidateArgument(pTemp1); if (!pTemp1) { THROW( CException(MSG_WEBHITS_QS_FORMAT_INVALID)); } // get the variable value cbToConvert = strlen(pTemp1); _rUnescaper.UnescapeAndConvertToUnicode( pTemp1, cbToConvert, _xwszVarValue ); // // if we got this far, the variable had a value // _isNull = FALSE; } else { // // null value - delete the string name // delete _xwszVarName.Acquire(); } // advance to the next variable assignment if (_pEnd < _pszQSEnd) { _pBeg = ++_pEnd; } else { _pBeg = NULL; } return TRUE; } //+------------------------------------------------------------------------- // // Member: CGetEnvVars::CGetEnvVars(), public constructor // // Arguments: [webServer] - web server to use // [langInfo] - language-specific info // [rCollectVar] - ref. to a variable-retrieving object // [rUnescaper] - ref. to unescaper object // // Synopsis: The constructor retrieves the Filename, VPath, QUERY_STRING, // and Restriction. //-------------------------------------------------------------------------- CGetEnvVars::CGetEnvVars( CWebServer & webServer, CLanguageInfo & langInfo, CCollectVar& rCollectVar, CURLUnescaper& rUnescaper ) : _lcid(langInfo.GetUrlCodePage()), _hiliteType(SUMMARY), _rCollectVar( rCollectVar ), _rUnescaper(rUnescaper), _isBold(FALSE), _isItalic(FALSE), _isFixedFont(FALSE), _ccFixedFontLine(1), _xwc24BitColourMask(new WCHAR[8]), _aUserParams(eMaxUserReplParams), _langInfo( langInfo ), _webServer( webServer ), _dwWebHitsFileFlags( 0 ), _dwQueryFileFlags( 0 ), _dwTemplateFileFlags( 0 ), _dialect( ISQLANG_V2 ) { for ( ULONG i = 0; i < eMaxUserReplParams; i++ ) _aUserParams[i] = 0; wcscpy(_xwc24BitColourMask.GetPointer(),L"#FF0000"); // // We support only the GET and POST methods -- verify // BYTE * pszBuffer; ULONG cbBuffer; if ( ( strcmp( webServer.GetMethod(), "GET" ) != 0 ) && ( strcmp( webServer.GetMethod(), "POST" ) != 0 ) ) { webDebugOut(( DEB_ERROR, "WEBHITS: invalid REQUEST_METHOD\n" )); THROW( CException(MSG_WEBHITS_REQUEST_METHOD_INVALID) ); } XArray xHTW; ULONG cwc; if ( webServer.GetCGI_PATH_INFO( xHTW, cwc ) ) { webDebugOut(( DEB_ITRACE, "htw file: '%ws'\n", xHTW.GetPointer() )); // Allow null.htw files to not exist, in which case the file just // serves as a script map trigger with default formatting info. WCHAR *pwc = wcsrchr( xHTW.GetPointer(), L'/' ); if ( ( 0 != pwc ) && ( _wcsicmp( pwc+1, NullHTWFile ) ) ) _xwcsTemplateFileVPath.Set( xHTW.Acquire() ); } // // retrieve and parse the command line - we store in the same // buffer in both places // RetrieveQueryString(); ParseQUERY_STRING(); } //+------------------------------------------------------------------------- // // Member: CGetEnvVars::ParseQUERY_STRING() // // Synopsis: parses QUERY_STRING for the various command-line parameters. // This method must be called AFTER RetrieveQueryString. If a // "crucial" variable remains unset at the end, an exception is // is thrown. // //-------------------------------------------------------------------------- void CGetEnvVars::ParseQUERY_STRING() { webDebugOut(( DEB_ITRACE, "WEBHITS: parsing QS '%s'\n", _xszQueryString.GetPointer() )); CQueryStringParser QSParser( _xszQueryString.GetPointer(), _rUnescaper ); while(QSParser.NextVar()) { XPtrST xwcTempVarName; XPtrST xwcTempVarValue; // // acquire the variable name and value from the parser, and hand it over to SetVar // if (!QSParser.IsNull()) { QSParser.GetVarName(xwcTempVarName); QSParser.GetVarValue(xwcTempVarValue); SetVar(xwcTempVarName,xwcTempVarValue); } } VerifyQSVariablesComplete(); } //+--------------------------------------------------------------------------- // // Member: CGetEnvVars::IsUserParam // // Synopsis: // // Arguments: [pwcsParam] - // // Returns: // // Modifies: // // History: 9-10-96 srikants Created // // Notes: // //---------------------------------------------------------------------------- inline ULONG CGetEnvVars::IsUserParam( const WCHAR * pwcsParam ) { if ( 0 == _wcsnicmp( pwcsParam, wcsUserParamPrefix, cwcUserParamPrefix) ) { int i = _wtoi( pwcsParam+cwcUserParamPrefix ); if ( i > 0 && i <= eMaxUserReplParams ) return (ULONG) i; else return 0; } else { return 0; } } //+------------------------------------------------------------------------- // // Member: CGetEnvVars::SetVar // // Arguments: [xwszVarName] - the name of the variable being set // [xwszVarValue]- the value it is being set to // // Synopsis: Sets the command-line variable [xwszVarName] to [xwszVarValue] // and throws if the variable name is invalid. The string // containing the variable name is deleted here, as is the // string containing the variable value if the latter is of no // use and should not be stored. // //-------------------------------------------------------------------------- void CGetEnvVars::SetVar(XPtrST& xwszVarName, XPtrST& xwszVarValue) { // // create local copies of the smart pointers, ensuring deletion at the end // XPtrST xwszLocalVarName(xwszVarName.Acquire()); XPtrST xwszLocalVarValue(xwszVarValue.Acquire()); webDebugOut(( DEB_ITRACE, "var '%ws', value '%ws'\n", xwszLocalVarName.GetPointer(), xwszLocalVarValue.GetPointer() )); if(_wcsicmp(xwszLocalVarName.GetPointer(),RestrictionVarName) == 0) { if ( 0 != _xwcsRestriction.GetPointer() ) THROW( CException( MSG_WEBHITS_DUPLICATE_PARAMETER ) ); _xwcsRestriction.Set(xwszLocalVarValue.Acquire()); } else if (_wcsicmp(xwszLocalVarName.GetPointer(),HiliteTypeVarName) == 0) { if (_wcsicmp(xwszLocalVarValue.GetPointer(),L"full") == 0) { _hiliteType = FULL; } } else if (_wcsicmp(xwszLocalVarName.GetPointer(),IDQFilenameVarName) == 0) { if ( 0 != _xwcsQueryFileVPath.GetPointer() ) THROW( CException( MSG_WEBHITS_DUPLICATE_PARAMETER ) ); _xwcsQueryFileVPath.Set(xwszLocalVarValue.Acquire()); } else if (_wcsicmp(xwszLocalVarName.GetPointer(),ColorVarName)==0) { if ( (*(xwszLocalVarValue.GetPointer()) == L'0') && (wcslen(xwszLocalVarValue.GetPointer()) == 8)) { // // a 24-bit colour spec is being passed in // RtlCopyMemory(_xwc24BitColourMask.GetPointer(),xwszLocalVarValue.GetPointer()+1, sizeof(Red24BitMask)); *(_xwc24BitColourMask.GetPointer())=L'#'; } else if (_wcsicmp(xwszLocalVarValue.GetPointer(),L"red")==0) { RtlCopyMemory(_xwc24BitColourMask.GetPointer(),Red24BitMask,sizeof(Red24BitMask)); } else if (_wcsicmp(xwszLocalVarValue.GetPointer(),L"blue")==0) { RtlCopyMemory(_xwc24BitColourMask.GetPointer(),Blue24BitMask,sizeof(Blue24BitMask)); } else if (_wcsicmp(xwszLocalVarValue.GetPointer(),L"green")==0) { RtlCopyMemory(_xwc24BitColourMask.GetPointer(),Green24BitMask,sizeof(Green24BitMask)); } else if (_wcsicmp(xwszLocalVarValue.GetPointer(),L"black")==0) { RtlCopyMemory(_xwc24BitColourMask.GetPointer(),Black24BitMask,sizeof(Black24BitMask)); } else if (_wcsicmp(xwszLocalVarValue.GetPointer(),L"yellow")==0) { RtlCopyMemory(_xwc24BitColourMask.GetPointer(),Yellow24BitMask,sizeof(Yellow24BitMask)); } } else if (_wcsicmp(xwszLocalVarName.GetPointer(),VPathWebHitsFile) == 0) { if ( 0 != _xwcsWebHitsFileVPath.GetPointer() ) THROW( CException( MSG_WEBHITS_DUPLICATE_PARAMETER ) ); _xwcsWebHitsFileVPath.Set(xwszLocalVarValue.Acquire()); } else if (_wcsicmp(xwszLocalVarName.GetPointer(),BoldVarName) == 0) { _isBold=TRUE; } else if (_wcsicmp(xwszLocalVarName.GetPointer(),ItalicVarName)==0) { _isItalic=TRUE; } else if ( _wcsicmp(xwszLocalVarName.GetPointer(),MaxLineLength) == 0 ) { _isFixedFont = TRUE; if ( xwszLocalVarValue.GetPointer() ) _ccFixedFontLine = _wtoi( xwszLocalVarValue.GetPointer() ); _ccFixedFontLine = max( _ccFixedFontLine, 1 ); } else if ( _wcsicmp(xwszLocalVarName.GetPointer(),LocaleVar) == 0 ) { // // Set the output and cirestriction locale info now. // if ( xwszLocalVarValue.GetPointer() ) { if ( !_locale.IsNull() ) delete _locale.Acquire(); LCID lcid = GetLCIDFromString( xwszLocalVarValue.GetPointer() ); _locale.Init(1+wcslen(xwszLocalVarValue.GetPointer())); wcscpy(_locale.GetPointer(), xwszLocalVarValue.GetPointer()); // // The output will be generated using this codepage and for // interpretation of the "CiRestriction". // _langInfo.SetRestrictionLocale( lcid ); } } else if ( _wcsicmp(xwszLocalVarName.GetPointer(), CodepageVar) == 0) { if (xwszLocalVarValue.GetPointer() ) { if ( !_codepage.IsNull() ) delete _codepage.Acquire(); _codepage.Init(1+wcslen(xwszLocalVarValue.GetPointer())); wcscpy(_codepage.GetPointer(), xwszLocalVarValue.GetPointer()); } } else if ( _wcsicmp(xwszLocalVarName.GetPointer(), DialectVar) == 0) { if ( xwszLocalVarValue.GetPointer() ) { ULONG d = (ULONG) _wtoi( xwszLocalVarValue.GetPointer() ); if ( d > ISQLANG_V2 || d < ISQLANG_V1 ) THROW( CException( MSG_WEBHITS_INVALID_DIALECT ) ); _dialect = d; } } else if ( _wcsicmp(xwszLocalVarName.GetPointer(),BeginHiliteVar) == 0 ) { if ( 0 != _xwcsBeginHiliteTag.GetPointer() ) THROW( CException( MSG_WEBHITS_DUPLICATE_PARAMETER ) ); #if 0 // security hole -- we can't display random html from users _xwcsBeginHiliteTag.Set(xwszLocalVarValue.Acquire()); #endif } else if ( _wcsicmp(xwszLocalVarName.GetPointer(),EndHiliteVar) == 0 ) { if ( 0 != _xwcsEndHiliteTag.GetPointer() ) THROW( CException( MSG_WEBHITS_DUPLICATE_PARAMETER ) ); #if 0 // security hole -- we can't display random html from users _xwcsEndHiliteTag.Set(xwszLocalVarValue.Acquire()); #endif } else { ULONG nUserParam = IsUserParam( xwszLocalVarName.GetPointer() ); if ( nUserParam > 0 ) { if ( 0 != _aUserParams[nUserParam-1] ) THROW( CException( MSG_WEBHITS_DUPLICATE_PARAMETER ) ); _aUserParams[nUserParam-1] = xwszLocalVarValue.Acquire(); } else { // // the variable name is not recognized - throw. We don't need to // delete the strings as they are contained in smart pointers // webDebugOut((DEB_ERROR,"WEBHITS: bad variable name:%ws\n", xwszLocalVarName.GetPointer() )); THROW(CException(MSG_WEBHITS_VARNAME_INVALID)); } } } //+------------------------------------------------------------------------- // // Member: CGetEnvVars::VerifyQSVariablesComplete() // // Synopsis: Checks whether all the "crucial" variables that were to be // set as part of QUERY_STRING have been set, and throws an // exception otherwise. // //-------------------------------------------------------------------------- void CGetEnvVars::VerifyQSVariablesComplete() { if(_xwcsRestriction.IsNull()) { webDebugOut(( DEB_ERROR, "WEBHITS: incomplete variable set read from QS\n" )); THROW(CException(MSG_WEBHITS_INVALID_QUERY)); } } //+------------------------------------------------------------------------- // // Member: CGetEnvVars::RetrieveCONTENT_LENGTH // // Synopsis: retrieves and returns the value of CONTENT_LENGTH, // and throws if unable to retrieve // //-------------------------------------------------------------------------- int CGetEnvVars::RetrieveCONTENT_LENGTH() { if(!_rCollectVar.GetEnvVar("CONTENT_LENGTH")) { webDebugOut(( DEB_ERROR, "WEBHITS: failed to get CONTENT_LENGTH\n" )); return 0; THROW( CException(MSG_WEBHITS_CONTENT_LENGTH_INVALID) ); } else { // // get the number of bytes // return _wtoi(_rCollectVar.GetVarValue()); } } //+------------------------------------------------------------------------- // // Member: CGetEnvVars::RetrieveQueryString // // Synopsis: retrieves the value of QUERY_STRING, setting _xwcsQueryString // to point to it, and throws otherwise // //-------------------------------------------------------------------------- void CGetEnvVars::RetrieveQueryString() { if(!_rCollectVar.GetEnvVar("QUERY_STRING")) { webDebugOut(( DEB_ERROR, "WEBHITS: failed to get QS\n" )); THROW( CException(MSG_WEBHITS_INVALID_QUERY) ); } else { int cbToCopy = _rCollectVar.GetMultiByteStrLen(); // // we do not unescape QUERY_STRING to preserve delimiting information // _xszQueryString.Init(cbToCopy+1); RtlCopyMemory( _xszQueryString.GetPointer(), _rCollectVar.GetMultiByteStr(), cbToCopy ); _xszQueryString[cbToCopy] = 0; } } //+------------------------------------------------------------------------- // // function GetLCID // // Synopsis: returns the locale in HTTP_ACCEPT_LANGUAGE, or if that one is // is not set, the one obtained from GetSystemDefaultLCID() // //-------------------------------------------------------------------------- LCID GetLCID( CWebServer & webServer ) { webDebugOut(( DEB_ITRACE, "WEBHITS: Getting HTTP_ACCEPT_LANGUAGE variable\n" )); XArray xBuffer; ULONG cwcBuffer; BOOL fOK = webServer.GetCGIVariableW( L"HTTP_ACCEPT_LANGUAGE", xBuffer, cwcBuffer ); if ( !fOK ) { LCID locale = GetSystemDefaultLCID(); return locale; } LCID lcid = GetLCIDFromString( xBuffer.GetPointer() ); if ( 0xFFFFFFFF == lcid ) lcid = GetSystemDefaultLCID(); return lcid; } //+--------------------------------------------------------------------------- // // Function: SetCodePageForCRunTimes // // Synopsis: Set the appropriate code page for the c-runtimes so that // swprintf, putchar, etc correctly translate the unicode into // the appropriate multi-byte sequence. // // Arguments: [codePage] - Code page of the client. // // History: 9-06-96 srikants Created // //---------------------------------------------------------------------------- UINT CLanguageInfo::SetCodePageForCRunTimes( UINT codePage ) { char szCodePage[20]; sprintf( szCodePage,".%d", codePage ); char * p = setlocale( LC_ALL, szCodePage ); if ( 0 == p ) { webDebugOut(( DEB_WARN, "Could not set code page for %d\n. Going to system default", codePage )); LCID lcid = GetSystemDefaultLCID(); codePage = LocaleToCodepage( lcid ); sprintf( szCodePage,".%d", codePage ); char * p = setlocale( LC_ALL, szCodePage ); } return codePage; } //+------------------------------------------------------------------------- // // Member: CSortQueryHits::Init() // // Synopsis: Initialization function for CSortQueryHits (need to do this // as a HitIter& is needed to fully construct this class). // creates array of positions sorted in order of occurrence // within the document (note that a position may occur several // times). // //-------------------------------------------------------------------------- void CSortQueryHits::Init() { Win4Assert(0 == _aPosition); _positionCount = CountPositions(); if (0 != _positionCount) { _aPosition = new Position[_positionCount]; int iPosition=0; for (BOOL fOk=_rHitIter.FirstHit();fOk;fOk=_rHitIter.NextHit()) { int posInHit=_rHitIter.GetPositionCount(); for (int i=0;i 0) { Position nextPos = _sortedHits.GetPosition(0); _nextBegOffset = nextPos.GetBegOffset(); _nextEndOffset = nextPos.GetEndOffset(); } } //+------------------------------------------------------------------------- // // Member: CLinkQueryHits::IsSeparatedBySpaces // // Arguments: [startOffset] - beginning offset // [endOffset] - ending offset // // Synopsis: returns TRUE if the positions in the current paragraph // determined by [startOffset] and [endOffset] are separated // by whitespace characters. // // Note: There is a maximum 'allowed' separation, beyond which // whitespace is considered significant. // //-------------------------------------------------------------------------- unsigned const ccSignificantWhitespace = 20; BOOL CLinkQueryHits::IsSeparatedBySpaces(int startOffset, int endOffset) { Win4Assert( startOffset <= endOffset ); if (startOffset > endOffset) return FALSE; // // Small buffer for calls to GetStringTypeW // WORD awCharType[ccSignificantWhitespace]; int len = endOffset - startOffset; if ( 0 == len ) return TRUE; if ( len > ccSignificantWhitespace ) return FALSE; const WCHAR* pStart = _document.GetPointerToOffset(startOffset); // // Check for whitespace // if ( !GetStringTypeW( CT_CTYPE1, pStart, len, awCharType ) ) { webDebugOut(( DEB_ERROR, "GetStringType returned %d\n", GetLastError() )); return FALSE; } // // Only blanks are legal 'spaces' // for ( int i = 0; i < len; i++ ) { if ( 0 == (awCharType[i] & C1_BLANK) ) return FALSE; } return TRUE; } //+------------------------------------------------------------------------- // // Member: CLinkQueryHits::InsertLinks // // Synopsis: Hit highlight the document by inserting linked HTML tags // //-------------------------------------------------------------------------- void CLinkQueryHits::InsertLinks() { if ( !_rHttpOutput.IsTemplateFilePresent() ) _rHttpOutput.TagPosition(-1); _rHttpOutput.OutputFullHeader(); // // Determine if this is a mainly text document. // if ( _rGetEnvVars.IsFixedFont() ) _rHttpOutput.OutputPreformattedTag(); ULONG eofOffset = _document.GetEOFOffset(); if (_sortedHits.GetPositionCount() == 0) { // // There are no hits. Just render the whole text. // WCHAR* pText = _document.GetWritablePointerToOffset(0); _rHttpOutput.OutputHttpText(pText,eofOffset); } else { BOOL openTag = FALSE; while(_currentOffset < (int) eofOffset) { WCHAR* pText = _document.GetWritablePointerToOffset(_currentOffset); const ULONG cSectionSize = _nextBegOffset - _currentOffset; if ( cSectionSize > 0 ) { _rHttpOutput.OutputHttpText(pText,cSectionSize); _currentOffset +=cSectionSize; if ( (int) eofOffset == _currentOffset ) break; pText+=cSectionSize; } const ULONG cHiliteSize = _nextEndOffset - _nextBegOffset; if ( cHiliteSize > 0 ) { // // display the "<<" tag - if a tag is not already open // if (!openTag) { _rHttpOutput.OutputLeftTag(_tagCount-1); _rHttpOutput.TagPosition(_tagCount); openTag = TRUE; } // // display the highlited position text // _rHttpOutput.OutputHilite(pText, cHiliteSize); _currentOffset += cHiliteSize; Win4Assert(_currentOffset == _nextEndOffset); } // // get the next distinct position // BOOL existsNextPosition = MoveToNextDifferentPosition(); // // display the ">>" tag unless separated only by spaces or // last tag in doc in which case we have to make it point to the // top if ( !existsNextPosition ) { if ( openTag ) { _rHttpOutput.OutputRightTag(-1); openTag = FALSE; } _nextBegOffset = _nextEndOffset = eofOffset; } else if ( openTag && !IsSeparatedBySpaces(_currentOffset,_nextBegOffset) ) { _rHttpOutput.OutputRightTag(_tagCount+1); _tagCount++; openTag = FALSE; } } } } //+------------------------------------------------------------------------- // // Member: CExtractedHit::CExtractedHit, public constructor // // Arguments: [rDocument] - ref. to document being hilited // [rHit] - ref. to hit being hilited // [rOutput] - ref. to Http Output object // [cwcMargin] - number of chars. to be printed before and // after the hit // [cwcSeparation]- maximum number of characters that may // separate consecutive positions before // truncation occurs // [cwcDelim] - the number of characters to print before and // after a position in the case of truncation //-------------------------------------------------------------------------- CExtractedHit::CExtractedHit( CDocument& rDocument, Hit& rHit, PHttpOutput& rOutput, int cwcMargin, int cwcSeparation, int cwcDelim ): _rDocument(rDocument), _rHit(rHit), _cwcMargin(cwcMargin), _rOutput(rOutput), _cwcSeparation(cwcSeparation), _cwcDelim(cwcDelim), _cwcOutputBuffer(0) { SortHit(); } //+------------------------------------------------------------------------- // // Member: CExtractedHit::SortHit() // // Synopsis: Sort the positions in the hit in the order in which they occur // in the document // //-------------------------------------------------------------------------- void CExtractedHit::SortHit() { qsort( _rHit._aPos, _rHit.GetPositionCount(), sizeof(Position), &ComparePositions ); } //+------------------------------------------------------------------------- // // Member: CExtractedHit::DisplayPosition // // Arguments: [rPos] - ref. to position being displayed // // Synopsis: Display the highlighted position (i.e. JUST the position) // //-------------------------------------------------------------------------- void CExtractedHit::DisplayPosition(const Position& rPos) { const WCHAR* pText = _rDocument.GetPointerToOffset(rPos.GetBegOffset()); _rOutput.OutputHilite(pText,rPos.GetLength()); } //+------------------------------------------------------------------------- // // Member: CExtractedHit::ExtractHit() // // Synopsis: Extract the hit - i.e. display all of the positions and // the associated preamble/postamble text // //-------------------------------------------------------------------------- void CExtractedHit::ExtractHit() { // // make sure that we are not dealing with a null hit - i.e. a hit composed // entirely of null positions if (_rHit.IsNullHit()) return; // stores the number of positions in the hit int cPositions = _rHit.GetPositionCount(); // // introduce new paragraph // _rOutput.OutputParaTag(); _rOutput.OutputEllipsis(); // // go through displaying each position // // // find the first non-null position in the hit // int firstRealPos = EatNullPositions(); // // display the preamble // PrintPreamble(_rHit.GetPos(firstRealPos),_cwcMargin); // // display the positions and the stuff in between the positions // for (int i=firstRealPos;i < cPositions; i++) { DisplayPosition(_rHit.GetPos(i)); // the stuff between consecutive positions if (i != cPositions - 1) { // // guard against the case where multiple identical positions are // returned as part of the same hit // if (_rHit.GetPos(i).GetBegOffset() < _rHit.GetPos(i+1).GetBegOffset()) { PrintBtwPositions(_rHit.GetPos(i),_rHit.GetPos(i+1)); } else { while ( ( i != ( cPositions - 1 ) ) && ( _rHit.GetPos(i).GetBegOffset() == _rHit.GetPos(i+1).GetBegOffset() ) ) i++; } } } // // display the postamble // PrintPostamble(_rHit.GetPos(cPositions-1),_cwcMargin); _rOutput.OutputEllipsis(); _rOutput.OutputHRULE(); } //ExtractHit //+------------------------------------------------------------------------- // // Member: CExtractedHit::ComputeDistance // // Arguments: [rStartPos] - ref. to starting position // [rEndPos] - ref. to end position // // Synopsis: Compute the distance in characters between the start and // end positions // //-------------------------------------------------------------------------- ULONG CExtractedHit::ComputeDistance (const Position& rStartPos, const Position& rEndPos) { return rEndPos.GetBegOffset() - rStartPos.GetEndOffset(); } //+------------------------------------------------------------------------- // // Member: CExtractedHit::PrintPreamble // // Arguments: [rStartPosition] - ref. to start position // [cwcDist] - maximum number of characters to print // // // Synopsis: Prints the context text preceding the hit - the number of // characters printed depends on cwcDist. The break is made // at a word boundary // //-------------------------------------------------------------------------- void CExtractedHit::PrintPreamble(const Position& rStartPosition,int cwcDist) { WCHAR* pBeg = _rDocument.GetWritablePointerToOffset( rStartPosition.GetBegOffset()-cwcDist); const WCHAR* pEnd = _rDocument.GetPointerToOffset( rStartPosition.GetBegOffset()); // Boundary case - if the beginning of the document, don't skip any thing if ( pBeg != _rDocument.GetPointerToOffset(0) ) { while( (pBeg < pEnd) && !iswspace(*pBeg) && !(UNICODE_PARAGRAPH_SEPARATOR == *pBeg)) pBeg++; } Win4Assert(pBeg <= pEnd); _rOutput.OutputHttpText(pBeg, CiPtrToUlong( pEnd-pBeg )); } //PrintPreamble //+------------------------------------------------------------------------- // // Member: CExtractedHit::PrintPostamble // // Arguments: [rStartPosition] - ref. to start position // [cwcDist] - maximum number of characters to be printed // // Synopsis: Print the context text following the hit - the number of // characters printed depends on cwcDist. The break is made // at a word boundary // //-------------------------------------------------------------------------- void CExtractedHit::PrintPostamble(const Position& rStartPosition, int cwcDist) { WCHAR* pBeg = _rDocument.GetWritablePointerToOffset( rStartPosition.GetEndOffset()); const WCHAR* pEnd = _rDocument.GetPointerToOffset( rStartPosition.GetEndOffset()+cwcDist); while( (pEnd > pBeg) && !iswspace(*pEnd) && !(UNICODE_PARAGRAPH_SEPARATOR == *pEnd) ) pEnd--; Win4Assert(pEnd >= pBeg); _rOutput.OutputHttpText( pBeg, CiPtrToUlong( pEnd-pBeg )); } //PrintPostamble //+------------------------------------------------------------------------- // // Member: CExtractedHit::PrintBtwPositions // // Arguments: [rStartPosition] - ref. to start position // [rEndPosition] - ref. to end position // // Synopsis: Display the text between the two positions, breaking at word // boundaries. If the positions are separated by a distance // greated than _cwcSeparation, then the text in-between is // truncated, and up to _cwcDelim characters after the first and // before the second position are printed, separated by an // ellipsis // //-------------------------------------------------------------------------- void CExtractedHit::PrintBtwPositions( const Position& rStartPosition, const Position& rEndPosition) { long dist; if ((dist=ComputeDistance(rStartPosition,rEndPosition)) > _cwcSeparation) { PrintPostamble(rStartPosition,_cwcDelim); _rOutput.OutputEllipsis(); PrintPreamble(rEndPosition,_cwcDelim); } else { WCHAR* pText = _rDocument.GetWritablePointerToOffset( rStartPosition.GetEndOffset() ); _rOutput.OutputHttpText(pText,dist); } } //PrintBtwPositions //+------------------------------------------------------------------------- // // Member: CExtractedHits::ExtractHits // // Arguments: same as CExtractHit constructor, except rHitIter - a hit // iterator replaces the hit reference // // Synopsis: Class functor to encapsulate the action of highliting the // hits in rHitIter. A temporary CExtractHit object is created // for each hit. // //-------------------------------------------------------------------------- CExtractHits::CExtractHits(CDocument& rDocument, HitIter& rHitIter, PHttpOutput& rOutput, int cwcMargin, int cwcDelim, int cwcSeparation ) { for (BOOL fOK=rHitIter.FirstHit(); fOK; fOK = rHitIter.NextHit()) { CExtractedHit extractedHit( rDocument, rHitIter.GetHit(), rOutput, cwcMargin, cwcDelim, cwcSeparation ); extractedHit.ExtractHit(); } } //CExtractHits //+------------------------------------------------------------------------- // // Member: PHttpOutput::PHttpOutput - public constructor // // Arguments: [webServer] -- The web server to write to // [langInfo] -- Language information // //-------------------------------------------------------------------------- PHttpOutput::PHttpOutput( CWebServer & webServer, CLanguageInfo & langInfo ): _hasPrintedHeader(FALSE), _xwc24BitColourMask(new WCHAR[8]), _isBold(FALSE), _isItalic(FALSE), _isInPreformat(FALSE), _newLine(FALSE), _cwcOutputBuffer(0), _pGetEnvVars(NULL), _pTemplate(NULL), _fUseHiliteTags(FALSE), _cwcBeginHiliteTag(0), _cwcEndHiliteTag(0), _webServer( webServer ), _langInfo( langInfo ) { wcscpy(_xwc24BitColourMask.GetPointer(),L"#FF0000"); _mbStr.Init( MAX_OUTPUT_BUF ); } //+--------------------------------------------------------------------------- // // Member: PHttpOutput::Init // // Synopsis: Initializes the output generation class with the environment // variables object and the template object. // // Arguments: [pGetEnvVars] - Pointer to the object that has the relevant // environment variables. // [pTemplate] - [OPTIONAL] - Pointer to the template object. // If non-zero, this will be used to generate the output for // hit-highlighter. // // History: 9-09-96 srikants Created // //---------------------------------------------------------------------------- void PHttpOutput::Init(CGetEnvVars* pGetEnvVars, CWebhitsTemplate * pTemplate ) { Win4Assert( 0 != pGetEnvVars ); delete[] _xwc24BitColourMask.Acquire(); _pGetEnvVars = pGetEnvVars; _xwc24BitColourMask.Set(_pGetEnvVars->GetColour().Acquire()); _isItalic = _pGetEnvVars->GetItalic(); _isBold = _pGetEnvVars->GetBold(); _ccCurrLine = 0; _ccMaxLine = _pGetEnvVars->GetFixedFontLineLen(); _pTemplate = pTemplate; _fUseHiliteTags = _pGetEnvVars->GetBeginHiliteTag() && _pGetEnvVars->GetEndHiliteTag(); if ( _fUseHiliteTags ) { _cwcBeginHiliteTag = wcslen( _pGetEnvVars->GetBeginHiliteTag() ); _cwcEndHiliteTag = wcslen( _pGetEnvVars->GetEndHiliteTag() ); } } #define WCHAR_COUNT(x) ( (sizeof(x)/sizeof(WCHAR))-1 ) //+------------------------------------------------------------------------- // // Member: PHttpOutput::OutputHtmlHeader() // // Synopsis: Output the HTML header // //-------------------------------------------------------------------------- void PHttpOutput::OutputHTMLHeader() { if ( 0 == _pTemplate || !_pTemplate->DoesHeaderExist() ) { WCHAR* pwszTemp; static const WCHAR wszHdr1[] = L"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n\n\n"; static const ULONG ccHdr1 = WCHAR_COUNT(wszHdr1); OutputHttp( wszHdr1, ccHdr1 ); static const WCHAR wszHdr2[] = L"Query Results\n\n"; static const ULONG ccHdr2 = WCHAR_COUNT(wszHdr2); OutputHttp( wszHdr2, ccHdr2 ); static const WCHAR wszHdr3[] = L"

\""; static const ULONG ccHdr3 = WCHAR_COUNT(wszHdr3); OutputHttp( wszHdr3, ccHdr3 ); pwszTemp = (WCHAR*) _pGetEnvVars->GetRestriction(); OutputHttpText(pwszTemp, wcslen(pwszTemp)); static const WCHAR wszHdr4[] = L"\" in

\n"; static const ULONG ccHdr4 = WCHAR_COUNT( wszHdr4 ); OutputHttp( wszHdr4, ccHdr4 ); static const WCHAR wszHdr5[] = L"

GetWebHitsFileVPath(); OutputHttp(pwszTemp,wcslen(pwszTemp)); static const WCHAR wszHdr6[] = L"\">"; static const ULONG ccHdr6 = WCHAR_COUNT(wszHdr6); OutputHttp( wszHdr6, ccHdr6 ); OutputHttpText(pwszTemp,wcslen(pwszTemp)); static const WCHAR wszHdr7[] = L"


\n"; static const ULONG ccHdr7 = WCHAR_COUNT(wszHdr7); OutputHttp( wszHdr7, ccHdr7 ); } else { static const WCHAR wszHdr8[] = L"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"; static const ULONG ccHdr8 = WCHAR_COUNT(wszHdr8); OutputHttp( wszHdr8, ccHdr8 ); CVirtualString str; _pTemplate->GetWTXFile().GetHeader( str, _pTemplate->GetVariableSet() ); OutputHttp( str.Get(), str.StrLen() ); } _hasPrintedHeader = TRUE; } //OutputHTMLHeader //+------------------------------------------------------------------------- // // Member: PHttpOutput::OutputHtmlFooter // // Synopsis: Output HTML footer // //-------------------------------------------------------------------------- void PHttpOutput::OutputHTMLFooter() { if ( _isInPreformat ) { static const WCHAR wszTag1[] = L""; static const ULONG ccTag1 = WCHAR_COUNT( wszTag1 ); OutputHttp( wszTag1, ccTag1 ); } if ( 0 == _pTemplate || !_pTemplate->DoesFooterExist() ) { static const WCHAR wszTag2[] = L"\n "; static const ULONG ccTag2 = WCHAR_COUNT( wszTag2 ); OutputHttp( wszTag2, ccTag2 ); } else { CVirtualString str; _pTemplate->GetWTXFile().GetFooter( str, _pTemplate->GetVariableSet() ); OutputHttp( str.Get(), str.StrLen() ); } } //OutputHTMLFooter //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputHilite() // // Arguments: [pwszBuffer] - pointer to the buffer to be output highlited // [cwcBuffLength] - number of characters to print from the buff. // // Synopsis: Output the buffer in highlited form // //+----------------------------------------------------------------------- void PHttpOutput::OutputHilite(const WCHAR* pwszBuffer, ULONG cwcBuffLength) { if ( !_fUseHiliteTags ) { WCHAR wcsColourCodeEnd[] = L""; WCHAR wcsColourTag[50]; swprintf(wcsColourTag,L"",_xwc24BitColourMask.GetPointer()); static const WCHAR wcsBoldBegin[]=L""; static const cwcBoldBegin = wcslen( wcsBoldBegin ); static const WCHAR wcsItalicBegin[]=L""; static const cwcItalicBegin = wcslen( wcsItalicBegin ); static const WCHAR wcsBoldEnd[]=L""; static const cwcBoldEnd = wcslen(wcsBoldEnd); static const WCHAR wcsItalicEnd[]=L""; static const cwcItalicEnd=wcslen(wcsItalicEnd); if (_isBold) { OutputHttp( wcsBoldBegin,cwcBoldBegin); } if (_isItalic) { OutputHttp( wcsItalicBegin, cwcItalicBegin); } OutputHttp(wcsColourTag,wcslen(wcsColourTag)); OutputHttp(pwszBuffer,cwcBuffLength); OutputHttp(wcsColourCodeEnd,wcslen(wcsColourCodeEnd)); if (_isItalic) { OutputHttp( wcsItalicEnd, cwcItalicEnd ); } if (_isBold) { OutputHttp( wcsBoldEnd, cwcBoldEnd ); } } else { OutputHttp( _pGetEnvVars->GetBeginHiliteTag(), _cwcBeginHiliteTag ); OutputHttp( pwszBuffer,cwcBuffLength ); OutputHttp( _pGetEnvVars->GetEndHiliteTag(), _cwcEndHiliteTag ); } } //OutputHilite //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputLeftTag() // // Arguments: [tagParam] - integer to be used in setting destination tag // // Synopsis: Output the "<<" tag, making it refer to "Tag[tagParam]" // //+----------------------------------------------------------------------- void PHttpOutput::OutputLeftTag(int tagParam) { _cwcOutputBuffer = swprintf(_wcOutputBuffer,L"%s\"%s%d\"%s", L"<<"); OutputHttp(_wcOutputBuffer,_cwcOutputBuffer); } //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputRightTag() // // Arguments: [tagParam] - integer to be used in setting destination tag // // Synopsis: Output the ">>" tag, making it refer to "Tag[tagParam]" // //+----------------------------------------------------------------------- void PHttpOutput::OutputRightTag(int tagParam) { _cwcOutputBuffer = swprintf(_wcOutputBuffer, L"%s\"%s%d\"%s",L">> "); OutputHttp(_wcOutputBuffer,_cwcOutputBuffer); } //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputEllipsis() // // Synopsis: Output the ellipsis that separates the truncated text between // two consecutive positions // //+----------------------------------------------------------------------- void PHttpOutput::OutputEllipsis() { OutputHttp(L" ... ",5); } //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputFullHeader() // // Synopsis: Output the additional header information specific to the // full hit-highliting // //+----------------------------------------------------------------------- void PHttpFullOutput::OutputFullHeader() { if ( 0 == _pTemplate || !_pTemplate->DoesHeaderExist() ) { _cwcOutputBuffer = swprintf(_wcOutputBuffer, L"%s\"%s\"%s %s %s\"%s\"%s", L"

", L"<< takes you to the previous hit. ", L" >> takes you to the next hit.

\n"); OutputHttp(_wcOutputBuffer,_cwcOutputBuffer); _cwcOutputBuffer = swprintf(_wcOutputBuffer, L"%s\"%s\"%s %s", L"Click >> to go to the first hit\n", L"


\n" ); OutputHttp(_wcOutputBuffer, _cwcOutputBuffer); } } //OutputFullHeader //+--------------------------------------------------------------------------- // // Member: PHttpOutput::WriteToStdout // // Synopsis: // // Arguments: [pwcsBuffer] - the buffer // [cLength] - count of wide characters // // History: 11-15-96 srikants Created // //---------------------------------------------------------------------------- void PHttpOutput::WriteToStdout( WCHAR const * pwcsBuffer, ULONG cLength ) { if ( 0 != cLength ) _vsResult.StrCat( pwcsBuffer, cLength ); } //WriteToStdout //+--------------------------------------------------------------------------- // // Member: PHttpOutput::Flush // // Synopsis: Flushes the buffer to the web server // // History: 10-10-97 dlee Created // //---------------------------------------------------------------------------- void PHttpOutput::Flush() { if ( 0 != _vsResult.StrLen() ) { DWORD cbToWrite = WideCharToXArrayMultiByte( _vsResult.Get(), _vsResult.StrLen(), _langInfo.GetOutputCodePage(), _mbStr ); if ( 0 != cbToWrite ) _webServer.RawWriteClient( _mbStr.Get(), cbToWrite ); _vsResult.Empty(); } } //Flush //+--------------------------------------------------------------------------- // // Member: PHttpOutput::OutputPreformattedTag // // Synopsis: Outputs the pre-formatted tag if not already emitted. // //---------------------------------------------------------------------------- void PHttpOutput::OutputPreformattedTag() { if ( !_isInPreformat ) { static WCHAR wszTag[] = L"
";
        static const len = ( sizeof(wszTag)/sizeof(WCHAR) ) - 1;

        WriteToStdout( wszTag, len );

        _isInPreformat = TRUE;
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     PHttpOutput::OutputPreFormatRawText
//
//  Synopsis:   Outputs the text for pre-formatted type.
//
//  Arguments:  [pwcsBuffer] -
//              [cLength]    -
//
//  History:    11-15-96   srikants   Created
//
//----------------------------------------------------------------------------

void PHttpOutput::OutputPreFormatRawText( const WCHAR * pwcsBuffer,
                                          ULONG cLength )
{
    Win4Assert( _isInPreformat );

    WCHAR const * pwcsCurrLineBegin = pwcsBuffer;

    for ( ULONG i=0, cwcToWrite = 0;
          i< cLength;
          i++ )
    {
        if ( IsNewLine( pwcsBuffer[i] ) )
        {
            //
            // Empty out the current line - as much as has been
            // accumulated.
            //
            WriteToStdout( pwcsCurrLineBegin, cwcToWrite );
            WriteNewline();


            if ( pwcsBuffer[i] == '\r' && pwcsBuffer[i+1] == '\n' )
                i++;

            _ccCurrLine = cwcToWrite = 0;
            pwcsCurrLineBegin = pwcsBuffer+i+1;  // Position at the next character
        }
        else
        {
            //
            // Include this character in the current line.
            //
            cwcToWrite++;

            //
            // _ccCurrLine is cumulative from multiple invocations.
            //
            _ccCurrLine++;

            if ( _ccCurrLine > _ccMaxLine && iswspace( pwcsBuffer[i] ) )
            {
                //
                // Write out the line and force a line feed.
                //

                WriteToStdout( pwcsCurrLineBegin, cwcToWrite );
                WriteNewline();

                pwcsCurrLineBegin += cwcToWrite;
                Win4Assert( pwcsCurrLineBegin == pwcsBuffer+i+1 );
                _ccCurrLine = cwcToWrite = 0;
            }
        }
    }

    WriteToStdout( pwcsCurrLineBegin, cwcToWrite );
} //OutputPreFormatRawText

void PHttpOutput::WriteNewline()
{
    static WCHAR wcNewLine[] = L"\r\n";
    static const len = (sizeof(wcNewLine)/sizeof(WCHAR))-1;

    WriteToStdout( wcNewLine, len );
}

void PHttpOutput::WriteBreakTag()
{
    static WCHAR wcBreakTag[] = L"
"; static const len = (sizeof(wcBreakTag)/sizeof(WCHAR)) - 1; WriteToStdout( wcBreakTag, len ); } //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputHttp // // Arguments: [pwcsBuffer] - pointer to buffer containing text // [cLength] - the number of characters to print // [fRawText] - if TRUE, convert cr, lf and paragraph // mark to
or newline // // Synopsis: Encapsulates the output operation - for now, writes to stdout // //+----------------------------------------------------------------------- void PHttpOutput::OutputHttp( const WCHAR* pwcsBuffer, ULONG cLength, BOOL fRawText ) { if ( !fRawText ) { // // This is HTML header or footer or formatting. No need to // introduce
tags for new-lines and paragraphs. // WriteToStdout( pwcsBuffer, cLength ); return; } else if ( _isInPreformat ) { // // We can emit cr-lf as cr-lf. No need to convert to
tags. // However, we have to respect the max-line length. // OutputPreFormatRawText( pwcsBuffer, cLength ); return; } // // We must output raw text that is not pre-formatted. We have to // convert the newlines, paragraph separators to BreakTags. // WCHAR const * pwcsCurrLineBegin = pwcsBuffer; for ( ULONG i=0, cwcToWrite = 0; i< cLength; i++ ) { if ( IsNewLine( pwcsBuffer[i] ) ) { WriteToStdout( pwcsCurrLineBegin, cwcToWrite ); WriteBreakTag(); if ( pwcsBuffer[i] == '\r' && pwcsBuffer[i+1] == '\n' ) i++; cwcToWrite = 0; pwcsCurrLineBegin = pwcsBuffer+i+1; } else { cwcToWrite++; } } WriteToStdout( pwcsCurrLineBegin, cwcToWrite ); } //OutputHttp //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputErrorHeader() // // Synopsis: Output an "ERROR" header // //+----------------------------------------------------------------------- void PHttpOutput::OutputErrorHeader() { static const WCHAR wszHdr[] = L"HTTP/1.0 200 OK \r\nContent-Type: text/html\r\n\r\n\n\n"; static const ULONG ccHdr = WCHAR_COUNT( wszHdr ); OutputHttp( wszHdr, ccHdr ); } //+----------------------------------------------------------------------- // // Member: PHttpOutput::OutputErrorMessage // // Synopsis: Send error message to stdout / web page // // Arguments: [pwcsBuffer] -- Error message // [ccBuffer] -- Size in characters of [pwcsBuffer] // // History: 31-Jul-97 KyleP Added header // //+----------------------------------------------------------------------- void PHttpOutput::OutputErrorMessage( WCHAR * pwcsBuffer, ULONG ccBuffer ) { static const WCHAR wszTag1[] = L"

"; static const ULONG ccTag1 = WCHAR_COUNT( wszTag1 ); OutputHttp( wszTag1, ccTag1 ); OutputHttpText( pwcsBuffer, ccBuffer ); static const WCHAR wszTag2[] = L"


"; static const ULONG ccTag2 = WCHAR_COUNT( wszTag2 ); OutputHttp( wszTag2, ccTag2 ); } //+----------------------------------------------------------------------- // // Member: PHttpOutput::TagPosition // // Arguments: [tagParam] - output a tag // // Synopsis: Tag the current position // //+----------------------------------------------------------------------- void PHttpOutput::TagPosition(int tagParam) { _cwcOutputBuffer= swprintf(_wcOutputBuffer,L"%s\"%s%d\"%s", L" "); OutputHttp(_wcOutputBuffer,_cwcOutputBuffer); } //+--------------------------------------------------------------------------- // // Member: PHttpOutput::OutputHttpText // // Synopsis: Outputs the given data as "text" and not as html formatting. // // Arguments: [pwcsBuffer] - buffer to be output // [cchLength] - length of pwcsBuffer // // Notes: Any UNICODE_PARAGRAPH_SEPARATOR characters in the buffer // need to be preserved for use by OutputHttp // // History: 9-09-96 srikants Created // //---------------------------------------------------------------------------- void PHttpOutput::OutputHttpText( WCHAR * pwcsBuffer, ULONG cchLength ) { if ( cchLength > 0 ) { // // NOTE - HTMLEscapeW expects the string to be NULL terminated. It // does not accept a length field. HTMLEscapeW is one of the most // frequently executed routines in idq.dll and so changing it to // optionally take in a length parameter may affect performance. // That is why I have chosen to overwrite the current buffer with // a NULL at cchLength and restore it after the escaping - srikants. // const WCHAR wcTemp = pwcsBuffer[cchLength]; pwcsBuffer[cchLength] = 0; WCHAR * pwcsTmpBuf = pwcsBuffer; WCHAR * pwcsParaMark = wcschr( pwcsTmpBuf, UNICODE_PARAGRAPH_SEPARATOR ); while ( 0 != pwcsParaMark ) { _escapedStr.Empty(); *pwcsParaMark = 0; HTMLEscapeW( pwcsTmpBuf, _escapedStr, _langInfo.GetOutputCodePage() ); _escapedStr.CharCat( UNICODE_PARAGRAPH_SEPARATOR ); OutputHttp( _escapedStr.Get(), _escapedStr.StrLen(), TRUE ); *pwcsParaMark = UNICODE_PARAGRAPH_SEPARATOR; pwcsTmpBuf = pwcsParaMark + 1; pwcsParaMark = wcschr( pwcsTmpBuf, UNICODE_PARAGRAPH_SEPARATOR ); } if (*pwcsTmpBuf) { _escapedStr.Empty(); HTMLEscapeW( pwcsTmpBuf, _escapedStr, _langInfo.GetOutputCodePage() ); OutputHttp( _escapedStr.Get(), _escapedStr.StrLen(), TRUE ); } pwcsBuffer[cchLength] = wcTemp; } } //OutputHttpText //+--------------------------------------------------------------------------- // // Member: CWebhitsTemplate::CWebhitsTemplate // // Synopsis: Constructor of the webhits template file. It takes the // environment variables object and reads in the htx file, // processes it. Also, adds the appropriate replaceable // parameters. // // Arguments: [envVars] - Environment variables to use // [codePage] - Codepage to use for the template // // History: 9-09-96 srikants Created // //---------------------------------------------------------------------------- CWebhitsTemplate::CWebhitsTemplate( CGetEnvVars const & envVars, ULONG codePage ) : _wtxFile( envVars.GetTemplateFileVPath(), envVars.GetTemplateFilePPath(), codePage ) { // // Add the replacable parameters to the variable set. // _variableSet.AddParam( wcsParamCiUrl, envVars.GetWebHitsFileVPath() ); _variableSet.AddParam( wcsParamRestriction, envVars.GetRestriction() ); if (envVars.GetLocale()) _variableSet.AddParam( wcsLocale, envVars.GetLocale() ); if (envVars.GetCodepage()) _variableSet.AddParam( wcsCodepage, envVars.GetCodepage() ); // // Add user definable parameters. // for ( ULONG i = 0; i < CGetEnvVars::eMaxUserReplParams; i++ ) { if ( envVars.GetUserParam(i+1) ) { _variableSet.AddParam( awcsUserParamNames[i], envVars.GetUserParam(i+1) ); } } // // Parse the htx file. // _wtxFile.ParseFile( _variableSet ); } //CWebhitsTemplate