532 lines
29 KiB
C++
532 lines
29 KiB
C++
// ==========================================================================
|
||
// Class Implementation : COXURL
|
||
// ==========================================================================
|
||
|
||
// Source file : OXURL.cpp
|
||
|
||
// Version: 9.3
|
||
|
||
// This software along with its related components, documentation and files ("The Libraries")
|
||
// is ?1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
||
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
||
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
||
// to obtain this file, or directly from our office. For a copy of the license governing
|
||
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
||
|
||
// //////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "stdafx.h"
|
||
#include "OXURL.h"
|
||
|
||
#include "UTBStrOp.h"
|
||
|
||
#ifdef _DEBUG
|
||
#undef THIS_FILE
|
||
static char BASED_CODE THIS_FILE[] = __FILE__;
|
||
#endif
|
||
|
||
#define new DEBUG_NEW
|
||
|
||
#pragma warning(disable : 4927)
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// Definition of static members
|
||
|
||
// All string constants will use normal slashes (no back slashes)
|
||
// and lowercase characters
|
||
static const TCHAR szURLPrePrefix[] = _T("url:");
|
||
static const TCHAR szProtocolSuffix[] = _T("://");
|
||
static const TCHAR szFileProtocol[] = _T("file://");
|
||
static const TCHAR szPortPrefix[] = _T(":");
|
||
|
||
const LPCTSTR COXURL::m_pszURLPrePrefix = szURLPrePrefix;
|
||
// --- The (optional) prefix of a URL
|
||
const LPCTSTR COXURL::m_pszProtocolSuffix = szProtocolSuffix;
|
||
// --- The suffix of a protocol
|
||
const LPCTSTR COXURL::m_pszFileProtocol = szFileProtocol;
|
||
// --- The file protocol
|
||
const LPCTSTR COXURL::m_pszPortPrefix = szPortPrefix;
|
||
// --- The prefix of a port
|
||
|
||
const UINT COXURL::m_nUnknownPort = 0;
|
||
// Data members -------------------------------------------------------------
|
||
// protected:
|
||
// CString m_sURL;
|
||
// --- The full URL specification
|
||
// This must be the first data member of this object, so that a dumb cast
|
||
// of a URL object to a LPCTSTR will work.
|
||
// (The first data member of a CString is the LPCTSTR)
|
||
// This is important when working with a variable number of paramaters
|
||
// for a function call (e.g. printf, TRACE etc)
|
||
|
||
// BOOL m_bMainBuilt;
|
||
// --- Whether the main part has been built
|
||
// When this is FALSE the value of m_sURL should not be used
|
||
|
||
// CString m_sProtocol;
|
||
// --- The protocol specification
|
||
// UINT m_nPort;
|
||
// --- The port specification
|
||
// COXUNC m_UNC;
|
||
// --- The UNC specification
|
||
|
||
// BOOL m_bPartsBuilt;
|
||
// --- Whether the parts have been built
|
||
// When this is FALSE the values of m_sProtocol, m_nPort and m_UNC should not be used
|
||
|
||
// private:
|
||
|
||
// Member functions ---------------------------------------------------------
|
||
// public:
|
||
|
||
COXURL::COXURL(LPCTSTR pszURL /* = NULL */)
|
||
{
|
||
m_sURL = pszURL;
|
||
m_UNC.URLPart() = TRUE;
|
||
|
||
SetMainBuilt();
|
||
DestroyParts();
|
||
}
|
||
|
||
COXURL::COXURL(LPCTSTR pszProtocol, int nPort, LPCTSTR pszUNC)
|
||
{
|
||
m_sProtocol = pszProtocol;
|
||
m_nPort = nPort;
|
||
m_UNC = pszUNC;
|
||
m_UNC.URLPart() = TRUE;
|
||
|
||
SetPartsBuilt();
|
||
DestroyMain();
|
||
}
|
||
|
||
COXURL::COXURL(LPCTSTR pszProtocol, int nPort, COXUNC UNC)
|
||
{
|
||
m_sProtocol = pszProtocol;
|
||
m_nPort = nPort;
|
||
m_UNC = UNC;
|
||
m_UNC.URLPart() = TRUE;
|
||
|
||
SetPartsBuilt();
|
||
DestroyMain();
|
||
}
|
||
|
||
COXURL::COXURL(const COXURL& URL)
|
||
{
|
||
m_sURL = URL.m_sURL;
|
||
m_bMainBuilt = URL.m_bMainBuilt;
|
||
|
||
m_sProtocol = URL.m_sProtocol;
|
||
m_nPort = URL.m_nPort;
|
||
m_UNC = URL.m_UNC;
|
||
m_bPartsBuilt = URL.m_bPartsBuilt;
|
||
}
|
||
|
||
COXURL& COXURL::operator=(const COXURL& URL)
|
||
{
|
||
if(this==&URL)
|
||
return *this;
|
||
|
||
m_sURL = URL.m_sURL;
|
||
m_bMainBuilt = URL.m_bMainBuilt;
|
||
|
||
m_sProtocol = URL.m_sProtocol;
|
||
m_nPort = URL.m_nPort;
|
||
m_UNC = URL.m_UNC;
|
||
m_bPartsBuilt = URL.m_bPartsBuilt;
|
||
|
||
return *this;
|
||
}
|
||
|
||
COXURL::operator const CString() const
|
||
{
|
||
COXURL copyThis(*this);
|
||
return copyThis;
|
||
}
|
||
|
||
const CString COXURL::Protocol() const
|
||
{
|
||
COXURL copyThis(*this);
|
||
return copyThis.Protocol();
|
||
}
|
||
|
||
const UINT COXURL::Port() const
|
||
{
|
||
COXURL copyThis(*this);
|
||
return copyThis.Port();
|
||
}
|
||
|
||
const COXUNC COXURL::UNC() const
|
||
{
|
||
COXURL copyThis(*this);
|
||
copyThis.UNC().URLPart() = TRUE;
|
||
|
||
return copyThis.UNC();
|
||
}
|
||
|
||
COXURL& COXURL::operator=(LPCTSTR pszURL)
|
||
{
|
||
operator=(COXURL(pszURL));
|
||
return *this;
|
||
}
|
||
|
||
COXURL::operator CString&()
|
||
{
|
||
BuildMain();
|
||
// ... We pass a reference so the user may change the string
|
||
DestroyParts();
|
||
|
||
return m_sURL;
|
||
}
|
||
|
||
CString& COXURL::Protocol()
|
||
{
|
||
BuildParts();
|
||
// ... We pass a reference so the user may change the string
|
||
DestroyMain();
|
||
|
||
return m_sProtocol;
|
||
}
|
||
|
||
UINT& COXURL::Port()
|
||
{
|
||
BuildParts();
|
||
// ... We pass a reference so the user may change the string
|
||
DestroyMain();
|
||
|
||
return m_nPort;
|
||
}
|
||
|
||
COXUNC& COXURL::UNC()
|
||
{
|
||
BuildParts();
|
||
// ... We pass a reference so the user may change the string
|
||
DestroyMain();
|
||
|
||
return m_UNC;
|
||
}
|
||
|
||
COXURL COXURL::StandardForm(BOOL bMakeLower /* = TRUE */) const
|
||
{
|
||
CString sStandardURL = *this;
|
||
COXUNC::ConvertBackslashToSlash(sStandardURL);
|
||
if (bMakeLower)
|
||
sStandardURL.MakeLower();
|
||
return COXURL(sStandardURL);
|
||
}
|
||
|
||
void COXURL::Empty()
|
||
{
|
||
m_sURL.Empty();
|
||
m_bMainBuilt = FALSE;
|
||
|
||
m_sProtocol.Empty();
|
||
m_nPort = m_nUnknownPort;
|
||
m_UNC.Empty();
|
||
m_bPartsBuilt = FALSE;
|
||
}
|
||
|
||
COXURL::~COXURL()
|
||
{
|
||
}
|
||
|
||
// protected:
|
||
void COXURL::BuildMain()
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Builds the main URL from the parts
|
||
{
|
||
if (IsMainBuilt())
|
||
{
|
||
// ... Re-adjust main data (may have changed)
|
||
AdjustMain();
|
||
return;
|
||
}
|
||
|
||
if (!ArePartsBuilt())
|
||
{
|
||
// Nothing has been built yet, clean everything and set to built
|
||
Empty();
|
||
SetPartsBuilt();
|
||
return;
|
||
}
|
||
|
||
// ... Adjust parts before we use them
|
||
ASSERT(ArePartsBuilt());
|
||
AdjustParts();
|
||
|
||
// Build main data
|
||
if (m_nPort == m_nUnknownPort)
|
||
m_sURL = m_sProtocol + (LPCTSTR)m_UNC;
|
||
else
|
||
{
|
||
CString sPort;
|
||
sPort.Format(_T("%lu"), m_nPort);
|
||
m_sURL = m_sProtocol + m_UNC.Server() + m_pszPortPrefix + sPort +
|
||
m_UNC.Share() + m_UNC.Directory() + m_UNC.File();
|
||
}
|
||
|
||
// ... Adjust main after we have built it
|
||
AdjustMain();
|
||
|
||
// ... Mark that the main data has been built now
|
||
SetMainBuilt();
|
||
return;
|
||
}
|
||
|
||
void COXURL::BuildParts()
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Builds the different parts from the full URL
|
||
{
|
||
if (ArePartsBuilt())
|
||
{
|
||
// ... Re-adjust parts data (may have changed)
|
||
AdjustParts();
|
||
return;
|
||
}
|
||
|
||
if (!IsMainBuilt())
|
||
{
|
||
// Nothing has been built yet, clean everything and set to built
|
||
Empty();
|
||
SetMainBuilt();
|
||
}
|
||
|
||
// ... Adjust main before we use it
|
||
ASSERT(IsMainBuilt());
|
||
AdjustMain();
|
||
|
||
// Build the parts
|
||
|
||
// Search for protocol and port and two parts of the UNC
|
||
// E.g. http://www.test.com:80/page.htm
|
||
// must give : protocol : "http://"
|
||
// port : 80
|
||
// UNC1 : "www.test.com
|
||
// UNC2 : "/page.htm"
|
||
|
||
LPCTSTR pszProtocol= NULL;
|
||
LPCTSTR pszPort = NULL;
|
||
LPCTSTR pszUNC1 = NULL;
|
||
LPCTSTR pszUNC2 = NULL;
|
||
|
||
UINT nProtocolLength = 0;
|
||
UINT nPortLength = 0;
|
||
UINT nUNC1Length = 0;
|
||
UINT nUNC2Length = 0;
|
||
|
||
// Remove possible URL preprefix ("URL:")
|
||
if (m_sURL.Left((int)_tcslen(m_pszURLPrePrefix)) == m_pszURLPrePrefix)
|
||
m_sURL = m_sURL.Mid((int)_tcslen(m_pszURLPrePrefix));
|
||
|
||
// Initialize UNC (if there is no protocol or port)
|
||
ASSERT(IsMainBuilt());
|
||
pszUNC1 = m_sURL;
|
||
nUNC1Length = m_sURL.GetLength();
|
||
|
||
// Search for protocol
|
||
// ... First make a copy with only normal slashes (no backslashes) and lowercase characters
|
||
// This copy will only be used to search
|
||
CString sURLCopy = m_sURL;
|
||
sURLCopy.MakeLower();
|
||
COXUNC::ConvertBackslashToSlash(sURLCopy);
|
||
|
||
// ... Now search for protocol suffix
|
||
int nProtocolPos = sURLCopy.Find(m_pszProtocolSuffix);
|
||
if (0 <= nProtocolPos)
|
||
{
|
||
// ... Store pointer to protocol
|
||
pszProtocol = m_sURL;
|
||
nProtocolLength = (UINT)(nProtocolPos + _tcslen(m_pszProtocolSuffix));
|
||
// ... Move UNC1
|
||
pszUNC1 += nProtocolLength;
|
||
nUNC1Length -= nProtocolLength;
|
||
// ... Check string boundaries
|
||
ASSERT(((LPCTSTR)m_sURL <= pszUNC1) && (pszUNC1 + nUNC1Length <= (LPCTSTR)m_sURL + m_sURL.GetLength()));
|
||
ASSERT(((LPCTSTR)m_sURL <= pszProtocol) && (pszProtocol + nProtocolLength <= (LPCTSTR)m_sURL + m_sURL.GetLength()));
|
||
}
|
||
|
||
// Search for port number
|
||
|
||
// Search for the first slash after a non-slash character
|
||
// Then search back for a colon
|
||
// E.g. //Comp12:80/page.htm
|
||
// will result in 80
|
||
LPCTSTR pszPortCopyStart = (LPCTSTR)sURLCopy + (pszUNC1 - (LPCTSTR)m_sURL);
|
||
LPCTSTR pszPortCopySearch = pszPortCopyStart;
|
||
LPCTSTR pszPortCopyEnd = NULL;
|
||
// ... Skip leading slashes
|
||
while (*pszPortCopySearch == COXUNC::m_cSlash)
|
||
pszPortCopySearch++;
|
||
ASSERT(*pszPortCopySearch != COXUNC::m_cSlash);
|
||
|
||
// ... Skip non-slash characters
|
||
while ( (*pszPortCopySearch != COXUNC::m_cNull) && (*pszPortCopySearch != COXUNC::m_cSlash))
|
||
pszPortCopySearch++;
|
||
pszPortCopyEnd = pszPortCopySearch;
|
||
ASSERT( (*pszPortCopySearch == COXUNC::m_cNull) || (*pszPortCopySearch == COXUNC::m_cSlash));
|
||
ASSERT( (*pszPortCopyEnd == COXUNC::m_cNull) || (*pszPortCopyEnd == COXUNC::m_cSlash));
|
||
|
||
// ... Go back until we find a colon (or the leading slash again)
|
||
pszPortCopySearch--;
|
||
while ( (pszPortCopyStart < pszPortCopySearch) && (*pszPortCopySearch != COXUNC::m_cColon))
|
||
pszPortCopySearch--;
|
||
if (*pszPortCopySearch == COXUNC::m_cColon)
|
||
{
|
||
// ... Colon position from the beginning of the search
|
||
int nColonPos = (int)(pszPortCopySearch - pszPortCopyStart);
|
||
// ... Set port right after colon
|
||
ASSERT((*pszPortCopyEnd == COXUNC::m_cNull) || (*pszPortCopyEnd == COXUNC::m_cSlash));
|
||
pszPort = pszUNC1 + nColonPos + 1;
|
||
nPortLength = (UINT)( pszPortCopyEnd - pszPortCopySearch - 1);
|
||
// ... Ignore ports that contain non-numeric characters
|
||
// (According to the URL specification a port may only consist of digits : 0-9)
|
||
for (UINT nPortIndex = 0; nPortIndex < nPortLength; nPortIndex++)
|
||
{
|
||
if (!_istdigit(pszPort[nPortIndex]))
|
||
nPortLength = 0;
|
||
}
|
||
// ... Check string boundaries
|
||
ASSERT(((LPCTSTR)m_sURL <= pszPort) && (pszPort + nPortLength <= (LPCTSTR)m_sURL + m_sURL.GetLength()));
|
||
|
||
// ... nPortLength may be 0 if colon is immediately followed by slash or backslash
|
||
if (nPortLength != 0)
|
||
{
|
||
// Adjust UNC1 and UNC2
|
||
pszUNC2 = pszPort + nPortLength;
|
||
nUNC2Length = nUNC1Length - (pszUNC2 - pszUNC1);
|
||
nUNC1Length = (UINT)(pszPort - pszUNC1 - 1);
|
||
// ... Check string boundaries
|
||
ASSERT(((LPCTSTR)m_sURL <= pszUNC1) && (pszUNC1 + nUNC1Length <= (LPCTSTR)m_sURL + m_sURL.GetLength()));
|
||
ASSERT(((LPCTSTR)m_sURL <= pszUNC2) && (pszUNC2 + nUNC2Length<= (LPCTSTR)m_sURL + m_sURL.GetLength()));
|
||
}
|
||
}
|
||
|
||
// The sum of all parts must result in the total UNC
|
||
ASSERT((UINT)m_sURL.GetLength() == nProtocolLength + (nPortLength == 0 ? 0 : nPortLength + 1) + nUNC1Length + nUNC2Length);
|
||
// ... Assign protocol
|
||
if (pszProtocol != NULL)
|
||
UTBStr::tcsncpy(m_sProtocol.GetBufferSetLength(nProtocolLength), nProtocolLength+1, pszProtocol, nProtocolLength);
|
||
else
|
||
m_sProtocol.Empty();
|
||
|
||
// ... Assign port
|
||
if (pszPort != NULL)
|
||
m_nPort = _ttol(pszPort);
|
||
else
|
||
m_nPort = m_nUnknownPort;
|
||
|
||
// ... Assign UNC
|
||
CString sUNC;
|
||
sUNC.GetBufferSetLength(nUNC1Length + nUNC2Length);
|
||
if (pszUNC1 != NULL)
|
||
UTBStr::tcsncpy(sUNC.GetBuffer(0), nUNC1Length+1, pszUNC1, nUNC1Length);
|
||
if (pszUNC2 != NULL)
|
||
UTBStr::tcsncpy(sUNC.GetBuffer(0) + nUNC1Length, nUNC2Length+1, pszUNC2, nUNC2Length);
|
||
m_UNC = COXUNC(sUNC, TRUE);
|
||
|
||
// ... Adjust parts after we have built them
|
||
AdjustParts();
|
||
|
||
// ... Mark that the parts data has been built now
|
||
SetPartsBuilt();
|
||
return;
|
||
}
|
||
|
||
void COXURL::AdjustMain()
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Adjust the main (full URL)
|
||
{
|
||
Trim(m_sURL);
|
||
}
|
||
|
||
void COXURL::AdjustParts()
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Adjust the URL parts
|
||
{
|
||
Trim(m_sProtocol);
|
||
|
||
// Adjust Protocol
|
||
// ... Is protocol exists, make sure it ends with "://"
|
||
if (!m_sProtocol.IsEmpty())
|
||
{
|
||
// First check trailng slashes
|
||
LPCTSTR pszFirstSlash = NULL;
|
||
LPCTSTR pszSecondSlash = NULL;
|
||
pszFirstSlash = _tcspbrk(m_sProtocol, COXUNC::m_pszSlashes);
|
||
if (pszFirstSlash != NULL)
|
||
pszSecondSlash = _tcspbrk(pszFirstSlash + 1, COXUNC::m_pszSlashes);
|
||
if (pszFirstSlash == NULL)
|
||
{
|
||
// ... No slash present add two
|
||
m_sProtocol += COXUNC::m_cSlash;
|
||
m_sProtocol += COXUNC::m_cSlash;
|
||
}
|
||
else if (pszSecondSlash != pszFirstSlash + 1)
|
||
{
|
||
// Second slash does not immediately follow the first
|
||
TCHAR cSlash = *pszFirstSlash;
|
||
// ... One slash present, trucate after this slash
|
||
m_sProtocol = m_sProtocol.Left((int)(pszFirstSlash - (LPCTSTR)m_sProtocol + 1));
|
||
// ... Add a second slash
|
||
m_sProtocol += cSlash;
|
||
}
|
||
|
||
// Check for colon in fraont of the two slashes
|
||
LPCTSTR pszColon = _tcschr(m_sProtocol, COXUNC::m_cColon);
|
||
pszFirstSlash = _tcspbrk(m_sProtocol, COXUNC::m_pszSlashes);
|
||
ASSERT(pszFirstSlash != NULL);
|
||
ASSERT(2 <= m_sProtocol.GetLength());
|
||
if (pszColon != pszFirstSlash - 1)
|
||
{
|
||
// ... Colon not in front of trailing slashes
|
||
// Remove all colons
|
||
int nColonPos = m_sProtocol.Find(COXUNC::m_cColon);
|
||
while (0 <= nColonPos)
|
||
{
|
||
m_sProtocol = m_sProtocol.Left(nColonPos) + m_sProtocol.Mid(nColonPos + 1);
|
||
nColonPos = m_sProtocol.Find(COXUNC::m_cColon);
|
||
}
|
||
|
||
// Add colon in front of trailing slashes
|
||
pszFirstSlash = _tcspbrk(m_sProtocol, COXUNC::m_pszSlashes);
|
||
m_sProtocol = m_sProtocol.Left((int)(pszFirstSlash - (LPCTSTR)m_sProtocol)) +
|
||
COXUNC::m_cColon + m_sProtocol.Right(2);
|
||
}
|
||
}
|
||
|
||
// Adjust Port
|
||
// ... Nothing to adjust
|
||
|
||
// Adjust UNC
|
||
m_UNC.AdjustParts();
|
||
}
|
||
|
||
void COXURL::Trim(CString& sText)
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Removes invalid characters and leading and trailing spaces
|
||
{
|
||
// ... Remove invalid chars <>|"
|
||
int nInvalidCharPos = sText.FindOneOf(COXUNC::m_pszInvalidChars);
|
||
while (0 <= nInvalidCharPos)
|
||
{
|
||
sText = sText.Left(nInvalidCharPos) + sText.Mid(nInvalidCharPos + 1);
|
||
nInvalidCharPos = sText.FindOneOf(COXUNC::m_pszInvalidChars);
|
||
}
|
||
|
||
// ... Remove leading and trailng spaces
|
||
sText.TrimLeft();
|
||
sText.TrimRight();
|
||
}
|
||
|
||
// private:
|
||
|
||
// ==========================================================================
|