240 lines
6.6 KiB
C++
240 lines
6.6 KiB
C++
|
//
|
||
|
// MODULE: APGTSQRY.CPP
|
||
|
//
|
||
|
// PURPOSE: Implementation file for PTS Query Parser
|
||
|
// Fully implements class CHttpQuery, parsing out NAME=VALUE pairs from HTTP query string
|
||
|
//
|
||
|
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
|
||
|
//
|
||
|
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
|
||
|
//
|
||
|
// AUTHOR: Roman Mach
|
||
|
//
|
||
|
// ORIGINAL DATE: 8-2-96
|
||
|
//
|
||
|
// NOTES:
|
||
|
// 1. Based on Print Troubleshooter DLL
|
||
|
// 2. Caller is responsible to assure that all buffers passed in are large enough
|
||
|
//
|
||
|
// Version Date By Comments
|
||
|
//--------------------------------------------------------------------
|
||
|
// V0.1 - RM Original
|
||
|
// V3.1 12/17/98 JM Major cleanup, add Push capablity
|
||
|
//
|
||
|
|
||
|
#pragma warning(disable:4786)
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "apgts.h"
|
||
|
#include "apgtscls.h"
|
||
|
|
||
|
//
|
||
|
//
|
||
|
CHttpQuery::CHttpQuery() :
|
||
|
m_state(ST_GETDATA),
|
||
|
m_nIndex(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//
|
||
|
CHttpQuery::~CHttpQuery()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// INPUT *szInput - this is the URL-encoded query string in which we are searching
|
||
|
// INPUT *pchName - must point to a buffer of size MAXBUF
|
||
|
// OUTPUT *pchName - Typically NAME of a NAME=VALUE pair. Any URL-encoding stripped out.
|
||
|
// Null-terminated. Leading and trailing blanks stripped.
|
||
|
// INPUT *pchValue - must point to a buffer of size MAXBUF
|
||
|
// OUTPUT *pchValue - Typically VALUE of a NAME=VALUE pair. Any URL-encoding stripped out.
|
||
|
// Null-terminated. Leading and trailing blanks stripped.
|
||
|
// RETURN - TRUE ==> more data to come
|
||
|
BOOL CHttpQuery::GetFirst(LPCTSTR szInput, TCHAR *pchName, TCHAR *pchValue)
|
||
|
{
|
||
|
m_state = ST_GETDATA;
|
||
|
m_strInput = szInput;
|
||
|
m_nIndex = 0;
|
||
|
|
||
|
BOOL status = LoopFind(pchName, pchValue);
|
||
|
CleanStr(pchName);
|
||
|
CleanStr(pchValue);
|
||
|
return (status);
|
||
|
}
|
||
|
|
||
|
// Called after a call to CHttpQuery::GetFirst or to this fn has returned true
|
||
|
// INPUT *pchName - must point to a buffer of size MAXBUF
|
||
|
// OUTPUT *pchName - Typically NAME of a NAME=VALUE pair
|
||
|
// Null-terminated. Leading and trailing blanks stripped.
|
||
|
// INPUT *pchValue - must point to a buffer of size MAXBUF
|
||
|
// OUTPUT *pchValue - Typically VALUE of a NAME=VALUE pair
|
||
|
// Null-terminated. Leading and trailing blanks stripped.
|
||
|
// RETURN - TRUE ==> more data to come
|
||
|
BOOL CHttpQuery::GetNext(TCHAR *pchName, TCHAR *pchValue)
|
||
|
{
|
||
|
BOOL status = LoopFind(pchName, pchValue);
|
||
|
CleanStr(pchName);
|
||
|
CleanStr(pchValue);
|
||
|
return (status);
|
||
|
}
|
||
|
|
||
|
// put new content on the front of the unparsed portion of the query string in which we are
|
||
|
// searching.
|
||
|
// Typically, szPushed should consist of 1 or more NAME=VALUE pairs, each terminated by an
|
||
|
// ampersand ("&").
|
||
|
void CHttpQuery::Push(LPCTSTR szPushed)
|
||
|
{
|
||
|
m_state = ST_GETDATA;
|
||
|
m_strInput = CString(szPushed) + m_strInput.Mid(m_nIndex);
|
||
|
m_nIndex = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// RETURN - TRUE ==> more data to come
|
||
|
// INPUT *pchName - must point to a buffer of size MAXBUF
|
||
|
// OUTPUT *pchName - Typically NAME of a NAME=VALUE pair. Any URL-encoding stripped out.
|
||
|
// Null-terminated. May have leading and/or trailing blanks
|
||
|
// INPUT *pchValue - must point to a buffer of size MAXBUF
|
||
|
// OUTPUT *pchValue - Typically VALUE of a NAME=VALUE pair. Any URL-encoding stripped out.
|
||
|
// Null-terminated. May have leading and/or trailing blanks
|
||
|
BOOL CHttpQuery::LoopFind(TCHAR *pchName, TCHAR *pchValue)
|
||
|
{
|
||
|
*pchName = NULL;
|
||
|
*pchValue = NULL;
|
||
|
|
||
|
TCHAR ch;
|
||
|
int val, oldval = 0;
|
||
|
TCHAR temp[20]; // a way bigger buffer than we need
|
||
|
TCHAR *pchPut; // initially points to pchName but can change to point to pchValue
|
||
|
|
||
|
int nLength = m_strInput.GetLength();
|
||
|
|
||
|
if (m_nIndex >= nLength)
|
||
|
return (FALSE);
|
||
|
|
||
|
pchPut = pchName;
|
||
|
|
||
|
while (m_nIndex < nLength)
|
||
|
{
|
||
|
ch = m_strInput[m_nIndex++]; // You might think something related to _tcsinc()
|
||
|
// would be called for to advance m_nIndex. You'd be wrong,
|
||
|
// although the choice would be harmless.
|
||
|
// URL-encoding keeps us within the ASCII character set, so no double-
|
||
|
// byte issues should arise. Besides that, the strings passed in to the
|
||
|
// command line of the troubleshooter controls are even further
|
||
|
// constrained: for example, even in a Japanese-language topic, node
|
||
|
// names will be ASCII.
|
||
|
switch(m_state) {
|
||
|
case ST_GETDATA:
|
||
|
if (ch == _T('&'))
|
||
|
// expect another NAME=VALUE pair
|
||
|
return (TRUE);
|
||
|
else if (ch == _T('=')) {
|
||
|
// Got a name, expect a value
|
||
|
pchPut = pchValue;
|
||
|
break;
|
||
|
}
|
||
|
else if (ch == _T('%'))
|
||
|
// expect to be followed by 2-digit hex
|
||
|
m_state = ST_DECODEHEX1;
|
||
|
else if (ch == _T('+'))
|
||
|
// encoded blank
|
||
|
AddBuffer(_T(' '),pchPut);
|
||
|
else
|
||
|
AddBuffer(ch,pchPut);
|
||
|
break;
|
||
|
case ST_DECODEHEX1:
|
||
|
// first of 2 hex digits
|
||
|
temp[0] = ch;
|
||
|
m_state = ST_DECODEHEX2;
|
||
|
break;
|
||
|
case ST_DECODEHEX2:
|
||
|
// second of 2 hex digits; parse it into a hex value & affix it to *pchPut
|
||
|
temp[1] = ch;
|
||
|
temp[2] = 0;
|
||
|
_stscanf(temp,_T("%02X"),&val);
|
||
|
|
||
|
// reinterpret CR, LF, or CRLF as '\n'
|
||
|
if (val == 0x0A) {
|
||
|
if (oldval != 0x0D)
|
||
|
AddBuffer(_T('\n'),pchPut);
|
||
|
}
|
||
|
else if (val == 0x0D)
|
||
|
AddBuffer(_T('\n'),pchPut);
|
||
|
else
|
||
|
AddBuffer( static_cast<TCHAR>(val), pchPut );
|
||
|
|
||
|
oldval = val;
|
||
|
m_state = ST_GETDATA;
|
||
|
break;
|
||
|
default:
|
||
|
return (FALSE);
|
||
|
}
|
||
|
}
|
||
|
return (TRUE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// append ch to *tostr, with a few subtleties: see comments in body of routine
|
||
|
void CHttpQuery::AddBuffer( TCHAR ch, TCHAR *tostr)
|
||
|
{
|
||
|
if (ch == _T('\t'))
|
||
|
// TAB -> 4 blanks
|
||
|
PutStr(_T(" "),tostr);
|
||
|
else if (ch == _T('\n'))
|
||
|
// blank before newline
|
||
|
PutStr(_T(" \n"),tostr);
|
||
|
else if (ch == _T('<'))
|
||
|
// html: must encrypt left angle bracket.
|
||
|
PutStr(_T("<"),tostr);
|
||
|
else if (ch == _T('>'))
|
||
|
// html: must encrypt right angle bracket.
|
||
|
PutStr(_T(">"),tostr);
|
||
|
else if (ch > 0x7E || ch < 0x20)
|
||
|
// refuse DEL, NUL, and control characters
|
||
|
return;
|
||
|
else {
|
||
|
TCHAR temp[2];
|
||
|
temp[0] = ch;
|
||
|
temp[1] = _T('\0');
|
||
|
PutStr(temp,tostr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// append string *addtostr to string *instr up to a maximum size of MAXBUF-1
|
||
|
// INPUT/OUTPUT *instr
|
||
|
// INPUT *addtostr
|
||
|
// NOTE that this fails silently if total lengths exceed MAXBUF-1 chars
|
||
|
void CHttpQuery::PutStr(LPCTSTR instr, TCHAR *addtostr)
|
||
|
{
|
||
|
if ((_tcslen(instr)+_tcslen(addtostr)) >= (MAXBUF-1)) {
|
||
|
// can't add it to buff
|
||
|
return;
|
||
|
}
|
||
|
_tcscat(addtostr,instr);
|
||
|
}
|
||
|
|
||
|
// Acts upon INPUT/OUTPUT *str - strip any leading control characters and spaces,
|
||
|
// turn any other control characters and spaces into '\0's
|
||
|
/* static */ void CHttpQuery::CleanStr(TCHAR *str)
|
||
|
{
|
||
|
TCHAR temp[MAXBUF], *ptr;
|
||
|
int len;
|
||
|
|
||
|
ptr = str;
|
||
|
while (*ptr > _T('\0') && *ptr <= _T(' '))
|
||
|
ptr = _tcsinc(ptr);
|
||
|
_tcscpy(temp,ptr);
|
||
|
if ((len = _tcslen(temp))!=0) {
|
||
|
ptr = &temp[len-1];
|
||
|
while (ptr > temp) {
|
||
|
if (*ptr > _T('\0') && *ptr <= _T(' '))
|
||
|
*ptr = _T('\0');
|
||
|
else
|
||
|
break;
|
||
|
ptr = _tcsdec(temp, ptr);
|
||
|
}
|
||
|
}
|
||
|
_tcscpy(str,temp);
|
||
|
}
|