hxurl.cpp
Upload User: zhongxx05
Upload Date: 2007-06-06
Package Size: 33641k
Code Size: 35k
Category:

Symbian

Development Platform:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK ***** 
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location: 
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s): 
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include "hlxclib/string.h"
  36. #include "hlxclib/stdlib.h"
  37. //#include "hlxclib/stdio.h"
  38. #include <ctype.h>
  39. #include "hxcom.h"
  40. #include "hxtypes.h"
  41. #include "hxresult.h"
  42. #include "hxcomm.h"
  43. #include "tparse.h"
  44. #include "dbcs.h"
  45. #include "protdefs.h"
  46. #include "hxstrutl.h"
  47. #include "hxslist.h"
  48. #include "hxurl.h"
  49. #include "ihxpckts.h"
  50. #include "chxminiccf.h"
  51. #include "hxheap.h"
  52. #ifdef _DEBUG
  53. #undef HX_THIS_FILE
  54. static const char HX_THIS_FILE[] = __FILE__;
  55. #endif
  56. /* We should really define it in a common header file */
  57. #if defined (_WINDOWS ) || defined (WIN32) || defined(_SYMBIAN)
  58. #define OS_SEPARATOR_CHAR '\'
  59. #define OS_SEPARATOR_STRING "\"
  60. #elif defined (_UNIX) || defined(_OPENWAVE)
  61. #define OS_SEPARATOR_CHAR '/'
  62. #define OS_SEPARATOR_STRING "/"
  63. #elif defined (_MACINTOSH)
  64. #define OS_SEPARATOR_CHAR ':'
  65. #define OS_SEPARATOR_STRING ":"
  66. #else
  67. #error "undefined platform hxurl.cpp"
  68. #endif // defined (_WINDOWS ) || defined (WIN32)
  69. CHXURL::CHXURL (const char* pszURL)
  70. :m_LastError (HXR_OK)
  71. ,m_pActualURL(NULL)
  72.                 ,m_pszURL(NULL)
  73.                 ,m_pszEscapedURL(NULL)
  74. ,m_pszOptions (NULL)
  75. ,m_pszHost (NULL)
  76. ,m_pszPort (NULL)
  77. ,m_pszUsername(NULL)
  78. ,m_pszPassword(NULL)
  79. ,m_unProtocol(fileProtocol)
  80.                 ,m_unDefaultPort(0)
  81. ,m_bNetworkProtocol (FALSE)
  82. ,m_pszResource (NULL)
  83. ,m_pProperties (NULL)
  84. ,m_pOptions (NULL)
  85.                 ,m_pCCF(CreateCCF())
  86. {
  87.     if (m_pCCF)
  88.     {
  89. m_pCCF->AddRef();
  90.     }
  91.     
  92.     ConstructURL(pszURL);
  93. }
  94. CHXURL::CHXURL (const char* pszURL, IUnknown* pContext)
  95. :m_LastError (HXR_OK)
  96. ,m_pActualURL(NULL)
  97.                 ,m_pszURL(NULL)
  98.                 ,m_pszEscapedURL(NULL)
  99. ,m_pszOptions (NULL)
  100. ,m_pszHost (NULL)
  101. ,m_pszPort (NULL)
  102. ,m_pszUsername(NULL)
  103. ,m_pszPassword(NULL)
  104. ,m_unProtocol(fileProtocol)
  105.                 ,m_unDefaultPort(0)
  106. ,m_bNetworkProtocol (FALSE)
  107. ,m_pszResource (NULL)
  108. ,m_pProperties (NULL)
  109. ,m_pOptions (NULL)
  110.                 ,m_pCCF(0)
  111. {
  112.     if (pContext)
  113.     {
  114. pContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&m_pCCF);
  115.     }
  116.     
  117.     ConstructURL(pszURL);
  118. }
  119. void CHXURL::ConstructURL(const char* pszURL)
  120. {
  121.     char*   pszInputURL = NULL;
  122.     char*   pszTemp = NULL;
  123.     char*   pFragment = NULL;
  124.     char*   pNewURL = NULL;
  125.     char*   pResource = NULL;
  126.     char*   pszDollarSign = NULL;
  127.     HX_ASSERT(pszURL != NULL);
  128.     if (!pszURL)
  129.     {
  130. m_LastError = HXR_INVALID_PATH;
  131. return;
  132.     }
  133.     
  134.     if (!m_pCCF)
  135.     {
  136. m_LastError = HXR_UNEXPECTED;
  137. return;
  138.     }
  139.     pszInputURL = new char[strlen(pszURL) + 1];
  140.     if(!pszInputURL)
  141.     {
  142.         m_LastError = HXR_OUTOFMEMORY;
  143.         return;
  144.     }
  145.     strcpy(pszInputURL, pszURL); /* Flawfinder: ignore */
  146.     // Keep permanent copy of input url
  147.     m_pszEscapedURL = new char[strlen(pszInputURL)+1];
  148.     strcpy(m_pszEscapedURL, pszInputURL); /* Flawfinder: ignore */
  149.     // IHXValues 
  150.     if (m_pCCF)
  151.     {
  152. m_pCCF->CreateInstance(CLSID_IHXValues, (void**)&m_pProperties);
  153. m_pCCF->CreateInstance(CLSID_IHXValues, (void**)&m_pOptions);
  154.     }
  155.     // protocol: determine whether it's network or local
  156.     if (0 == StringNCompare(pszInputURL, "http:", 5))
  157.     {
  158. m_unProtocol = httpProtocol;
  159. m_unDefaultPort = DEFAULT_HTTP_PORT;
  160.     }
  161.     else if (0 == StringNCompare(pszInputURL, "chttp:", 6))
  162.     {
  163. m_unProtocol = httpProtocol;
  164. m_unDefaultPort = DEFAULT_HTTP_PORT;
  165.     }
  166.     else if (0 == StringNCompare(pszInputURL, "pnm:", 4))
  167.     {
  168. m_unProtocol = pnmProtocol;
  169. m_bNetworkProtocol = TRUE;
  170. m_unDefaultPort = DEFAULT_PNA_PORT;
  171.     }
  172.     else if (0 == StringNCompare(pszInputURL, "rtsp:", 5))
  173.     {
  174. m_unProtocol = rtspProtocol;
  175. m_bNetworkProtocol = TRUE;
  176. m_unDefaultPort = DEFAULT_RTSP_PORT;
  177.     }
  178.     else if (0 == StringNCompare(pszInputURL, "helix-sdp:", 10))
  179.     {
  180. m_unProtocol = helixSDPProtocol;
  181. m_bNetworkProtocol = TRUE;
  182. m_unDefaultPort = DEFAULT_RTSP_PORT;
  183.     }
  184.     else if (0 == StringNCompare(pszInputURL, "https:", 6))
  185.     {
  186. m_unProtocol = httpsProtocol;
  187. m_unDefaultPort = DEFAULT_HTTPS_PORT;
  188.     }
  189.     if (m_pProperties)
  190.     {
  191. m_pProperties->SetPropertyULONG32(PROPERTY_PROTOCOL, (ULONG32)m_unProtocol);
  192.     }
  193.     else
  194.     {
  195. m_LastError = HXR_UNEXPECTED;
  196. goto cleanup;
  197.     }
  198.     // no need to further parse helix-sdp protocol
  199.     if (helixSDPProtocol != m_unProtocol)
  200.     {
  201.         // separate fragment from the URL
  202.         if (0!=(pszTemp = (char*) ::HXFindChar(pszInputURL, '#')))
  203.         {
  204.     // save fragment
  205.     pFragment = pszTemp + 1;
  206.     ::SaveStringToHeader(m_pProperties, PROPERTY_FRAGMENT, pFragment);
  207.     *pszTemp = '';
  208.         }
  209.         // HP - allow '$' in directory/file name
  210.         //
  211.         // still need to take care of that obsolete $ sign option:
  212.         // rtsp://moe.cr.prognet.com/ambush.rm$1:00
  213.         // time after the $ is assumed to be the start time.
  214.         //
  215.         // the solution is to compare the string following the $ to 
  216.         // a properly formed time. If the string is a time and only 
  217.         // a time, then we know its the old-style start-time option
  218.         // otherwise, '$' is part of the directory/file name and we
  219.         // will keep it.
  220.         pszDollarSign = (char*) ::HXFindChar(pszInputURL, '$');
  221.         while (pszDollarSign)
  222.         {
  223.     pszTemp = pszDollarSign + 1;
  224.     if (::TimeParse(pszTemp))
  225.     {
  226.         *pszDollarSign = '';
  227.         INT32 lLen = (2 * strlen(pszURL)) + 8;
  228.         pNewURL = new char[lLen];
  229.         memset(pNewURL, 0, lLen);
  230.           
  231.         // upgrade to a new URL
  232.         SafeSprintf(pNewURL, lLen, "%s?start=%s", pszInputURL, pszTemp);
  233.         HX_VECTOR_DELETE(pszInputURL);
  234.         pszInputURL = pNewURL;
  235.         break;
  236.     }
  237.     pszDollarSign = (char*) ::HXFindChar(pszTemp, '$');
  238.         }
  239.     }
  240. #if !defined(_MACINTOSH) && !defined(_MAC_UNIX)
  241.     // on Mac, unescaping can put /'s and ?'s back into file and folder names, which is deadly inside URLs
  242.     //if (0 == StringNCompare(pszInputURL, "file:", 5))
  243.     {
  244. // we only unescape the URL on local source since we are
  245. // *responding* instead of *requesting* for the given source
  246. Unescape(pszInputURL);
  247.     }
  248. #endif
  249.     if (!CompressURL(pszInputURL, m_pszURL))
  250.     {
  251. // make a copy of the URL
  252. m_pszURL = new char[strlen(pszInputURL)+1];
  253. strcpy(m_pszURL, pszInputURL); /* Flawfinder: ignore */
  254.     }
  255.     m_pActualURL = new char[strlen(m_pszURL)+1];
  256.     strcpy(m_pActualURL, m_pszURL); /* Flawfinder: ignore */
  257.     ::SaveStringToHeader(m_pProperties, PROPERTY_URL, m_pszURL);
  258.     // no need to further parse helix-sdp protocol
  259.     if (helixSDPProtocol != m_unProtocol)
  260.     {
  261.         // separate options from the URL
  262.         if (0 != (pszTemp = (char*) ::HXFindChar(m_pszURL, '?')))
  263.         {
  264.     // options
  265.     m_pszOptions = pszTemp + 1;
  266.         }
  267.        
  268.         // collect protocol, host, port and resource info
  269.         ParseURL (m_pszURL);
  270.     
  271.         // collect other options info if it has 
  272.         if (m_pszOptions)
  273.         {
  274.     if (HXR_INCOMPLETE == CollectOptions(m_pszOptions) && m_pszResource)
  275.     {
  276.              // bad options and remove it from the URL
  277.              pszTemp = (char*) ::HXFindChar(m_pszResource, '?');
  278.         if (pszTemp)
  279.         {
  280.     *pszTemp = '';
  281.     ParseResource();
  282.         }
  283.     }
  284.         }   
  285.     }
  286. cleanup:
  287.     HX_VECTOR_DELETE(pszInputURL);
  288. }
  289. CHXURL::~CHXURL ()
  290. {
  291.     HX_VECTOR_DELETE(m_pActualURL);
  292.     HX_VECTOR_DELETE(m_pszURL);
  293.     HX_VECTOR_DELETE(m_pszEscapedURL);
  294.     HX_RELEASE(m_pProperties);
  295.     HX_RELEASE(m_pOptions);
  296.     HX_RELEASE(m_pCCF);
  297. }
  298. CHXURL::CHXURL(const CHXURL& rhs) 
  299.     :m_LastError (HXR_OK)
  300.     ,m_pActualURL(NULL)
  301.     ,m_pszURL(NULL)
  302.     ,m_pszOptions (NULL)
  303.     ,m_pszHost (NULL)
  304.     ,m_pszPort (NULL)
  305.     ,m_pszUsername(NULL)
  306.     ,m_pszPassword(NULL)
  307.     ,m_unProtocol (fileProtocol)
  308.     ,m_bNetworkProtocol (FALSE)
  309.     ,m_pszResource (NULL)
  310.     ,m_pProperties (NULL)
  311.     ,m_pOptions (NULL)
  312.     ,m_pCCF(rhs.m_pCCF)
  313. {
  314.     if (m_pCCF)
  315.     {
  316. m_pCCF->AddRef();
  317.     }
  318.     ConstructURL(rhs.GetEscapedURL());
  319. }
  320. CHXURL& CHXURL::operator=(const CHXURL& rhs)
  321. {
  322.     if (&rhs != this)
  323.     {
  324. // Clean out old data
  325. HX_VECTOR_DELETE(m_pActualURL);
  326. HX_VECTOR_DELETE(m_pszURL);
  327.         HX_VECTOR_DELETE(m_pszEscapedURL);
  328. HX_RELEASE(m_pProperties);
  329. HX_RELEASE(m_pOptions);
  330. HX_RELEASE(m_pCCF);
  331. m_LastError = HXR_OK;
  332. m_pszOptions = NULL;
  333. m_pszHost = NULL;
  334. m_pszPort = NULL;
  335. m_pszUsername = NULL;
  336. m_pszPassword = NULL;
  337. m_unProtocol = fileProtocol;
  338. m_bNetworkProtocol = FALSE;
  339. m_pszResource = NULL;
  340. ConstructURL(rhs.GetEscapedURL());
  341. m_pCCF = rhs.m_pCCF;
  342. if (m_pCCF)
  343. {
  344.     m_pCCF->AddRef();
  345. }
  346.     }
  347.     return *this;
  348. }
  349. HX_RESULT CHXURL::ParseURL (char* pszURL)
  350. {
  351.     char* pszOption = NULL;
  352.     char* pszTemp = NULL;
  353.     char* pszSlash = NULL;
  354.     char* pszCursor = NULL;    
  355.     if (HXR_OK != m_LastError)
  356.     {
  357. goto cleanup;
  358.     }
  359.     pszCursor = pszURL;
  360.     
  361.     //
  362.     // let's start
  363.     //
  364.     // find the scheme - note that ParseURL only succeeds
  365.     // for absolute URLs
  366.     m_LastError = HXR_INVALID_PROTOCOL; // assume the worst
  367.     pszTemp = (char *) FindURLSchemeEnd(pszURL);
  368.     if (pszTemp)
  369.     {
  370. char* pScheme = new_string(pszURL, pszTemp - pszURL);
  371. m_LastError = HXR_OK;
  372. ::SaveStringToHeader(m_pProperties, PROPERTY_SCHEME, pScheme);
  373. delete[] pScheme;
  374.     }
  375.     if(HXR_OK != m_LastError)
  376.     {
  377. goto cleanup;
  378.     }
  379.     pszCursor = pszTemp + 1;
  380.   
  381.     if(strncmp(pszCursor, "//", 2) == 0)
  382.     {
  383. pszCursor += 2; // skip '//'
  384.     }
  385.     else if(strncmp(pszCursor, "/", 1) == 0)
  386.     {
  387. pszCursor += 1; // skip '/' (ill-formed url?)
  388.     }
  389.     if (fileProtocol == m_unProtocol)
  390.     {
  391. // resource
  392. if (*(m_pszResource = pszCursor) == '')
  393. {
  394.     m_LastError = HXR_INVALID_URL_PATH;
  395.     goto cleanup;
  396. }
  397.     }
  398.     // network URL + http
  399.     else
  400.     {
  401. // First check for optional username and password parameters.
  402. // The colon is also optional if either username or password
  403. // is not given Form in context:
  404. // protocol://username:password@host:port/resource (From RFC
  405. // 1738)
  406. pszTemp = (char*) ::HXFindChar(pszCursor, ':');
  407. pszSlash = (char*) ::HXFindChar(pszCursor, '/');
  408. pszOption = (char*) ::HXFindChar(pszCursor, '@');
  409. // There is a username or password if we see a '@' character
  410. // according to RFC 1738 this is a reserved character
  411. if (pszOption && pszOption < pszSlash)
  412. {
  413.     // Username
  414.     if (*(m_pszUsername = pszCursor) == '')
  415.     {
  416. m_LastError = HXR_INVALID_URL_HOST;
  417. goto cleanup;
  418.     }
  419.     // If the is a ':' and it is before the '@' then we have a
  420.     // password, so zero terminate the username and move the
  421.     // cursor to the password
  422.     if (pszTemp && (pszTemp < pszOption))
  423.     {
  424. *pszTemp = '';
  425. pszCursor = pszTemp+1;
  426.     }
  427.     // There is no ':' so move the cursor to the '@' character
  428.     // so it will be a zero terminated empty string
  429.     else
  430. pszCursor = pszOption;
  431.     // Password
  432.     if (*(m_pszPassword = pszCursor) == '')
  433.     {
  434. m_LastError = HXR_INVALID_URL_HOST;
  435. goto cleanup;
  436.     }
  437.     // Zero terminate password and move the cursor to the hostname
  438.     *pszOption = '';
  439.     if (m_pszUsername)
  440.     {
  441. ::SaveStringToHeader(m_pProperties, PROPERTY_USERNAME, m_pszUsername);
  442.     }
  443.     if (m_pszPassword)
  444.     {
  445. ::SaveStringToHeader(m_pProperties, PROPERTY_PASSWORD, m_pszPassword);
  446.     }
  447.     pszCursor = pszOption+1;
  448. }
  449. // host
  450. if (*(m_pszHost = pszCursor) == '')
  451. {
  452.     m_LastError = HXR_INVALID_URL_HOST;
  453.     goto cleanup;
  454. }
  455. // port
  456. pszTemp = (char*) ::HXFindChar(pszCursor, '/');
  457. if (pszTemp)
  458. {
  459.     *pszTemp = '';
  460.     pszCursor = pszTemp+1;
  461. }
  462. else
  463. {
  464.     // it's legal to have RTSP or HTTP URLs with no resource.
  465.             // The correct behavior for RTSP would be not to flag this as
  466.             // an error but the client core treats the lack of a resource
  467.             // as a failure. The correct change would be to have the client
  468.             // core check explicitly for a resource but legacy code rears its
  469.             // ugly head... so we flag the error (client core is happy) but
  470.             // go ahead and parse the rest of the headers (so you can still 
  471.             // get the host:port if you need to). */
  472.     if (m_unProtocol != httpProtocol && m_unProtocol != rtspProtocol)
  473.     {
  474. m_LastError = HXR_INVALID_URL_PATH;                
  475.     }
  476.     pszCursor = NULL;
  477. }
  478. // port
  479. pszTemp = (char*) ::HXFindChar(m_pszHost, ':');
  480. if (pszTemp)
  481. {
  482.     *pszTemp = '';
  483.   
  484.     if (*(m_pszPort = pszTemp+1) == '')
  485.     {
  486. m_LastError = HXR_INVALID_URL_HOST;
  487. goto cleanup;
  488.     }
  489. }
  490. if (m_pszHost)
  491. {
  492.     ::SaveStringToHeader(m_pProperties, PROPERTY_HOST, m_pszHost);
  493. }
  494. if (m_pszPort)
  495. {
  496.     m_pProperties->SetPropertyULONG32(PROPERTY_PORT, (ULONG32)atol(m_pszPort));
  497. }
  498. else if (m_unDefaultPort > 0)
  499. {
  500.     m_pProperties->SetPropertyULONG32(PROPERTY_PORT, (ULONG32)m_unDefaultPort);
  501. }
  502. if (pszCursor && (*(m_pszResource = pszCursor) == '' && m_unProtocol != httpProtocol && m_unProtocol != rtspProtocol))
  503. {
  504.     m_LastError = HXR_INVALID_URL_PATH;
  505.     goto cleanup;
  506. }
  507.     }
  508.     //
  509.     // other options?
  510.     //
  511.     /*
  512.     // 1.0 player
  513.     if (pszTemp = (char*) ::HXFindChar(pszCursor, '$'))
  514.     {
  515. *pszTemp = '';
  516. pszCursor = pszTemp+1;
  517. if (*(m_pszStartTime = pszCursor) == '')
  518. {
  519.     m_LastError = HXR_FAILED;
  520.     goto cleanup;
  521. }
  522.     }
  523.     */
  524. cleanup:
  525.     ParseResource();
  526.     return m_LastError;
  527. }
  528. void
  529. CHXURL::TrimOffSpaces(char*& pszString)
  530. {
  531.     if( NULL == pszString ) 
  532.         return;
  533.     
  534.     char* pszValue = pszString;
  535.     char* pszCursor = pszString;
  536.     
  537.     // trim off the leading spaces 
  538.     while (*pszCursor == ' ')
  539.     {
  540.         pszCursor++;
  541.     }
  542.     pszValue = pszCursor;
  543.     // trim off the tailing spaces
  544.     if( strlen(pszCursor) != 0 )
  545.     {
  546.         pszCursor = pszCursor + strlen(pszCursor) - 1;
  547.         
  548.         while (*pszCursor == ' ' )
  549.         {
  550.             pszCursor--;
  551.         }
  552.         ++pszCursor;
  553.         if( *pszCursor != '' )
  554.             *pszCursor = '';
  555.     }
  556.     
  557.     pszString = pszValue;
  558. }
  559. HX_RESULT
  560. CHXURL::CollectOptions (char* pszOptions)
  561. {
  562.     HX_RESULT hr = HXR_OK;
  563.     char*   pszCursor = NULL;
  564.     char*   pszKey = NULL;
  565.     char*   pszValue = NULL;
  566.     char*   pszTemp = NULL;
  567.     BOOL    bValueQuoted = FALSE;
  568.   
  569.     if (HXR_OK != m_LastError)
  570.     {
  571. return m_LastError;
  572.     }
  573.     pszCursor = pszOptions;
  574.     char* pszEndOptions = pszOptions + strlen(pszOptions);
  575.     // let's start parsing
  576.     while (pszCursor < pszEndOptions)
  577.     {
  578. //
  579. // collect one value pair at a time
  580. //
  581. // <key>="<value>" or <key>=<value> 
  582. pszKey = pszCursor;
  583. if (!(pszTemp = (char*) ::HXFindChar(pszCursor, '=')))
  584. {
  585.     hr = HXR_FAILED;
  586.     goto cleanup;
  587. }
  588. *pszTemp = '';
  589. pszCursor = pszTemp + 1;
  590. // remove all the spaces between '=' and actual value
  591. while (*pszCursor == ' ')
  592. {
  593.     pszCursor++;
  594. }
  595. // "<value>"
  596. if (*pszCursor == '"')
  597. {
  598.     bValueQuoted = TRUE;
  599.     pszCursor += 1;
  600. }
  601. pszValue = pszCursor;
  602. if (bValueQuoted)
  603. {
  604.     if (!(pszTemp = (char*) ::HXFindChar(pszCursor, '"')))
  605.     {
  606. hr = HXR_INCOMPLETE;
  607. goto cleanup;
  608.     }
  609.     *pszTemp = '';
  610.     pszCursor = pszTemp + 1;
  611. }
  612. if ((pszTemp = (char*) ::HXFindChar(pszCursor, '&')) != 0)
  613. {
  614.     *pszTemp = '';
  615.     
  616.     // move cursor to the next pair
  617.     pszCursor = pszTemp + 1;
  618. }
  619. else
  620. {
  621.     // move cursor to the end of this URL
  622.     pszCursor += strlen(pszValue);
  623. }
  624. // trim off leading/tailing spaces
  625. TrimOffSpaces(pszKey);
  626. TrimOffSpaces(pszValue);
  627.         // decode each value (option) since it may be URL-encoded.
  628.         CHXString strUnescapedOptionValue;
  629.         CHXURL::decodeURL(pszValue, strUnescapedOptionValue);
  630.         pszValue = (char *)(const char *)strUnescapedOptionValue;
  631. // save to the header
  632. if (!strcasecmp("Start", pszKey)
  633.     || !strcasecmp("End", pszKey)
  634.     || !strcasecmp("Delay", pszKey)
  635.     || !strcasecmp("Duration", pszKey))
  636. {
  637.     m_pOptions->SetPropertyULONG32(pszKey, (ULONG32) ::TimeParse(pszValue) * 100);
  638. }
  639. else if (bValueQuoted || !IsNumber(pszValue))
  640. {
  641.     IHXBuffer* pBuffer = NULL;
  642.     if (m_pCCF)
  643.     {
  644. m_pCCF->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);
  645.     }
  646.     if (!pBuffer)
  647.     {
  648. hr = HXR_OUTOFMEMORY;
  649. goto cleanup;
  650.     }
  651.     pBuffer->Set((UCHAR*)pszValue, strlen(pszValue)+1);
  652.     m_pOptions->SetPropertyBuffer(pszKey, (IHXBuffer*) pBuffer);
  653.     pBuffer->Release();
  654. }
  655. else
  656. {
  657.     m_pOptions->SetPropertyULONG32(pszKey, (ULONG32) atol(pszValue));
  658. }
  659. bValueQuoted = FALSE;
  660. pszKey = NULL;
  661. pszValue = NULL;
  662.     }
  663.     
  664. cleanup:
  665.     return hr;
  666. }
  667. BOOL
  668. CHXURL::IsTimeValue (char* pszValue)
  669. {
  670.     int     i = 0;
  671.     char*   pszData = NULL;
  672.     char*   pszTemp = NULL;
  673.     // time value format: hh:mm:ss
  674.     if (isdigit(*pszValue) &&
  675. isdigit(*(pszValue+1)) &&
  676. *(pszValue+2) == ':' &&
  677. isdigit(*(pszValue+3)) &&
  678. isdigit(*(pszValue+4)) &&
  679. *(pszValue+5) == ':' &&
  680. isdigit(*(pszValue+6)) &&
  681. isdigit(*(pszValue+7)))
  682.     {
  683. for (i = 0; i < 3; i++)
  684. {
  685.     pszData = pszValue;
  686.     if (i < 2)
  687.     {
  688. pszTemp = (char*) ::HXFindChar(pszValue, ':');
  689. pszTemp = '';
  690.     }
  691.     switch (i)
  692.     {
  693.     case 0: // hh
  694. if (atoi(pszData) >= 24)
  695. {
  696.     return FALSE;
  697. }
  698. break;
  699.     case 1: // mm
  700. if (atoi(pszData) >= 60)
  701. {
  702.     return FALSE;
  703. }
  704. break;
  705.     case 2: // ss
  706. if (atoi(pszData) >= 60)
  707. {
  708.     return FALSE;
  709. }
  710. break;
  711.     default:
  712. break;
  713.     }
  714.     pszValue = pszTemp + 1;
  715. }
  716. return TRUE;
  717.     }
  718.     return FALSE;
  719. }    
  720. BOOL
  721. CHXURL::IsNumber(char* pszValue)
  722. {
  723.     char* pszCursor = pszValue;
  724.     while (*pszCursor != '')
  725.     {
  726. if (!isdigit(*pszCursor))
  727. {
  728.     return FALSE;
  729. }
  730. pszCursor++;
  731.     }
  732.     return TRUE;
  733. }
  734. // case insensitive compare
  735. int
  736. CHXURL::StringNCompare (const char* pszStr1, const char* pszStr2, size_t nChars)
  737. {
  738. #ifdef _WINDOWS
  739.     return strnicmp (pszStr1, pszStr2, nChars);
  740. #elif defined(_MACINTOSH)
  741.     return strnicmp (pszStr1, pszStr2, nChars);
  742. #elif defined(_UNIX) || defined(_OPENWAVE)
  743.     return strncasecmp (pszStr1, pszStr2, nChars);
  744. #elif defined(_SYMBIAN)
  745.     return strnicmp(pszStr1, pszStr2, nChars);
  746. #elif
  747. #   error "undefined platform....."    
  748. #else
  749.     return -1;
  750. #endif
  751. }
  752. void
  753. CHXURL::AddOption(char* pKey, char* pValue)
  754. {
  755.     // trim off leading/tailing spaces
  756.     TrimOffSpaces(pKey);
  757.     TrimOffSpaces(pValue);
  758.     
  759.     // save to the header
  760.     if (IsNumber(pValue))
  761.     {
  762. m_pOptions->SetPropertyULONG32(pKey, (ULONG32) atol(pValue));
  763.     }
  764.     else
  765.     {
  766. IHXBuffer*  pBuffer = NULL;
  767. if (m_pCCF)
  768. {
  769.     m_pCCF->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);
  770. }
  771. if (pBuffer)
  772. {
  773.     pBuffer->Set((UCHAR*)pValue, strlen(pValue)+1);
  774.     m_pOptions->SetPropertyBuffer(pKey, (IHXBuffer*) pBuffer);
  775.     pBuffer->Release();
  776. }
  777.     }
  778. }
  779. void
  780. CHXURL::AddOption(char* pKey, UINT32 ulValue)
  781. {
  782.     TrimOffSpaces(pKey);
  783.     m_pOptions->SetPropertyULONG32(pKey, ulValue);
  784. }
  785. IHXValues*
  786. CHXURL::GetProperties(void)
  787. {
  788.     if (m_pProperties)
  789.     {
  790. m_pProperties->AddRef();
  791.     }
  792.     return m_pProperties;
  793. }
  794. IHXValues*
  795. CHXURL::GetOptions(void)
  796. {
  797.     if (m_pOptions)
  798.     {
  799. m_pOptions->AddRef();
  800.     }
  801.     return m_pOptions;
  802. }
  803. char*
  804. CHXURL::GetAltURL(BOOL& bDefault)
  805. {
  806.     IHXBuffer* pValue = NULL;
  807.     char* pAltURL = NULL;
  808.     char* pURL = NULL;
  809.     char* pCursor1 = NULL;
  810.     char* pCursor2 = NULL;
  811.     bDefault = FALSE;
  812.     if (HXR_OK != m_LastError)
  813.     {
  814. goto cleanup;
  815.     }
  816.     // retrieve Alt-URL if it exists in the option list
  817.     if (HXR_OK == m_pOptions->GetPropertyBuffer("altURL", pValue) && pValue)
  818.     {     
  819. // allocate mem. for m_pszAltURL
  820. pAltURL = new char[pValue->GetSize()];
  821. SafeStrCpy(pAltURL, (const char*)pValue->GetBuffer(), pValue->GetSize());
  822.     }
  823.     else if (HXR_OK == m_pProperties->GetPropertyBuffer("url", pValue) && pValue)
  824.     {
  825. if (m_unProtocol == pnmProtocol ||
  826.     m_unProtocol == rtspProtocol)
  827. {
  828.     bDefault = TRUE;
  829.     
  830.     // The +1 is NOT for the NULL terminator since the size already has space for it.  The +1 is actually because
  831.     // if the URL contains a pnm:// the code below will potentially add a http:// which is one more character than
  832.     // a pnm URL.  A rtsp:// URL will work correctly 
  833.             INT32 lSize = pValue->GetSize() + 1;
  834.     pAltURL = new char[lSize];
  835.     memset(pAltURL, 0, lSize);
  836.     pURL = (char*)pValue->GetBuffer();
  837.     
  838.     switch (m_unProtocol)
  839.     {
  840.     case pnmProtocol:
  841. SafeSprintf(pAltURL, lSize, "http:%s", pURL + 4);
  842. break;
  843.     case rtspProtocol:
  844. SafeSprintf(pAltURL, lSize, "http:%s", pURL + 5);
  845. break;
  846.     default:
  847. break;
  848.     }
  849.     // exclude the port from the URL
  850.     if (m_pszPort)
  851.     {
  852. pCursor1 = strstr(pAltURL, m_pszPort);
  853.                 pCursor2 = pCursor1 + strlen(m_pszPort);
  854.                 pCursor1--; // back up over the ':'
  855.                 
  856.                 while(*pCursor2)
  857.                 {
  858.                     *pCursor1++ = *pCursor2++;
  859.                 }
  860.                 *pCursor1 = '';
  861.     }
  862. }
  863.     }
  864. cleanup:
  865.     HX_RELEASE(pValue);
  866.     return pAltURL;
  867. }
  868. void
  869. CHXURL::ParseResource(void)
  870. {
  871.     char* pszTemp = NULL;
  872.     char* pResource = NULL;
  873.     char* pPath = NULL;
  874.     char* pFullPath = NULL;
  875.     if (m_pszResource && (*m_pszResource != ''))
  876.     {
  877. ::SaveStringToHeader(m_pProperties, PROPERTY_RESOURCE, m_pszResource);
  878. ::StrAllocCopy(pResource, m_pszResource);
  879. pszTemp = ::HXFindChar(pResource, '?');
  880. if (pszTemp)
  881. {
  882.     *pszTemp = '';
  883. }
  884.         pFullPath = new char[strlen(pResource) + 2];
  885. SafeSprintf(pFullPath, strlen(pResource)+2, "/%s", pResource);
  886. ::SaveStringToHeader(m_pProperties, PROPERTY_FULLPATH, pFullPath);
  887. pszTemp = ::HXReverseFindChar(pResource, '/');
  888. if (pszTemp)
  889. {
  890.     *pszTemp = '';
  891.     
  892.     pPath = new char[strlen(pResource)+2];
  893.     SafeSprintf(pPath, strlen(pResource)+2, "/%s", pResource);
  894.     ::SaveStringToHeader(m_pProperties, PROPERTY_PATH, pPath);
  895. }
  896. else
  897. {
  898.     ::SaveStringToHeader(m_pProperties, PROPERTY_PATH, "/");
  899. }
  900. HX_VECTOR_DELETE(pFullPath);
  901. HX_VECTOR_DELETE(pPath);
  902. HX_VECTOR_DELETE(pResource);
  903.     }
  904.     else if (m_unProtocol == rtspProtocol)
  905.     {
  906. ::SaveStringToHeader(m_pProperties, PROPERTY_RESOURCE, "");
  907. ::SaveStringToHeader(m_pProperties, PROPERTY_FULLPATH, "");
  908. ::SaveStringToHeader(m_pProperties, PROPERTY_PATH, "");
  909.     }
  910. }
  911. HX_RESULT
  912. CHXURL::GeneratePrefixRootFragment(const char* pURL, CHXString& urlPrefix, 
  913.    CHXString& urlRoot, char*& pURLFragment)
  914. {
  915.     BOOL bHasHost = FALSE;
  916.     CHXURL urlObj(pURL);
  917.     IHXValues* pHeader = urlObj.GetProperties();
  918.     if(!pHeader)
  919.     {
  920. return HXR_FAIL;
  921.     }
  922.     IHXBuffer* pBuffer = 0;
  923.     ULONG32 ulTemp;
  924.     if(HXR_OK == pHeader->GetPropertyBuffer(PROPERTY_SCHEME, pBuffer))
  925.     {
  926. urlPrefix = (const char*)pBuffer->GetBuffer();
  927. urlPrefix += "://";
  928. pBuffer->Release();
  929.     }
  930.     if(HXR_OK == pHeader->GetPropertyBuffer(PROPERTY_HOST, pBuffer))
  931.     {
  932. urlPrefix += (const char*)pBuffer->GetBuffer();
  933. pBuffer->Release();
  934. bHasHost = TRUE;
  935.     }
  936.     if(HXR_OK == pHeader->GetPropertyULONG32(PROPERTY_PORT, ulTemp))
  937.     {
  938.   char szTemp[10]; /* Flawfinder: ignore */
  939. SafeSprintf(szTemp, sizeof(szTemp), ":%d", (UINT16)ulTemp);
  940. urlPrefix += szTemp;
  941.     }
  942.     // set root
  943.     urlRoot = urlPrefix;
  944.     if(bHasHost)
  945.     {
  946. urlPrefix += "/";
  947.     }
  948.     if(HXR_OK == pHeader->GetPropertyBuffer(PROPERTY_RESOURCE, pBuffer))
  949.     {
  950. const char* pResource = (const char*)pBuffer->GetBuffer();
  951. const char  cDelimiter1  = '/';
  952. const char  cDelimiter2  = '\';
  953. const char  cOSDelimiter = OS_SEPARATOR_CHAR;
  954. CHXString   strURLWork = pResource; 
  955. char* pFirstChar    = strURLWork.GetBuffer(strURLWork.GetLength());
  956. char* pLastChar     = NULL;
  957. char* pOptions     = NULL;
  958. char* pFragment     = NULL;
  959. pOptions = strchr(pFirstChar, '?');
  960. if (pOptions)
  961. {
  962.     pLastChar = pOptions -1;
  963. }
  964. else
  965. {
  966.     pLastChar = pFirstChar + strlen(pFirstChar)-1;
  967. }
  968. while ((pLastChar > pFirstChar) && 
  969.    (*pLastChar != cDelimiter1) && (*pLastChar != cDelimiter2) &&
  970.    (*pLastChar != cOSDelimiter))
  971. {
  972. pLastChar--;
  973. }
  974. // If we hit a delimiter before hitting the end, back up one character!
  975. if(pLastChar > pFirstChar) 
  976. {
  977.     *(++pLastChar) = '';
  978.     
  979.     urlPrefix += pFirstChar;
  980. }
  981. pBuffer->Release();
  982.     }
  983.     if(HXR_OK == pHeader->GetPropertyBuffer(PROPERTY_FRAGMENT, pBuffer))
  984.     {
  985. const char* pFragment = (const char*)pBuffer->GetBuffer();
  986. pURLFragment = new_string(pFragment);
  987. pBuffer->Release();
  988.     }
  989.     HX_RELEASE(pHeader);
  990.     return HXR_OK;
  991. }
  992. BOOL 
  993. CHXURL::CompressURL(const char* pURL, char*& pCompressURL)
  994. {
  995.     HX_ASSERT(pURL != NULL);
  996.     if (!pURL)
  997.     {
  998. return FALSE;
  999.     }
  1000.     pCompressURL     = NULL;
  1001.     BOOL bNeedToCompress    = FALSE; 
  1002.     char separator1     = '\';
  1003.     char separator2     = '/';
  1004.     char* pWalker     = (char*) pURL;
  1005.     while (*pWalker)
  1006.     {
  1007. /* 
  1008.  *   /./ || /. || ./ || . || 
  1009.  *   /../ || /.. || ../ || ..
  1010.  */
  1011. if ((*pWalker     == separator1 || *pWalker == separator2) &&
  1012.     (*(pWalker+1)   == '.') &&
  1013.     ((*(pWalker+2)   == separator1 || *(pWalker+2) == separator2) ||
  1014.      ((*(pWalker+2)   == '.') &&
  1015.      (*(pWalker+3)   == separator1 || *(pWalker+3) == separator2))))
  1016. {
  1017.     // we need to commpress it
  1018.     bNeedToCompress = TRUE;
  1019.     break;
  1020. }
  1021. /* Do not process options in the URL (stuff after ?) */
  1022. if (*pWalker == '?')
  1023. {
  1024.     break;
  1025. }
  1026. pWalker++;
  1027.     }
  1028.     if (!bNeedToCompress)
  1029.     {
  1030. return FALSE;
  1031.     }
  1032.     UINT32 ulURLLength;
  1033.     char* pTempURL;
  1034.     char* pOptions;
  1035.     ulURLLength = strlen(pURL) + 1;
  1036.     pTempURL = new char[ulURLLength];
  1037.     ::strcpy(pTempURL, pURL); /* Flawfinder: ignore */
  1038.     pOptions = strchr(pTempURL, '?');
  1039.     /* We will only compress till before the options and then paste the options
  1040.      * at the end
  1041.      */
  1042.     if (pOptions)
  1043.     {
  1044. *pOptions = '';
  1045.     }
  1046.     CHXSimpleList* pList;
  1047.     char* pToken;
  1048.     UINT16 uNumToBeDiscarded;
  1049.     CHXSimpleList* pNewList;
  1050.     
  1051.     pList    = new CHXSimpleList;
  1052.     pNewList = new CHXSimpleList;
  1053.     uNumToBeDiscarded = 0;
  1054.     pWalker = pToken = pTempURL;    
  1055.     while (*pWalker)
  1056.     {
  1057. if (*pWalker == '/' || *pWalker == '\')
  1058. {
  1059.     *pWalker = '';
  1060.     pList->AddTail(pToken);
  1061.     pToken = pWalker+1;
  1062. }
  1063. pWalker++;
  1064.     }
  1065.     pList->AddTail(pToken);
  1066.     while (pList->GetCount() > 0)
  1067.     {
  1068. pToken = (char*) pList->RemoveTail();
  1069. if (::strcmp(pToken, ".") == 0)
  1070. {
  1071.     /* ignore it */    
  1072. }
  1073. else if (::strcmp(pToken, "..") == 0)
  1074. {
  1075.     uNumToBeDiscarded++;
  1076. }
  1077. else if (uNumToBeDiscarded > 0)
  1078. {
  1079.     uNumToBeDiscarded--;
  1080. }
  1081. else
  1082. {
  1083.     pNewList->AddTail(pToken);
  1084. }
  1085.     }
  1086. // /Valid content that starts with "../../" will trigger this, so turn it off
  1087. // unless someone wants to refine it:
  1088. #if defined(ALLOW_IRRITATING_ASSERTS_FOR_VALID_CONTENT)
  1089.     // This will trigger with malformed urls with two additional ellipses(..)
  1090.     HX_ASSERT(uNumToBeDiscarded == 0 && pNewList->GetCount() > 0);
  1091. #endif
  1092.     if (uNumToBeDiscarded > 0 || pNewList->GetCount() == 0)
  1093.     {
  1094. bNeedToCompress = FALSE;
  1095. goto exit;
  1096.     }
  1097.     pCompressURL = new char[ulURLLength];
  1098.     *pCompressURL = '';
  1099.     while (pNewList->GetCount() > 0)
  1100.     {
  1101. pToken = (char*) pNewList->RemoveTail();
  1102. SafeStrCat(pCompressURL, (const char*) pToken, ulURLLength);
  1103. if (!pNewList->IsEmpty())
  1104. {
  1105.     SafeStrCat(pCompressURL, "/", ulURLLength);
  1106. }
  1107.     }
  1108.     if (pOptions)
  1109.     {
  1110. SafeStrCat(pCompressURL, "?", ulURLLength);
  1111. SafeStrCat(pCompressURL, (const char*) (pOptions+1), ulURLLength);
  1112.     }
  1113. exit:
  1114.     HX_VECTOR_DELETE(pTempURL);
  1115.     HX_DELETE(pList);
  1116.     HX_DELETE(pNewList);
  1117.     return bNeedToCompress;
  1118. }
  1119. HX_RESULT
  1120. CHXURL::encodeURL(const char* pURL, CHXString& encodedURL)
  1121. {
  1122.     HX_RESULT rc = HXR_OK;
  1123.     char    hexBuf[3] = {0}; /* Flawfinder: ignore */
  1124.     char*   pEncodeBuf = new char[(strlen(pURL)+1)*3];    // overkill
  1125.     char*   pEncodePtr = pEncodeBuf;
  1126.     const   char* pURLPtr = pURL;
  1127.     while(*pURLPtr)
  1128.     {
  1129.      // according to the URL encoding spec. from
  1130. // http://www.isi.edu/in-notes/rfc1738.txt
  1131. if (*pURLPtr <= 0x1f ||
  1132.     *pURLPtr >= 0x7f ||
  1133.     HXIsEqual(pURLPtr, ' ') ||
  1134. //     HXIsEqual(pURLPtr, '<') ||
  1135. //     HXIsEqual(pURLPtr, '>') ||
  1136. //     HXIsEqual(pURLPtr, '"') ||
  1137. //     HXIsEqual(pURLPtr, '#') ||
  1138. //     HXIsEqual(pURLPtr, '%') ||
  1139.     HXIsEqual(pURLPtr, '{') ||
  1140.     HXIsEqual(pURLPtr, '}') ||
  1141.     HXIsEqual(pURLPtr, '|') ||
  1142.     HXIsEqual(pURLPtr, '\') ||
  1143.     HXIsEqual(pURLPtr, '^') ||
  1144.     HXIsEqual(pURLPtr, '~') ||
  1145.     HXIsEqual(pURLPtr, '[') ||
  1146.     HXIsEqual(pURLPtr, ']') ||
  1147.     HXIsEqual(pURLPtr, '`') ||
  1148.     HXIsEqual(pURLPtr, ',') ||
  1149.     HXIsEqual(pURLPtr, ';'))
  1150. {     
  1151.     SafeSprintf(hexBuf, sizeof(hexBuf), "%02x", (UCHAR)*pURLPtr);
  1152.     *pEncodePtr++ = '%';
  1153.     *pEncodePtr++ = hexBuf[0];
  1154.     *pEncodePtr++ = hexBuf[1];
  1155.     
  1156.     if (HXIsLeadByte(*pURLPtr))
  1157.     {
  1158. SafeSprintf(hexBuf, sizeof(hexBuf), "%02x", (UCHAR)*(pURLPtr+1));
  1159. *pEncodePtr++ = '%';
  1160. *pEncodePtr++ = hexBuf[0];
  1161. *pEncodePtr++ = hexBuf[1];
  1162.     }     
  1163. }
  1164. else
  1165. {
  1166.     *pEncodePtr++ = *pURLPtr;
  1167. }
  1168. pURLPtr = HXGetNextChar(pURLPtr) ;
  1169.     }
  1170.     *pEncodePtr = '';
  1171.     encodedURL = pEncodeBuf;
  1172.     delete[] pEncodeBuf;
  1173.     return rc;
  1174. }
  1175. HX_RESULT
  1176. CHXURL::decodeURL(const char* pURL, CHXString& decodedURL)
  1177. {
  1178.     HX_RESULT rc = HXR_OK;
  1179.     //XXXBAB - reimplement using CHXString::GetBuffer()/SetBuffer()
  1180.     // to avoid memcpy
  1181.     char* pDecodeBuf = new char[strlen(pURL)+1];
  1182.     char* pDecodePtr = pDecodeBuf;
  1183.     const char* pURLPtr = pURL;
  1184.     while(*pURLPtr)
  1185.     {
  1186. switch(*pURLPtr)
  1187. {
  1188.     case '%':
  1189.     {
  1190. char hexBuf[3]; /* Flawfinder: ignore */
  1191. if(pURLPtr[1] &&    // check for overbound condition
  1192.    pURLPtr[2])
  1193. {
  1194.     pURLPtr++;  // walk past '%'
  1195.     hexBuf[0] = *pURLPtr++;
  1196.     hexBuf[1] = *pURLPtr;
  1197.     hexBuf[2] = '';
  1198.     *pDecodePtr++ = (char)strtol(hexBuf, NULL, 16);
  1199. }
  1200.     }
  1201.     break;
  1202.     default:
  1203.     {
  1204. *pDecodePtr++ = *pURLPtr;
  1205.     }
  1206.     break;
  1207. }
  1208. pURLPtr++;
  1209.     }
  1210.     *pDecodePtr = '';
  1211.     decodedURL = pDecodeBuf;
  1212.     delete[] pDecodeBuf;
  1213.     return rc;
  1214. }
  1215. const char* CHXURL::FindURLSchemeEnd(const char *pszURL)
  1216. {
  1217.     const char *pszTemp;
  1218.     for (pszTemp = pszURL; *pszTemp; pszTemp++)
  1219.     {
  1220. if(*pszTemp == ':')
  1221. {
  1222.     return pszTemp;
  1223. }
  1224. else if(*pszTemp == '$' ||
  1225. *pszTemp == '#' ||
  1226. *pszTemp == '?' ||
  1227. *pszTemp == '/' ||
  1228. *pszTemp == '\')
  1229. {
  1230.     return NULL;
  1231. }
  1232.     }
  1233.     
  1234.     return NULL;
  1235. }
  1236. void
  1237. CHXURL::Unescape(char* s)
  1238. {
  1239. /*
  1240.  * Remove URL hex escapes from s... done in place.  The basic concept for
  1241.  * this routine is borrowed from the WWW library HTUnEscape() routine.
  1242.  */
  1243.     char* p = NULL;
  1244.     BOOL bProcessingOptionsPastQuestionMark = FALSE;
  1245.     for (p = s; *s != ''; ++s)
  1246.     {
  1247.         if ( (!bProcessingOptionsPastQuestionMark) && (*s == '%') )
  1248. {
  1249.     if (*++s != '') 
  1250.     {
  1251. *p = Unhex( *s ) << 4;
  1252.     }
  1253.     if (*++s != '') 
  1254.     {
  1255. *p++ += Unhex( *s );
  1256.     }
  1257. else 
  1258. {
  1259.             if (*s == '?')
  1260.             {
  1261.                 bProcessingOptionsPastQuestionMark = TRUE;
  1262.             }
  1263.     *p++ = *s;
  1264. }
  1265.     }
  1266.     *p = '';
  1267. }
  1268. int
  1269. CHXURL::Unhex(char c)
  1270. {
  1271.     return (c >= '0' && c <= '9' ? c - '0'
  1272.     : c >= 'A' && c <= 'F' ? c - 'A' + 10
  1273.     : c - 'a' + 10 );
  1274. }
  1275. void 
  1276. CHXURL::TestCompressURL()
  1277. {
  1278. #ifdef _DEBUG
  1279.     /* Brad made me do it...:( */
  1280.     char* pTestCompressURL = NULL;
  1281.     BOOL bTestCompress = FALSE;
  1282.     CHXURL url("blah");
  1283.     
  1284.     HX_VERIFY(url.CompressURL("http://blah.real.com/test/../foo.smi", pTestCompressURL) == TRUE &&
  1285.       ::strcmp(pTestCompressURL, "http://blah.real.com/foo.smi") == 0);
  1286.     HX_VECTOR_DELETE(pTestCompressURL);
  1287.     HX_VERIFY(url.CompressURL("http://blah.real.com/test/foo.smi?foo/../.", pTestCompressURL) == FALSE);
  1288.     HX_VECTOR_DELETE(pTestCompressURL);
  1289.     HX_VERIFY(url.CompressURL("http://blah.real.com/test/./foo.smi", pTestCompressURL) == TRUE &&
  1290.       ::strcmp(pTestCompressURL, "http://blah.real.com/test/foo.smi") == 0);
  1291.     HX_VECTOR_DELETE(pTestCompressURL);
  1292.     HX_VERIFY(url.CompressURL("http://blah.real.com/test/.blah/foo.smi", pTestCompressURL) == FALSE);
  1293.     HX_VECTOR_DELETE(pTestCompressURL);
  1294.     HX_VERIFY(url.CompressURL("http://blah.real.com/test/..foo.smi", pTestCompressURL) == FALSE);
  1295.     HX_VECTOR_DELETE(pTestCompressURL);
  1296.     HX_VERIFY(url.CompressURL("rtsp://blah.real.com/test/blah/../../foo.smi?end=./../blah", pTestCompressURL)  == TRUE &&
  1297.       ::strcmp(pTestCompressURL, "rtsp://blah.real.com/foo.smi?end=./../blah") == 0);
  1298.     HX_VECTOR_DELETE(pTestCompressURL);
  1299. #endif
  1300. }
  1301. IHXCommonClassFactory* CHXURL::CreateCCF()
  1302. {
  1303.     return  new CHXMiniCCF();
  1304. }