SBinetURL.cpp
上传用户:xqtpzdz
上传日期:2022-05-21
资源大小:1764k
文件大小:32k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. /****************License************************************************
  2.  * Vocalocity OpenVXI
  3.  * Copyright (C) 2004-2005 by Vocalocity, Inc. All Rights Reserved.
  4.  * This program is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU General Public License
  6.  * as published by the Free Software Foundation; either version 2
  7.  * of the License, or (at your option) any later version.
  8.  *  
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  17.  * Vocalocity, the Vocalocity logo, and VocalOS are trademarks or 
  18.  * registered trademarks of Vocalocity, Inc. 
  19.  * OpenVXI is a trademark of Scansoft, Inc. and used under license 
  20.  * by Vocalocity.
  21.  ***********************************************************************/
  22. #ifndef _SB_USE_STD_NAMESPACE
  23. #define _SB_USE_STD_NAMESPACE
  24. #endif
  25. #ifndef UNICODE
  26. #define UNICODE
  27. #endif
  28. #ifndef _UNICODE
  29. #define _UNICODE
  30. #endif
  31. #ifdef _WIN32
  32. #undef HTTP_VERSION
  33. #ifndef WIN32_LEAN_AND_MEAN
  34. #define WIN32_LEAN_AND_MEAN
  35. #endif
  36. #include <windows.h>
  37. #include <wininet.h>
  38. #include <urlmon.h>
  39. #else
  40. #include <unistd.h> // for getcwd()
  41. #endif /* WIN32 */
  42. #include <stdio.h>
  43. #include <string.h>
  44. #include "VXIvalue.h"
  45. #include "VXIinet.h"
  46. #include "VXItrd.h"
  47. #include "SBinetURL.h"
  48. #include "SBinetChannel.h"
  49. #include "HttpUtils.hpp"
  50. #include <SBinetString.hpp>
  51. #define INET_MAX_PATH 1024
  52. #ifdef _WIN32
  53. #include <time.h>
  54. struct tm* localtime_r(const time_t* t, struct tm* dummy)
  55. {
  56.   return localtime(t);
  57. }
  58. #endif
  59. // #if defined(_decunix_) || defined(_solaris_)
  60. // static int my_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
  61. // {
  62. //   register unsigned int u1, u2;
  63. //   for (;;) {
  64. //     u1 = (unsigned int) *s1++;
  65. //     u2 = (unsigned int) *s2++;
  66. //     if (HttpUtils::toUpper(u1) != HttpUtils::toUpper(u2)) {
  67. //       return HttpUtils::toUpper(u1) - HttpUtils::toUpper(u2);
  68. //     }
  69. //     if (u1 == '') {
  70. //       return 0;
  71. //     }
  72. //   }
  73. // }
  74. // #else
  75. // #define my_wcscasecmp ::wcscasecmp
  76. // #endif
  77. static void
  78. appendArrayIndexToName(SBinetNString& fieldName,
  79.                        VXIunsigned index)
  80. {
  81.   char tempBuf[8];
  82.   sprintf(tempBuf, ".%d", index);
  83.   fieldName += tempBuf;
  84. }
  85. VXIinetResult SBinetURL::create(const VXIchar *pszName,
  86.                                 const VXIchar *pszUrlBase,
  87.                                 SBinetURL *& url)
  88. {
  89.   url = new SBinetURL();
  90.   if (url == NULL)
  91.   {
  92.     return VXIinet_RESULT_OUT_OF_MEMORY;
  93.   }
  94.   VXIinetResult rc = url->parse(pszName, pszUrlBase);
  95.   if (rc != VXIinet_RESULT_SUCCESS)
  96.   {
  97.     delete url;
  98.     url = NULL;
  99.   }
  100.   return rc;
  101. }
  102. SBinetURL& SBinetURL::operator=(const SBinetURL& rhs)
  103. {
  104.   if (this != &rhs)
  105.   {
  106.     _absoluteURL = rhs._absoluteURL;
  107.     _baseURL = rhs._baseURL;
  108.     _host = rhs._host;
  109.     _strPath = rhs._strPath;
  110.     _protocol = rhs._protocol;
  111.     _port = rhs._port;
  112.     N_absoluteURL = rhs.N_absoluteURL;
  113.     N_baseURL = rhs.N_baseURL;
  114.     N_host = rhs.N_host;
  115.     N_strPath = rhs.N_strPath;
  116.   }
  117.   return *this;
  118. }
  119. bool SBinetURL::operator==(const SBinetURL& rhs)
  120. {
  121.   if (this == &rhs)
  122.     return true;
  123.   if (_protocol != rhs._protocol)
  124.     return false;
  125.   if (::wcscmp(_strPath.c_str(), rhs._strPath.c_str()) != 0)
  126.     return false;
  127.   if (_protocol >= HTTP_PROTOCOL)
  128.   {
  129.     if ( _port != rhs._port)
  130.       return false;
  131.     if (SBinetHttpUtils::casecmp(_host.c_str(), rhs._host.c_str()) != 0)
  132.       return false;
  133.   }
  134.   return true;
  135. }
  136. #ifdef _WIN32
  137. VXIinetResult SBinetURL::parse(const VXIchar* pszUrl, const VXIchar* pszUrlBase)
  138. {
  139.   VXIinetResult eResult( VXIinet_RESULT_SUCCESS );
  140.   if( !pszUrl || !*pszUrl)
  141.   {
  142.     //Error(200, L"%s%s", L"Operation", L"parse URL");
  143.     return VXIinet_RESULT_INVALID_ARGUMENT;
  144.   }
  145.   if (pszUrlBase != NULL)
  146.     _baseURL = pszUrlBase;
  147.   else
  148.     _baseURL = L"";
  149.   // If the caller is using "file:", just strip it because apparently
  150.   // Internet*() doesn't do the right thing with file URI's
  151.   bool relativeFileUri = false;
  152.   if (!wcsncmp(L"file:", pszUrl, 5)) { // file:...
  153.     pszUrl += 5;
  154.     if (!wcsncmp(L"//", pszUrl, 2)) // file://...
  155.       pszUrl += 2;
  156.     relativeFileUri = true;
  157.   }
  158.   wchar_t *tmpUrl = NULL;
  159.   const wchar_t *absoluteUrl = pszUrl;
  160.   // The parsing of URL is complicated by the fact that we want unsafe
  161.   // characters to be encoded using %-notation for HTTP URLs, but we want
  162.   // %-notatation to be converted into unsaface characters for file URLs.  So,
  163.   // on entry we convert the URL into unsafe notation, then for HTTP URLs, we
  164.   // re-encode it back using %-notation.
  165.   // Combine the base and (possibly relative) URL.  Since we stripped the
  166.   // transport type above, don't combine them if the transport type of the
  167.   // base URI differs.
  168.   if( pszUrlBase && pszUrlBase[0] && !(relativeFileUri && (0 == wcsncmp(L"http:", pszUrlBase, 5))))
  169.   {
  170.     VXIulong len = (::wcslen(pszUrlBase) + ::wcslen(pszUrl)) * 3;
  171.     tmpUrl = new VXIchar [len];
  172.     if( !tmpUrl )
  173.     {
  174.       //Error(103, NULL);
  175.       return VXIinet_RESULT_OUT_OF_MEMORY;
  176.     }
  177.     if (InternetCombineUrl(pszUrlBase, pszUrl, tmpUrl, &len, ICU_BROWSER_MODE | ICU_DECODE| ICU_NO_ENCODE) == TRUE)
  178.     {
  179.       absoluteUrl = tmpUrl;
  180.     }
  181.     else
  182.     {
  183.       int errCode = GetLastError();
  184.       //Error(225, NULL);
  185.       delete [] tmpUrl;
  186.       return VXIinet_RESULT_NON_FATAL_ERROR;
  187.     }
  188.   }
  189.   else
  190.   {
  191.     VXIulong len = ::wcslen (pszUrl) * 3;
  192.     tmpUrl = new VXIchar [len];
  193.     if( !tmpUrl )
  194.     {
  195.       //Error(103, NULL);
  196.       return VXIinet_RESULT_OUT_OF_MEMORY;
  197.     }
  198.     if (InternetCanonicalizeUrl(pszUrl, tmpUrl, &len, ICU_BROWSER_MODE | ICU_DECODE | ICU_NO_ENCODE) == TRUE)
  199.     {
  200.       absoluteUrl = tmpUrl;
  201.     }
  202.     else
  203.     {
  204.       int errCode = GetLastError();
  205.       //Error(225, NULL);
  206.       delete [] tmpUrl;
  207.       return VXIinet_RESULT_NON_FATAL_ERROR;
  208.     }
  209.   }
  210.   // Now parse the absolute URL to decide if it is file or network access
  211.   int pathLen = 0;
  212.   int queryLen = 0;
  213.   wchar_t* queryPtr = ::wcschr(absoluteUrl, L'?');
  214.   if (queryPtr)
  215.   {
  216.     pathLen = queryPtr - absoluteUrl + 1;
  217.     queryLen = ::wcslen(queryPtr) + 1;
  218.   }
  219.   else
  220.   {
  221.     pathLen = ::wcslen(absoluteUrl) + 1;
  222.     queryLen = 0;
  223.   }
  224.   if (pathLen < INET_MAX_PATH)
  225.     pathLen = INET_MAX_PATH;
  226.   wchar_t protocol[INET_MAX_PATH];
  227.   wchar_t* host = new wchar_t [pathLen];
  228.   wchar_t* urlPath = new wchar_t [pathLen];
  229.   wchar_t* query = NULL;
  230.   if (queryLen > 0)
  231.     query = new wchar_t [queryLen];
  232.   URL_COMPONENTS components;
  233.   memset (&components, 0, sizeof (URL_COMPONENTS));
  234.   components.dwStructSize = sizeof (URL_COMPONENTS);
  235.   components.lpszScheme = protocol;
  236.   components.dwSchemeLength = INET_MAX_PATH;
  237.   components.lpszHostName = host;
  238.   components.dwHostNameLength = pathLen;
  239.   components.lpszUrlPath = urlPath;
  240.   components.dwUrlPathLength = pathLen;
  241.   components.lpszExtraInfo = query;
  242.   components.dwExtraInfoLength = queryLen;
  243.   if(InternetCrackUrl(absoluteUrl, ::wcslen (absoluteUrl), 0, &components) == TRUE)
  244.   {
  245.     switch (components.nScheme)
  246.     {
  247.      case INTERNET_SCHEME_FILE:
  248.        // File access, return the local file path
  249.        _protocol = FILE_PROTOCOL;
  250.        _strPath = urlPath;
  251.        _absoluteURL = absoluteUrl;
  252.        break;
  253.      case INTERNET_SCHEME_HTTPS:
  254.      case INTERNET_SCHEME_HTTP:
  255.        {
  256.          // HTTP access, return the absolute URL
  257.          _protocol = (components.nScheme == INTERNET_SCHEME_HTTP ?
  258.                     HTTP_PROTOCOL : HTTPS_PROTOCOL);
  259.          _absoluteURL = absoluteUrl;
  260.          // remove trailing / in absolute URL to ensure that www.spechworks.com
  261.          // and www.speechwork.com/ are seen as the same URL.
  262.          int idx = _absoluteURL.length() - 1;
  263.          if (_absoluteURL[idx] == L'/')
  264.          {
  265.            _absoluteURL.resize(idx);
  266.          }
  267.          _host = host;
  268.          // Retrieve the host name, the port number and the local file.
  269.          _port = components.nPort;
  270.          if (components.dwUrlPathLength == 0)
  271.          {
  272.            _strPath = L"/";
  273.          }
  274.          else
  275.          {
  276.            _strPath = urlPath;
  277.            SBinetNString utf8path, escapedPath;
  278.            SBinetHttpUtils::utf8encode(urlPath, utf8path);
  279.            SBinetHttpUtils::escapeString(utf8path.c_str(),
  280.                                          SBinetHttpUtils::URL_PATH,
  281.                                          escapedPath);
  282.            _strPath = escapedPath;
  283.          }
  284.          if (components.dwExtraInfoLength > 0)
  285.            _strPath += query;
  286.          break;
  287.        }
  288.      default:
  289.        delete [] tmpUrl;
  290.        delete [] host;
  291.        delete [] urlPath;
  292.        delete [] query;
  293.        return VXIinet_RESULT_INVALID_ARGUMENT;
  294.     }
  295.   }
  296.   else
  297.   {
  298.     // Couldn't be parsed.
  299.     // If the absoluteUrl contains a colon, it is because the parsing of the
  300.     // URL failed.  If the URL contained a colon and were valid, it would have
  301.     // been parsed sucessfully by InternetCrackURL.  If it doesn't contain a
  302.     // colon, we assume it is a path relative to the current directory.
  303.     //
  304.     if (::wcschr(absoluteUrl, L':') != NULL)
  305.     {
  306.       delete [] tmpUrl;
  307.       delete [] host;
  308.       delete [] urlPath;
  309.       delete [] query;
  310.       return VXIinet_RESULT_INVALID_ARGUMENT;
  311.     }
  312.     wchar_t *ignored;
  313.     *urlPath = L'';
  314.     _protocol = FILE_PROTOCOL;
  315.     if ((GetFullPathName(absoluteUrl, pathLen, urlPath, &ignored) > 0) &&
  316.        *urlPath)
  317.     {
  318.       _strPath = urlPath;
  319.       _absoluteURL = absoluteUrl;
  320.     }
  321.     else
  322.     {
  323.       //Error(225, L"%s%s", L"URL", pszUrl);
  324.       eResult = VXIinet_RESULT_INVALID_ARGUMENT;
  325.     }
  326.   }
  327.   N_absoluteURL = _absoluteURL;
  328.   N_baseURL = _baseURL;
  329.   N_host = _host;
  330.   N_strPath = _strPath;
  331.   delete [] tmpUrl;
  332.   delete [] host;
  333.   delete [] urlPath;
  334.   delete [] query;
  335.   return eResult;
  336. }
  337. #else /* not WIN32 */
  338. struct URLInfo
  339. {
  340.   SBinetString protocol;
  341.   SBinetString fragment;
  342.   SBinetString query;
  343.   SBinetString path;
  344.   SBinetString host;
  345.   int port;
  346. };
  347. typedef std::basic_string<VXIchar> vxistring;
  348. static VXIinetResult canonicalizeUrl(const VXIchar* const srcUrl, VXIchar* finalUrl)
  349. {
  350.   VXIinetResult rc = VXIinet_RESULT_SUCCESS; 
  351.   vxistring url = srcUrl, tmp;
  352.   
  353.   if( !finalUrl ) return VXIinet_RESULT_INVALID_ARGUMENT; 
  354.   // make sure we got a copy
  355.   wcscpy(finalUrl, srcUrl);
  356.   
  357.   // TODO: need to convert to lowercase ???
  358.   
  359.   // return if a protocol not found
  360.   if( url.find(L"://") == vxistring::npos ) 
  361.     return VXIinet_RESULT_SUCCESS;      
  362.    
  363.   // remove "../" and adjust path appropriately
  364.   vxistring::size_type idx, i, queryIndex;
  365.   idx = url.find(L"../", 0);
  366.   while( idx != vxistring::npos ) {    
  367.     // don't want to touch any part after ? (query string)
  368.     queryIndex = url.find(L"?");
  369.     if( queryIndex != vxistring::npos && queryIndex < idx ) break;
  370.     
  371.     // part contains ../
  372.     tmp = url.substr(idx);  
  373.     // part contains base <protocol>:// where <protocol> could be
  374.     // http, https, ftp and so on
  375.     url.erase(idx-1);       
  376.     tmp.erase(0, 3);  // remove ../
  377.     i = url.rfind(L"/");
  378.     if( i != vxistring::npos &&
  379.         url.substr(0,i-1).find(L"://") != vxistring::npos ) {
  380.       url.erase(i);  // adjust base, we must keep the root i.e: http://abc.com
  381.     }
  382.     if( url[url.length()-1] != L'/' ) url += L"/";
  383.     url += tmp;
  384.     idx = url.find(L"../", 0);  // continue search for ../
  385.   }
  386.   // Make a copy of final result
  387.   wcscpy(finalUrl, url.c_str());      
  388.   return rc;  
  389. }
  390. static VXIinetResult parseURL(const VXIchar* const url, URLInfo& urlInfo)
  391. {
  392.   VXIinetResult rc = VXIinet_RESULT_SUCCESS;
  393.   // Initialize the URLInfo structure.
  394.   urlInfo.port = -1;
  395.   
  396.   // Check to see if the URL is invalid.
  397.   if (!url || (url[0] == 0))
  398.     return VXIinet_RESULT_NON_FATAL_ERROR;
  399.   VXIchar* tmpUrl = new VXIchar [wcslen(url) + 1];
  400.   if( !tmpUrl ) return VXIinet_RESULT_OUT_OF_MEMORY;
  401.   wcscpy(tmpUrl, url);
  402.   const VXIchar* tmpUrlOriginal = tmpUrl;
  403.   // Check for the protocol part.
  404.   bool needToCanonicalize = false;
  405.   VXIchar* protocolEndPtr = wcschr(tmpUrl, L':');
  406.   if (protocolEndPtr && (!wcsncmp(tmpUrl, L"file", 4) || !wcsncmp(tmpUrl, L"http", 4)))
  407.   {
  408.     // Found the protocol.  Copy it to the URLInfo structure.
  409.     int protLen = protocolEndPtr - tmpUrl;
  410.     urlInfo.protocol.append(tmpUrl, protLen);
  411.     // Advance past the protocol part of the URL.
  412.     if (0 == wcsncmp(protocolEndPtr + 1, L"//", 2))
  413.     {
  414.       tmpUrl = protocolEndPtr + 3;
  415.     }
  416.     else
  417.     {
  418.       tmpUrl = protocolEndPtr + 1;
  419.       needToCanonicalize = 1;
  420.     }
  421.   }
  422.   else
  423.   {
  424.     // The default protocol is the file protocol.
  425.     urlInfo.protocol = L"file";
  426.   }
  427.   if (!wcscmp(urlInfo.protocol.c_str(), L"file"))
  428.   {
  429.     // If the URL uses the file protocol, the rest of the URL is the path.
  430.     // The path needs to be canonicalized.  It could still be an absolute
  431.     // path if it starts with a '/'.  If it does not, create the absolute
  432.     // path by prepending the current path with the current directory.
  433.     if (needToCanonicalize && tmpUrl[0] != '/')
  434.     {
  435.       char buf[1024];
  436.       urlInfo.path = getcwd(buf, 1024);
  437.       urlInfo.path += '/';
  438.     }
  439.     VXIchar* endPtr = wcschr(tmpUrl, L'?');
  440.     VXIchar* fragmentPtr = wcschr(tmpUrl, L'#');
  441.     if (endPtr)
  442.     {
  443.       if (fragmentPtr && (fragmentPtr < endPtr))
  444.         endPtr = fragmentPtr;
  445.     }
  446.     else if (fragmentPtr)
  447.     {
  448.       endPtr = fragmentPtr;
  449.     }
  450.     // Strip the query and/or fragment part of the file URI if
  451.     // there is any.
  452.     if (endPtr)
  453.       *endPtr = L'';
  454.     urlInfo.path += tmpUrl;
  455.   }
  456.   else
  457.   {
  458.     // Check for the fragment part.
  459.     VXIchar* fragmentPtr = wcsrchr(tmpUrl, L'#');
  460.     if (fragmentPtr)
  461.     {
  462.       // Found the fragment.  Copy it to the URLInfo structure.
  463.       *fragmentPtr = L'';
  464.       urlInfo.fragment = ++fragmentPtr;
  465.     }
  466.     // Check for the query part.
  467.     VXIchar* queryPtr = wcsrchr(tmpUrl, L'?');
  468.     if (queryPtr)
  469.     {
  470.       // Found the query.  Copy it to the URLInfo structure.
  471.       *queryPtr = L'';
  472.       urlInfo.query = ++queryPtr;
  473.     }
  474.     // Check for the path part.
  475.     VXIchar* pathPtr = wcschr(tmpUrl, L'/');
  476.     if (pathPtr)
  477.     {
  478.       // Found the path.  Copy it to the URLInfo structure.
  479.       // First, we need to UTF-8 encode it and then escape it.
  480.       SBinetNString utfPath, escapedPath;
  481.       SBinetHttpUtils::utf8encode(pathPtr, utfPath);
  482.       SBinetHttpUtils::escapeString(utfPath.c_str(),
  483.                                     SBinetHttpUtils::URL_PATH,
  484.                                     escapedPath);
  485.       urlInfo.path = escapedPath;
  486.       // Append the query to the path.
  487.       if (queryPtr)
  488.       {
  489.         // Add the query to the path.
  490.         urlInfo.path += L'?';
  491.         urlInfo.path += urlInfo.query;
  492.       }
  493.       *pathPtr = L'';
  494.     }
  495.     else
  496.     {
  497.       // No path was specified so set the path to root ("/").
  498.       urlInfo.path = "/";
  499.     }
  500.     // Check for the username:password part.
  501.     VXIchar* userpassEndPtr = wcschr(tmpUrl, L'@');
  502.     if (userpassEndPtr)
  503.     {
  504.       // For now, just ignore username and password.
  505.       tmpUrl = userpassEndPtr + 1;
  506.     }
  507.     // Check for the port part.
  508.     VXIchar* portPtr = wcsrchr(tmpUrl, L':');
  509.     if (portPtr)
  510.     {
  511.       *portPtr = L'';
  512.       ++portPtr;
  513.       // Check if the port is valid (all digits).
  514.       VXIchar* end = NULL;
  515.       long port = wcstol(portPtr, &end, 10);
  516.       if ((end) && (*end == L''))
  517.         urlInfo.port = (int) port;
  518.     }
  519.     // The rest is the host part.
  520.     urlInfo.host = tmpUrl;
  521.     if (urlInfo.host[0] == 0)
  522.     {
  523.       delete [] tmpUrlOriginal;
  524.       return VXIinet_RESULT_NON_FATAL_ERROR;
  525.     }
  526.   }
  527.   delete [] tmpUrlOriginal;
  528.   return VXIinet_RESULT_SUCCESS;
  529. }
  530. static VXIinetResult combineURL(const VXIchar* const baseUrl,
  531.                                 const VXIchar* const relativeUrl,
  532.                                 SBinetString& absoluteUrl)
  533. {
  534.   // Check to see if the relative URL is referencing another host
  535.   //    (i.e. the protocol is specified).
  536.   const VXIchar* protocolEndPtr = wcschr(relativeUrl, L':');
  537.   if (!baseUrl || (protocolEndPtr && (!wcsncmp(relativeUrl, L"file", 4) || !wcsncmp(relativeUrl, L"http", 4))))
  538.   {
  539.     // HACK to allow OSR to load absolute local URLs
  540.     if (wcsstr(relativeUrl, L"/file://"))
  541.       absoluteUrl = relativeUrl + 1;
  542.     else
  543.       absoluteUrl = relativeUrl;
  544.   }
  545.   else
  546.   {
  547.     absoluteUrl = baseUrl;
  548.     const VXIchar* absoluteUrlStr = absoluteUrl.c_str();
  549.     const VXIchar* pathPtr = wcsstr(absoluteUrlStr, L"://");
  550.     if (pathPtr)
  551.       pathPtr += 3;
  552.     else
  553.       pathPtr = absoluteUrlStr;
  554.     if (relativeUrl[0] == L'/' && relativeUrl[1] != L'/')
  555.     {
  556.       // The relative URL is an absolute path.
  557.       // Find the pointer to the beginning of the path in the absoluteUrl.
  558.       pathPtr = wcschr(pathPtr, L'/');
  559.       if (pathPtr)
  560.         absoluteUrl.resize(pathPtr - absoluteUrlStr);
  561.     }
  562.     else
  563.     {
  564.       // The relative URL is a relative path.
  565.       // Find the pointer to the end of the path.
  566.       pathPtr = wcsrchr(pathPtr, L'/');
  567.       if (pathPtr)
  568.       {
  569.         // Clear anything after the last slash in the path part of the
  570.         //    absolute URL.
  571.         ++pathPtr;
  572.         absoluteUrl.resize(pathPtr - absoluteUrlStr);
  573.       }
  574.       else
  575.       {
  576.         // Add a slash at the end of the absolute URL if it didn't have one.
  577.         absoluteUrl += L"/";
  578.       }
  579.     }
  580.     absoluteUrl += relativeUrl;
  581.   }
  582.   
  583.   // canonicalize abs. url
  584.   VXIchar* tmpUrl = new VXIchar[absoluteUrl.length()+1];  
  585.   if( !tmpUrl ) return VXIinet_RESULT_OUT_OF_MEMORY;
  586.   VXIinetResult rc = canonicalizeUrl(absoluteUrl.c_str(), tmpUrl);
  587.   if( rc != VXIinet_RESULT_SUCCESS ) {
  588.     delete [] tmpUrl;
  589.     return rc;
  590.   }
  591.   absoluteUrl = tmpUrl;
  592.   delete [] tmpUrl;
  593.   return VXIinet_RESULT_SUCCESS;
  594. }
  595. VXIinetResult
  596. SBinetURL::parse(const VXIchar* pszUrl,
  597.                  const VXIchar* pszUrlBase)
  598. {
  599.   // TBD: Must apply SPR 7530 fix here too, for now only in WinInetResolveUrl,
  600.   // support for relative URL starting with / as specified by the RFC that
  601.   // defines file:// access
  602.   VXIinetResult eResult( VXIinet_RESULT_SUCCESS );
  603.   if( !pszUrl || !pszUrl[0])
  604.   {
  605.     //Error(200, L"%s%s", L"Operation", L"parse URL");
  606.     return VXIinet_RESULT_INVALID_ARGUMENT;
  607.   }
  608.   if (pszUrlBase != NULL)
  609.     _baseURL = pszUrlBase;
  610.   else
  611.     _baseURL = L"";
  612.   // Combine the base and relative URLs to get an absolute URL.
  613.   eResult = combineURL(pszUrlBase, pszUrl, _absoluteURL);
  614.   if (eResult == VXIinet_RESULT_SUCCESS)
  615.   {
  616.     // Parse the absolute URL into its components.
  617.     URLInfo urlInfo;
  618.     eResult = parseURL(_absoluteURL.c_str(), urlInfo);
  619.     if (eResult == VXIinet_RESULT_SUCCESS)
  620.     {
  621.       if (!wcscmp(urlInfo.protocol.c_str(), L"file"))
  622.       {
  623.         _protocol = FILE_PROTOCOL;
  624.       }
  625.       else if (!wcscmp(urlInfo.protocol.c_str(), L"http"))
  626.       {
  627.         _protocol = HTTP_PROTOCOL;
  628.         _host = urlInfo.host;
  629.         _port = urlInfo.port == -1 ? 80 : urlInfo.port;
  630.       }
  631.       else if (!wcscmp(urlInfo.protocol.c_str(), L"https"))
  632.       {
  633.         _protocol = HTTPS_PROTOCOL;
  634.         _host = urlInfo.host;
  635.         _port = urlInfo.port == -1 ? 443 : urlInfo.port;
  636.       }
  637.       else
  638.       {
  639.         eResult = VXIinet_RESULT_NON_FATAL_ERROR;
  640.       }
  641.     }
  642.     if (eResult == VXIinet_RESULT_SUCCESS)
  643.     {
  644.       _strPath = urlInfo.path;
  645.       // Remove trailing / in absolute URL to ensure that www.vocalocity.com
  646.       // and www.vocalocity.com/ are seen as the same URL.
  647.       int idx = _absoluteURL.length() - 1;
  648.       if (_absoluteURL[idx] == L'/')
  649.         _absoluteURL.resize(idx);
  650.     }
  651.   }
  652.   N_absoluteURL = _absoluteURL;
  653.   N_baseURL = _baseURL;
  654.   N_host = _host;
  655.   N_strPath = _strPath;
  656.   return eResult;
  657. }
  658. #endif /* WIN32 */
  659. SBinetNString SBinetURL::valueToNString(const VXIValue* value)
  660. {
  661.   // Convert numeric types using a narrow character buffer in order to
  662.   // avoid the need for swprintf( ) which doesn't exist in the GNU
  663.   // GCC C library for GCC 2.x and earlier.
  664.   char tempBuf[32];
  665.   *tempBuf = '';
  666.   switch (VXIValueGetType(value))
  667.   {
  668.    case VALUE_BOOLEAN:
  669.      {
  670.        VXIbool valBool = VXIBooleanValue( (const VXIBoolean *)value );
  671.        sprintf (tempBuf, "%s", valBool ? "true" : "false");
  672.      }
  673.      break;
  674.    case VALUE_INTEGER:
  675.      {
  676.        VXIint32 valInt = VXIIntegerValue( (const VXIInteger *)value );
  677.        sprintf (tempBuf, "%d", valInt);
  678.      }
  679.      break;
  680.    case VALUE_FLOAT:
  681.      {
  682.        VXIflt32 valFloat = VXIFloatValue( (const VXIFloat *)value );
  683.        sprintf (tempBuf, "%f", valFloat);
  684.      }
  685.      break;
  686. #if (VXI_CURRENT_VERSION >= 0x00030000)
  687.    case VALUE_ULONG:
  688.      {
  689.        VXIulong valInt = VXIULongValue( (const VXIULong *)value );
  690.        sprintf (tempBuf, "%u", valInt);
  691.      }
  692.      break;
  693.    case VALUE_DOUBLE:
  694.      {
  695.        VXIflt64 valFloat = VXIDoubleValue( (const VXIDouble *)value );
  696.        sprintf (tempBuf, "%f", valFloat);
  697.      }
  698.      break;
  699. #endif
  700.    case VALUE_STRING:
  701.        return VXIStringCStr( (const VXIString *)value );
  702.    case VALUE_PTR:
  703.      {
  704.        void *valPtr = VXIPtrValue( (const VXIPtr *)value );
  705.        sprintf (tempBuf, "%p", valPtr);
  706.      }
  707.      break;
  708.    case VALUE_MAP:
  709.    case VALUE_VECTOR:
  710.    case VALUE_CONTENT:
  711.    default:
  712.      // These types are supposed to be handled before entering this function.
  713.      break;
  714.   }
  715.   return tempBuf;
  716. }
  717. bool SBinetURL::requiresMultipart(const VXIValue *value)
  718. {
  719.   switch (VXIValueGetType(value))
  720.   {
  721.    case VALUE_CONTENT:
  722.      return true;
  723.    case VALUE_MAP:
  724.      return requiresMultipart((const VXIMap*) value);
  725.    case VALUE_VECTOR:
  726.      return requiresMultipart((const VXIVector*) value);
  727.    default:
  728.      return false;
  729.   }
  730. }
  731. bool SBinetURL::requiresMultipart(const VXIMap* vximap)
  732. {
  733.   if (vximap == NULL) return false;
  734.   const VXIchar  *key = NULL;
  735.   const VXIValue *value = NULL;
  736.   bool result = false;
  737.   VXIMapIterator *mapIterator = VXIMapGetFirstProperty( vximap, &key, &value );
  738.   do
  739.   {
  740.     if (key != NULL && value != NULL && requiresMultipart(value))
  741.     {
  742.       result = true;
  743.       break;
  744.     }
  745.   }
  746.   while (VXIMapGetNextProperty(mapIterator, &key, &value) == VXIvalue_RESULT_SUCCESS);
  747.   VXIMapIteratorDestroy(&mapIterator);
  748.   return result;
  749. }
  750. bool SBinetURL::requiresMultipart(const VXIVector* vxivector)
  751. {
  752.   for (VXIunsigned i = VXIVectorLength(vxivector); i > 0; i--)
  753.   {
  754.     const VXIValue* value = VXIVectorGetElement(vxivector, i);
  755.     if (value != NULL && requiresMultipart(value))
  756.     {
  757.       return true;
  758.     }
  759.   }
  760.   return false;
  761. }
  762. void SBinetURL::appendQueryArgsToURL(const VXIMap* queryArgs)
  763. {
  764.   SBinetNString queryArgsNStr = queryArgsToNString(queryArgs);
  765.   if (queryArgsNStr.length() > 0)
  766.   {
  767.     const wchar_t delim = wcschr(_strPath.c_str(), L'?') == NULL ? L'?' : L'&';
  768.     _strPath += delim;
  769.     _strPath += queryArgsNStr;
  770.     _absoluteURL += delim;
  771.     _absoluteURL += queryArgsNStr;
  772.     // maintain narrow and wide representations synchronized.
  773.     N_strPath += delim;
  774.     N_strPath += queryArgsNStr;
  775.     N_absoluteURL += delim;
  776.     N_absoluteURL += queryArgsNStr;
  777.   }
  778. }
  779. SBinetNString
  780. SBinetURL::queryArgsToNString(const VXIMap* queryArgs) const
  781. {
  782.   SBinetNString result;
  783.   if (queryArgs)
  784.   {
  785.     VXIString *strResult = VXIValueToString((const VXIValue *) queryArgs, L"",
  786.     VALUE_FORMAT_URL_QUERY_ARGS);
  787.     if (strResult)
  788.     {
  789.       // URL query args encoding ensures we only have ASCII here
  790.       result += VXIStringCStr(strResult);
  791.       VXIStringDestroy(&strResult);
  792.     }
  793.   }
  794.   return result;
  795. }
  796. /*
  797.  * Utilities
  798.  */
  799. void SBinetURL::appendKeyToMultipart(SBinetNString& result, const char *key, bool appendFilename )
  800. {
  801.   // Print the boundary start
  802.   if (result.length() != 0)
  803.     result += CRLF;
  804.   result += "--" SB_BOUNDARY CRLF;
  805.   result += "Content-Disposition: form-data; name="";
  806.   result += key;
  807.   result += """;
  808.   
  809.   if (appendFilename){
  810.      char filename[128];
  811.      struct tm* ct;
  812.      struct tm ct_r = {0};
  813.      time_t t = time(NULL);
  814.      ct = localtime_r(&t, &ct_r);
  815.      sprintf(filename, "%04d%02d%02d%02d%02d%02d.ulaw", ct->tm_year+1900,
  816.        ct->tm_mon+1, ct->tm_mday, ct->tm_hour, ct->tm_min,
  817.        ct->tm_sec);
  818.      result += "; filename="";
  819.      result += filename;
  820.      result += """;
  821.   }
  822.   result += CRLF;
  823. }
  824. void SBinetURL::appendValueToMultipart(SBinetNString& result, const SBinetNString& value)
  825. {
  826. //   char tempbuf[20];
  827. //   if (value.length() > 0)
  828. //   {
  829. //     result += "Content-Type: text/plain" CRLF;
  830. //     result += "Content-Length: ";
  831. //     sprintf(tempbuf, "%d", value.length());
  832. //     result += tempbuf;
  833. //     result += CRLF;
  834. //     // blank line.
  835. //     result += CRLF;
  836. //     result += value;
  837. //   }
  838.   result += CRLF;
  839.   result += value;
  840. }
  841. void SBinetURL::appendQueryArgsVectorToMultipart(SBinetNString& result,
  842.                                                  const VXIVector *vxivector,
  843.                                                  SBinetNString& fieldName)
  844. {
  845.   const VXIValue *value = NULL;
  846.   VXIunsigned vectorLen = VXIVectorLength(vxivector);
  847.   int prevLen = fieldName.length();
  848.   for(VXIunsigned i = 0 ; i < vectorLen ; i++)
  849.   {
  850.     value = VXIVectorGetElement(vxivector, i);
  851.     if (value != NULL)
  852.     {
  853.       appendArrayIndexToName(fieldName, i);
  854.       appendQueryArgsToMultipart(result, value, fieldName);
  855.       fieldName.resize(prevLen);
  856.     }
  857.   }
  858. }
  859. void SBinetURL::appendQueryArgsMapToMultipart(SBinetNString& result,
  860.                                               const VXIMap *vximap,
  861.                                               SBinetNString& fieldName)
  862. {
  863.   const VXIchar  *key = NULL;
  864.   const VXIValue *value = NULL;
  865.   int prevLen = fieldName.length();
  866.   VXIMapIterator *mapIterator = VXIMapGetFirstProperty(vximap, &key, &value );
  867.   do
  868.   {
  869.     if (key != NULL && value != NULL)
  870.     {
  871.       fieldName += '.';
  872.       fieldName += key;
  873.       appendQueryArgsToMultipart(result, value, fieldName);
  874.       fieldName.resize(prevLen);
  875.     }
  876.   }
  877.   while (VXIMapGetNextProperty(mapIterator, &key, &value) ==
  878.          VXIvalue_RESULT_SUCCESS);
  879.   VXIMapIteratorDestroy(&mapIterator);
  880. }
  881. SBinetNString SBinetURL::queryArgsToMultipart(const VXIMap* queryArgs)
  882. {
  883.   SBinetNString result;
  884.   if(!queryArgs) return(result);
  885.   const VXIchar *key = NULL;
  886.   const VXIValue *value = NULL;
  887.   VXIMapIterator* mapIterator = VXIMapGetFirstProperty( queryArgs, &key, &value );
  888.   do
  889.   {
  890.     if (key != NULL && value != NULL)
  891.     {
  892.       SBinetNString fieldName = key;
  893.       appendQueryArgsToMultipart(result, value, fieldName);
  894.     }
  895.   }
  896.   while ( VXIMapGetNextProperty( mapIterator, &key, &value ) == VXIvalue_RESULT_SUCCESS );
  897.   // Print the boundary terminator
  898.   result += CRLF "--" SB_BOUNDARY "--" CRLF;
  899.   VXIMapIteratorDestroy(&mapIterator);
  900.   return result;
  901. }
  902. void SBinetURL::appendQueryArgsToMultipart(SBinetNString& result, const VXIValue *value, SBinetNString& fieldName)
  903. {
  904.   switch (VXIValueGetType(value))
  905.   {
  906.    case VALUE_CONTENT:
  907.      {
  908.        // audio
  909.        const VXIchar* type;
  910.        const VXIchar* transferEnc;
  911.        const VXIbyte* data;
  912.        VXIulong size;
  913.        char sizeInt[10];
  914.        appendKeyToMultipart(result, fieldName.c_str());
  915.        VXIContentValue((const VXIContent *) value, &type, &data, &size);
  916.        result += "Content-Type: ";
  917.        result += type;
  918.        result += CRLF;
  919.        sprintf(sizeInt,"%lu",size);
  920.        result += "Content-Length: ";
  921.        result += sizeInt;
  922.        result += CRLF;
  923.        transferEnc = VXIContentGetTransferEncoding((const VXIContent *) value);
  924.        if( transferEnc != NULL )
  925.        {
  926.            result += "Content-Transfer-Encoding: ";
  927.            result += transferEnc;
  928.            result += CRLF;
  929.        }
  930.        // One blank line before actual data.
  931.        result += CRLF;
  932.        result.append((const char*) data, size);
  933.      }
  934.      break;
  935.    case VALUE_MAP:
  936.      appendQueryArgsMapToMultipart(result, (const VXIMap *) value, fieldName);
  937.      break;
  938.    case VALUE_VECTOR:
  939.      appendQueryArgsVectorToMultipart(result, (const VXIVector *)value, fieldName);
  940.      break;
  941.    default:
  942.      appendKeyToMultipart(result, fieldName.c_str(), false);
  943.      appendValueToMultipart(result, valueToNString(value));
  944.      break;
  945.   }
  946. }
  947. //
  948. // Infer a MIME content type from a URL (by the extension)
  949. //
  950. VXIString *SBinetURL::getContentTypeFromUrl() const
  951. {
  952.   //
  953.   // Determine the extension. This has to work for local files and also HTTP
  954.   //
  955.   const VXIchar *url = _strPath.c_str();
  956.   if (url == NULL || !*url)
  957.   {
  958.     return NULL;
  959.   }
  960.   const VXIchar *urlEnd = url;
  961.   // Search for the end of the URL.
  962.   while (*urlEnd != L'?' && *urlEnd != L'#' && *urlEnd) urlEnd++;
  963.   // Now look for the last '.'
  964.   VXIchar ext[1024];
  965.   const VXIchar *urlDot = urlEnd - 1;
  966.   while (urlDot >= url && *urlDot != L'.') urlDot--;
  967.   if (urlDot >= url)
  968.   {
  969.     ::wcsncpy(ext, urlDot, urlEnd - urlDot);
  970.     ext[urlEnd - urlDot] = L'';
  971.     if (*ext)
  972.     {
  973.       const VXIchar* typeFromMap = SBinetChannel::mapExtension(ext);
  974.       if (typeFromMap != NULL)
  975.       {
  976. if (*typeFromMap)
  977.   return VXIStringCreate(typeFromMap);
  978. else
  979.   return NULL;
  980.       }
  981.       // GN: Skip this step.  The default type will be returned instead.
  982.       //     This is what happens on other OSes.  The problem is that if
  983.       //     the file extension is .xml, FindMimeFromData() will return
  984.       //     a MIME type of text/xml and this is not one of the supported
  985.       //     types in SRGS.  Allow the user of INET to determine the type
  986.       //     instead by returning the default type.
  987. #if 0
  988.       /*
  989.        * Could not find in map, use Win32 if available
  990.        */
  991. #ifdef _WIN32
  992.       // Try Windows, this supports a number of hardcoded MIME content
  993.       // types as well as extension mapping rules. To define a new
  994.       // extension mapping rule, create a new Win32 registry key called
  995.       // HKEY_CLASSES_ROOT.<ext> where <ext> is the extension name. Then
  996.       // create a value under that called "Content Type" where the data is
  997.       // the content type string, such as "audio/aiff". Browse that
  998.       // location in the registry for numerous examples (not all have a
  999.       // content type mapping though).
  1000.       //
  1001.       // TBD sniff the actual data buffer too as this permits, pass the
  1002.       // proposed MIME type too (as returned by the web server, if any).
  1003.       VXIchar *mimeBuf;
  1004.       if (FindMimeFromData (NULL, url, NULL, 0, NULL, 0, &mimeBuf, 0) !=
  1005.   NOERROR)
  1006. mimeBuf = NULL;
  1007.       /* FindMimeFromData( ) leaks, we add unknown extensions here to
  1008.  make it so we only leak once per extension instead of
  1009.  repeatedly */
  1010.       if (mimeBuf && *mimeBuf) {
  1011. SBinetChannel::addExtensionRule(ext, mimeBuf);
  1012. return VXIStringCreate(mimeBuf);
  1013.       } else {
  1014. SBinetChannel::addExtensionRule(ext, L"");
  1015.       }
  1016. #endif
  1017. #endif
  1018.     }
  1019.   }
  1020.   /*
  1021.    * Couldn't figure out MIME type: return default type.
  1022.    */
  1023.   return NULL;
  1024. }