2025-04-27 07:49:33 -04:00

768 lines
21 KiB
C++

// PPUrl.cpp: implementation of the CPPUrl class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "PPUrl.h"
#include "passportexception.h"
#include "pputils.h"
#include "pphandlerbase.h"
#define LONG_DIGITS 10
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CPPUrl::CPPUrl(LPCSTR pszUrl)
{
m_pszQuestion = NULL;
Set(pszUrl);
}
BOOL CPPUrl::GetQParamQuick(LPCSTR qsStart, LPCSTR name, UINT nameStrLen, LPCSTR& qpStart, LPCSTR& qpEnd)
{
_ASSERT(nameStrLen >= 2); // = sign is required
_ASSERT(name[nameStrLen - 1] == '='); // high perf
_ASSERT(name[nameStrLen] != '?'); // high perf
_ASSERT(name[nameStrLen] != '&'); // high perf
_ASSERT(strlen(name) == nameStrLen); // high perf
if (!qsStart) return FALSE;
LPCSTR p = qsStart;
while(p = strstr(p, name))
{
if (p == qsStart || *(p - 1) == '&')
{
qpStart = p + nameStrLen;
break;
}
p += nameStrLen;
}
if (qpStart)
{
qpEnd = (LPSTR)strchr(qpStart, '&');
return TRUE;
}
return FALSE;
}
BOOL CPPUrl::GetQParamQuick(LPCSTR qsStart, LPCSTR name, UINT nameStrLen, INT& value)
{
LPCSTR qpS = NULL;
LPCSTR qpE = NULL;
if(GetQParamQuick(qsStart, name, nameStrLen, qpS, qpE))
{
char qpEChar;
if (qpE != NULL)
{
qpEChar = *(LPSTR)qpE;
*(LPSTR)qpE = 0;
}
value = atoi(qpS);
if (qpE != NULL)
*(LPSTR)qpE = qpEChar;
return TRUE;
}
return FALSE;
}
void CPPUrl::Set(LPCSTR pszUrl)
{
if (NULL != pszUrl)
{
CPPQueryString::Set(pszUrl);
m_pszQuestion = strchr(m_pszBegin, '?');
}
}
void CPPUrl::Set(LPCWSTR pwszUrl)
{
if (NULL != pwszUrl)
{
CPPQueryString::Set(pwszUrl);
m_pszQuestion = strchr(m_pszBegin, '?');
}
}
void CPPUrl::Reinit()
{
if (NULL == m_psz)
{
CPPQueryString::Reinit();
m_pszQuestion = strchr(m_pszBegin, '?');
}
}
void CPPQueryString::Set(LPCSTR lpsz)
{
if (NULL != lpsz)
{
long len = lstrlenA(lpsz);
if (len > (signed) (ATL_URL_MAX_URL_LENGTH-1))
{
throw CPassportException(__FILE__";url length exceeded max",
__LINE__,
E_FAIL,
len,
ATL_URL_MAX_URL_LENGTH);
}
UnlockData();
m_pszBegin = m_psz = LockData();
lstrcpynA(m_psz, lpsz, ATL_URL_MAX_URL_LENGTH);
m_psz += len;
}
}
void CPPQueryString::Set(LPCWSTR lpwsz)
{
if (NULL != lpwsz)
{
long len = lstrlenW(lpwsz);
long lIndex = len;
if (len > (signed) (ATL_URL_MAX_URL_LENGTH-1))
{
throw CPassportException(__FILE__";url length exceeded max",
__LINE__,
E_FAIL,
len,
ATL_URL_MAX_URL_LENGTH);
}
UnlockData(); // balanced the last LockBuffer;
m_pszBegin = m_psz = LockData();
for (;lIndex>0;lIndex--)
{
*m_psz++ = (char) *lpwsz++; // lpcwstr must be true ASCII) !!
}
*m_psz = 0;
}
}
void CPPQueryString::Reinit()
{
if (NULL == m_psz)
{
m_pszBegin = m_psz = LockData();
m_psz += lstrlenA(m_psz);
}
}
void CPPQueryString::Uninit(bool bUnlock)
{
if (bUnlock)
{
UnlockData();
}
else
{
// to deal with anomalies of CString class that doesn't respect preallocate
// during operator=, we need to remain locked when casting to CString *
if (!m_bLockedCString)
LockData();
}
if (NULL != m_psz)
{
m_pszBegin = m_psz = NULL;
}
}
#define TOHEX(a) ((a)>=10 ? 'a'+(a)-10 : '0'+(a))
void CPPQueryString::DoParamAdd(LPCWSTR pwszParamValue, bool fEncoding)
{
long lleng = lstrlenW(pwszParamValue);
long lStartStrLen = (long) (m_psz - m_pszBegin);
if (lleng == 0)
{
return;
}
if (lStartStrLen >= (signed) (ATL_URL_MAX_URL_LENGTH-lleng-1))
{
throw CPassportException( __FILE__";url length exceeded max",
__LINE__,
E_FAIL,
lleng,
ATL_URL_MAX_URL_LENGTH);
}
if (!fEncoding)
{
while (*pwszParamValue)
{
*m_psz++ = (char) *pwszParamValue; // we know this cast is safe for non-unicode wchar
pwszParamValue++;
}
}
else
{
long lIndex = 0;
char ch;
long lEncodedLen = lleng; // assume no encoding happens
UINT uiValue;
while (lIndex < lleng)
{
ch = (char)pwszParamValue[lIndex]; // we know this cast is safe for non-unicode wchar
if ( AtlIsUnsafeUrlChar(ch) )
{
if (ch == ' ')
{
*m_psz++ = '+';
}
else
{
// size of encoding increased by two
lEncodedLen +=2;
// check to see if we are still within the limit
if (lStartStrLen + lEncodedLen >= (signed) (ATL_URL_MAX_URL_LENGTH - 1))
{
throw CPassportException( __FILE__";url length exceeded max",
__LINE__,
E_FAIL,
lEncodedLen,
ATL_URL_MAX_URL_LENGTH);
}
//output the percent, followed by the hex value of the character
*m_psz++ = '%';
uiValue = ch >> 4;
*m_psz++ = TOHEX(uiValue);
uiValue = ch & 0x0f;
*m_psz++ = TOHEX(uiValue);
}
}
else //safe character
{
*m_psz++ = ch;
}
++lIndex;
}
}
*m_psz = 0;
}
void CPPQueryString::DoParamAdd(LPCSTR pszParamValue, bool fEncoding)
{
long lleng = lstrlenA(pszParamValue);
long lStartStrLen = (long) (m_psz - m_pszBegin);
if (lleng == 0)
{
return;
}
if (lStartStrLen >= (signed) (ATL_URL_MAX_URL_LENGTH-lleng-1))
{
throw CPassportException(__FILE__";url length exceeded max",
__LINE__,
E_FAIL,
lleng,
ATL_URL_MAX_URL_LENGTH);
}
if (!fEncoding)
{
lstrcpynA(m_psz, pszParamValue, lleng+1);
m_psz += lleng;
}
else
{
long lIndex = 0;
char ch;
long lEncodedLen = lleng; // assume no encoding happens
UINT uiValue;
while (lIndex < lleng)
{
ch = pszParamValue[lIndex];
if ( AtlIsUnsafeUrlChar(ch) )
{
if (ch == ' ')
{
*m_psz++ = '+';
}
else
{
// size of encoding increased by two
lEncodedLen +=2;
// check to see if we are still within the limit
if (lStartStrLen + lEncodedLen >= (signed) (ATL_URL_MAX_URL_LENGTH - 1))
{
throw CPassportException( __FILE__";url length exceeded max",
__LINE__,
E_FAIL,
lEncodedLen,
ATL_URL_MAX_URL_LENGTH);
}
//output the percent, followed by the hex value of the character
*m_psz++ = '%';
uiValue = ch >> 4;
*m_psz++ = TOHEX(uiValue);
uiValue = ch & 0x0f;
*m_psz++ = TOHEX(uiValue);
}
}
else //safe character
{
*m_psz++ = ch;
}
++lIndex;
}
}
*m_psz = 0;
}
void CPPQueryString::AddQueryParam(LPCSTR pszParamName, LPCWSTR pwszParamValue, bool bTrueUnicode, bool fEncoding)
{
Reinit();
if (NULL == pszParamName)
{
ATLASSERT(FALSE);
//*** assert/exception.
}
long lleng = lstrlenA(pszParamName);
// 3 for '?' or '&', '=' and NULL
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-lleng-3))
{
throw CPassportException( __FILE__";url length exceeded max",
__LINE__,
E_FAIL,
lleng,
ATL_URL_MAX_URL_LENGTH);
}
if (m_psz > m_pszBegin && '?' != *(m_psz-1) && '&' != *(m_psz-1))
{
*m_psz++ = '&';
*m_psz = 0;
}
lstrcpynA(m_psz, pszParamName, lleng+1);
m_psz += lleng;
*m_psz++ = '=';
*m_psz = 0;
if (NULL != pwszParamValue)
{
if (!bTrueUnicode)
{
DoParamAdd(pwszParamValue, fEncoding);
}
else
{
CStringA cszA;
::Unicode2Mbcs(pwszParamValue, 1252 /*ANSI codepage*/, true, cszA);
DoParamAdd(cszA, fEncoding);
}
}
}
void CPPQueryString::AddQueryParam(LPCSTR pszParamName, LPCSTR pszParamValue, bool fEncoding)
{
Reinit();
if (NULL == pszParamName)
{
//*** assert/exception.
}
long lleng = lstrlenA(pszParamName);
// 3 for '?' or '&', '=' and NULL
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-lleng-3))
{
throw CPassportException( __FILE__";url length exceeded max",
__LINE__,
E_FAIL,
lleng,
ATL_URL_MAX_URL_LENGTH);
}
if (m_psz > m_pszBegin && '?' != *(m_psz-1) && '&' != *(m_psz-1))
{
*m_psz++ = '&';
*m_psz = 0;
}
lstrcpynA(m_psz, pszParamName, lleng+1);
m_psz += lleng;
*m_psz++ = '=';
*m_psz = 0;
if (NULL != pszParamValue)
{
DoParamAdd(pszParamValue, fEncoding);
}
}
bool CPPQueryString::StripQueryParam(LPCSTR pszParamName)
{
ATLASSERT(pszParamName);
Reinit();
bool fIncremented;
if (m_pszBegin == m_psz)
{
// No string, so everything is already stripped
return false;
}
long lleng = lstrlenA(pszParamName);
char *pszIndex = m_pszBegin;
while (*pszIndex)
{
// Is this the beginning of a new parameter?
if ((*pszIndex == '?') || (*pszIndex == '&') || (pszIndex == m_pszBegin))
{
// move past '?' or '&'
if ((*pszIndex == '?') || (*pszIndex == '&'))
{
fIncremented = true;
++pszIndex;
}
else
{
fIncremented = false;
}
// is this the param we are looking for?
if (strnicmp(pszIndex, pszParamName, lleng) == 0)
{
// make sure it is an exact match
if (pszIndex[lleng] == '=')
{
// find the end of the param value
char *pszEnd = pszIndex + lleng + 1;
while ((*pszEnd != '&') && *pszEnd)
{
++pszEnd;
}
// Was this the last param
if (!*pszEnd)
{
// if previously incremented, back up one
if (fIncremented)
{
--pszIndex;
}
}
else
{
// need to move params on end
++pszEnd; // move past '&'
while (*pszEnd)
{
*pszIndex = *pszEnd;
++pszIndex;
++pszEnd;
}
}
// null terminate and exit
*pszIndex = '\0';
m_psz = pszIndex;
return true;
}
}
// if this was not incremented, make sure it gets incremented
if (!fIncremented)
{
++pszIndex;
}
}
else
{
++pszIndex;
}
}
return false;
}
void CPPUrl::AddQueryParam(LPCSTR pszParamName, long lValue)
{
Reinit();
char szLong[LONG_DIGITS + 1];
ltoa(lValue, szLong, 10);
AddQueryParam(pszParamName, szLong, false);
}
void CPPUrl::AddQueryParam(LPCSTR pszParamName, LPCSTR pszParamValue, bool fEncoding)
{
Reinit();
if (!m_pszQuestion)
{
// 2 for '?', and NULL
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-2))
{
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
m_pszQuestion = m_psz;
*m_psz++ = '?';
*m_psz = 0;
}
CPPQueryString::AddQueryParam(pszParamName, pszParamValue, fEncoding);
}
void CPPUrl::AddQueryParam(LPCSTR pszParamName, LPCWSTR pwszParamValue, bool bTrueUnicode, bool fEncoding)
{
Reinit();
if (!m_pszQuestion)
{
// 2 for '?', and NULL
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-2))
{
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
m_pszQuestion = m_psz;
*m_psz++ = '?';
*m_psz = 0;
}
CPPQueryString::AddQueryParam(pszParamName, pwszParamValue, bTrueUnicode, fEncoding);
}
void CPPUrl::AppendQueryString(LPCSTR pszQueryString)
{
if (NULL == pszQueryString)
return;
Reinit();
long lLeng = lstrlenA(pszQueryString);
if (!m_pszQuestion)
lLeng++;
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-1-lLeng))
{
ATLASSERT(FALSE);
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
if (!m_pszQuestion)
{
m_pszQuestion = m_psz;
*m_psz++ = '?';
lLeng--;
}
lstrcpyA(m_psz, pszQueryString);
m_psz += lLeng;
}
//pszQueryString doesn't contain leading '?' or '&'
void CPPUrl::InsertBQueryString(LPCSTR pszQueryString)
{
Reinit();
if (!m_pszQuestion)
{
AppendQueryString(pszQueryString);
}
else
{
long lLeng = lstrlenA(pszQueryString);
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-1-lLeng-1))
{
ATLASSERT(FALSE);
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
memmove(m_pszQuestion+1+lLeng+1, m_pszQuestion+1, m_psz-m_pszQuestion-1);
memmove(m_pszQuestion+1, pszQueryString, lLeng);
*(m_pszQuestion+1+lLeng) = '&';
m_psz += lLeng+1;
*m_psz = '\0';
}
}
void CPPUrl::MakeSecure()
{
Reinit();
if (0 == _strnicmp(m_pszBegin, "http:", 5)) // at least it's a http protocol url
{
// make sure there is room for one more char
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-1))
{
ATLASSERT(FALSE);
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
memmove(m_pszBegin+5, m_pszBegin+4, m_psz-m_pszBegin-4);
m_pszBegin[4] = 's';
*(++m_psz) = 0;
if (m_pszQuestion)
{
m_pszQuestion++;
}
}
}
void CPPUrl::MakeNonSecure()
{
Reinit();
if (0 == _strnicmp(m_pszBegin, "https:", 6)) // at least it's a http protocol url
{
memmove(m_pszBegin+4, m_pszBegin+5, m_psz-m_pszBegin-5);
*(--m_psz) = 0;
if (m_pszQuestion)
{
m_pszQuestion--;
}
}
}
void CPPUrl::ReplaceQueryParam(LPCSTR pszParamName, LPCSTR pszParamValue)
{
if (!pszParamName)
{
//todo -- badbad
}
Reinit();
long nlen = lstrlenA(pszParamName);
char *psz = StrStrA(m_pszBegin, pszParamName);
while (psz != NULL && (psz==m_pszBegin || (*(psz-1) != '?' && *(psz-1) != '&') ||
(*(psz+nlen) != '=' && *(psz+nlen) != '&'))) // not a query parameter
{
psz = strstr(psz+nlen, pszParamName);
}
if (NULL == psz)
AddQueryParam(pszParamName, pszParamValue);
else
{
//todo*** figure out the old value length
psz += lstrlenA(pszParamName);
long nold = 0;
if (pszParamValue)
nlen = lstrlenA(pszParamValue);
else
nlen = 0;
if (*psz != '=')
{
ATLASSERT(*psz == '&');
memmove(psz+1+nlen, psz, lstrlenA(psz));
*psz = '=';
if (nlen)
memcpy(psz+1, pszParamValue, nlen);
}
else
{
char *pszNext = StrStrA(psz, "&");
if (NULL == pszNext)
nold = lstrlenA(psz+1);
else
nold = (long) (pszNext-psz-1);
memmove(psz+1+nlen, psz+1+nold, lstrlenA(psz+1+nold));
if (nlen)
memcpy(psz+1, pszParamValue, nlen);
}
}
}
void CPPUrl::MakeFullUrl(LPCSTR pszUrlPath, bool bSecure)
{
Reinit();
CPassportHandlerBase* p = CPassportHandlerBase::GetPPHandler();
ATLASSERT( p != NULL && "this method is limited to a handler.");
CStringA cszA;
if (bSecure)
{
lstrcpyA(m_pszBegin, "https://");
m_psz = m_pszBegin + 8;
}
else
{
lstrcpyA(m_pszBegin, "http://");
m_psz = m_pszBegin + 7;
}
p->GetAServerVariable("SERVER_NAME", cszA);
lstrcpyA(m_psz, cszA);
m_psz += cszA.GetLength();
*(m_psz++) = '/';
*m_psz = '\0';
long len = lstrlenA(pszUrlPath);
if (len)
{
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-1-len))
{
ATLASSERT(FALSE);
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
lstrcpyA(m_psz, pszUrlPath);
m_pszQuestion = strchr(m_psz, '?');
m_psz += len;
}
else
{
m_pszQuestion = NULL;
}
}
void CPPUrl::ReplacePath(LPCSTR pszUrlPath)
{
Reinit();
m_psz = StrStrA(m_pszBegin, "://");
if (NULL == m_psz)
{
ATLASSERT(FALSE); // not good -- it was not a url to begin with
throw CPassportException(__FILE__, __LINE__, E_INVALIDARG, 0, 0, 0);
}
m_psz += 3;
m_psz = StrStrA(m_psz, "/");
if (NULL == m_psz)
{
// url contained host name only. Append first path separator, and then
// new path.
m_psz = m_pszBegin + lstrlenA(m_pszBegin);
*(m_psz++) = '/';
}
else
{
m_psz++;
}
long len = lstrlenA(pszUrlPath);
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-1-len))
{
ATLASSERT(FALSE);
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
lstrcpyA(m_psz, pszUrlPath);
m_psz += len;
m_pszQuestion = strchr(m_pszBegin, '?');
}
CPPUrl & CPPUrl::operator += (LPCSTR pcszAppend)
{
Reinit();
long lLeng = lstrlenA(pcszAppend);
if ((m_psz-m_pszBegin) >= (signed) (ATL_URL_MAX_URL_LENGTH-lLeng))
{
ATLASSERT(FALSE);
throw CPassportException(__FILE__";url too long",__LINE__,E_FAIL);
}
lstrcpynA(m_psz, pcszAppend, lLeng+1);
m_psz += lLeng;
m_pszQuestion = strchr(m_pszBegin, '?');
return *this;
}