/*
 * url.cpp - IUniformResourceLocator implementation for InternetShortcut class.
 */


/* Headers
 **********/

#include "project.hpp"
#pragma hdrstop

#include "assoc.h"
#include "resource.h"


/* Module Constants
 *******************/

#pragma data_seg(DATA_SEG_READ_ONLY)

PRIVATE_DATA const char s_cszURLSeparator[]        = ":";

PRIVATE_DATA const char s_cchURLSuffixSlash        = '/';

PRIVATE_DATA const char s_cszURLPrefixesKey[]      = "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes";
PRIVATE_DATA const char s_cszDefaultURLPrefixKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix";

#pragma data_seg()


/***************************** Private Functions *****************************/


PRIVATE_CODE PCSTR SkipLeadingSlashes(PCSTR pcszURL)
{
   PCSTR pcszURLStart;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));

   // Skip two leading slashes.

   if (pcszURL[0] == s_cchURLSuffixSlash &&
       pcszURL[1] == s_cchURLSuffixSlash)
   {
      pcszURLStart = CharNext(pcszURL);
      pcszURLStart = CharNext(pcszURLStart);
   }
   else
      pcszURLStart = pcszURL;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR) &&
          EVAL(IsStringContained(pcszURL, pcszURLStart)));

   return(pcszURLStart);
}


PRIVATE_CODE HRESULT ApplyURLPrefix(PCSTR pcszURL, PSTR *ppszTranslatedURL)
{
   HRESULT hr = S_FALSE;
   HKEY hkeyPrefixes;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR));

   *ppszTranslatedURL = NULL;

   if (RegOpenKey(g_hkeyURLSettings, s_cszURLPrefixesKey, &hkeyPrefixes)
       == ERROR_SUCCESS)
   {
      PCSTR pcszURLStart;
      DWORD dwiValue;
      char rgchValueName[MAX_PATH_LEN];
      DWORD dwcbValueNameLen;
      DWORD dwType;
      char rgchPrefix[MAX_PATH_LEN];
      DWORD dwcbPrefixLen;

      pcszURLStart = SkipLeadingSlashes(pcszURL);

      dwcbValueNameLen = sizeof(rgchValueName);
      dwcbPrefixLen = sizeof(rgchPrefix);

      for (dwiValue = 0;
           RegEnumValue(hkeyPrefixes, dwiValue, rgchValueName,
                        &dwcbValueNameLen, NULL, &dwType, (PBYTE)rgchPrefix,
                        &dwcbPrefixLen) == ERROR_SUCCESS;
           dwiValue++)
      {
         if (! _strnicmp(pcszURLStart, rgchValueName, dwcbValueNameLen))
         {
            // dwcbPrefixLen includes null terminator.

            *ppszTranslatedURL = new(char[dwcbPrefixLen + lstrlen(pcszURLStart)]);

            if (*ppszTranslatedURL)
            {
               lstrcpy(*ppszTranslatedURL, rgchPrefix);
               lstrcat(*ppszTranslatedURL, pcszURLStart);
               // (+ 1) for null terminator.
               ASSERT(lstrlen(*ppszTranslatedURL) + 1 == (int)dwcbPrefixLen + lstrlen(pcszURLStart));

               hr = S_OK;
            }
            else
               hr = E_OUTOFMEMORY;

            break;
         }

         dwcbValueNameLen = sizeof(rgchValueName);
         dwcbPrefixLen = sizeof(rgchPrefix);
      }

      RegCloseKey(hkeyPrefixes);

      switch (hr)
      {
         case S_OK:
            TRACE_OUT(("ApplyURLPrefix(): Prefix %s prepended to prefix %s of %s to yield URL %s.",
                       rgchValueName,
                       rgchPrefix,
                       pcszURL,
                       *ppszTranslatedURL));
            break;

         case S_FALSE:
            TRACE_OUT(("ApplyURLPrefix(): No matching prefix found for URL %s.",
                       pcszURL));
            break;

         default:
            ASSERT(hr == E_OUTOFMEMORY);
            break;
      }
   }
   else
      TRACE_OUT(("ApplyURLPrefix(): No URL prefixes registered."));

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) ||
          ((hr == S_FALSE ||
            hr == E_OUTOFMEMORY) &&
           ! *ppszTranslatedURL));

   return(hr);
}


PRIVATE_CODE HRESULT ApplyDefaultURLPrefix(PCSTR pcszURL,
                                           PSTR *ppszTranslatedURL)
{
   HRESULT hr;
   char rgchDefaultURLPrefix[MAX_PATH_LEN];
   DWORD dwcbDefaultURLPrefixLen;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR));

   *ppszTranslatedURL = NULL;

   dwcbDefaultURLPrefixLen = sizeof(rgchDefaultURLPrefix);

   if (GetDefaultRegKeyValue(g_hkeyURLSettings, s_cszDefaultURLPrefixKey,
                             rgchDefaultURLPrefix, &dwcbDefaultURLPrefixLen)
       == ERROR_SUCCESS)
   {
      // (+ 1) for null terminator.

      *ppszTranslatedURL = new(char[dwcbDefaultURLPrefixLen +
                                    lstrlen(pcszURL) + 1]);

      if (*ppszTranslatedURL)
      {
         PCSTR pcszURLStart;

         pcszURLStart = SkipLeadingSlashes(pcszURL);

         lstrcpy(*ppszTranslatedURL, rgchDefaultURLPrefix);
         lstrcat(*ppszTranslatedURL, pcszURLStart);
         // (+ 1) for null terminator.
         ASSERT(lstrlen(*ppszTranslatedURL) + 1 == (int)dwcbDefaultURLPrefixLen + lstrlen(pcszURLStart));

         hr = S_OK;
      }
      else
         hr = E_OUTOFMEMORY;
   }
   else
      hr = S_FALSE;

   switch (hr)
   {
      case S_OK:
         TRACE_OUT(("ApplyDefaultURLPrefix(): Default prefix %s prepended to %s to yield URL %s.",
                    rgchDefaultURLPrefix,
                    pcszURL,
                    *ppszTranslatedURL));
         break;

      case S_FALSE:
         TRACE_OUT(("ApplyDefaultURLPrefix(): No default URL prefix registered."));
         break;

      default:
         ASSERT(hr == E_OUTOFMEMORY);
         break;
   }

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) ||
          ((hr == S_FALSE ||
            hr == E_OUTOFMEMORY) &&
           ! *ppszTranslatedURL));

   return(hr);
}


PRIVATE_CODE HRESULT MyTranslateURL(PCSTR pcszURL, DWORD dwFlags,
                                    PSTR *ppszTranslatedURL)
{
   HRESULT hr;
   PARSEDURL pu;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_TRANSLATEURL_FLAGS));
   ASSERT(IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR));

   // Check URL syntax.

   if (ParseURL(pcszURL, &pu) == S_OK)
   {
      *ppszTranslatedURL = NULL;

      hr = S_FALSE;
   }
   else
   {
      if (IS_FLAG_SET(dwFlags, TRANSLATEURL_FL_GUESS_PROTOCOL))
         hr = ApplyURLPrefix(pcszURL, ppszTranslatedURL);
      else
         hr = S_FALSE;

      if (hr == S_FALSE &&
          IS_FLAG_SET(dwFlags, TRANSLATEURL_FL_USE_DEFAULT_PROTOCOL))
         hr = ApplyDefaultURLPrefix(pcszURL, ppszTranslatedURL);
   }

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) ||
          ((hr == S_FALSE ||
            hr == E_OUTOFMEMORY) &&
           ! *ppszTranslatedURL));

   return(hr);
}


#ifdef DEBUG

PRIVATE_CODE IsValidPCPARSEDURL(PCSTR pcszURL, PCPARSEDURL pcpu)
{
   return(IS_VALID_READ_PTR(pcpu, CPARSEDURL) &&
          (IS_VALID_STRING_PTR(pcpu->pcszProtocol, CSTR) &&
           EVAL(IsStringContained(pcszURL, pcpu->pcszProtocol)) &&
           EVAL(pcpu->ucbProtocolLen < (UINT)lstrlen(pcpu->pcszProtocol))) &&
          (IS_VALID_STRING_PTR(pcpu->pcszSuffix, CSTR) &&
           EVAL(IsStringContained(pcszURL, pcpu->pcszSuffix)) &&
           EVAL(pcpu->ucbSuffixLen <= (UINT)lstrlen(pcpu->pcszSuffix))) &&
          EVAL(pcpu->ucbProtocolLen + pcpu->ucbSuffixLen < (UINT)lstrlen(pcszURL)));
}


PRIVATE_CODE IsValidPCURLINVOKECOMMANDINFO(PCURLINVOKECOMMANDINFO pcurlici)
{
   return(IS_VALID_READ_PTR(pcurlici, CURLINVOKECOMMANDINFO) &&
          EVAL(pcurlici->dwcbSize >= sizeof(*pcurlici)) &&
          FLAGS_ARE_VALID(pcurlici->dwFlags, ALL_IURL_INVOKECOMMAND_FLAGS) &&
          (IS_FLAG_CLEAR(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI) ||
           IS_VALID_HANDLE(pcurlici->hwndParent, WND)) &&
          (IS_FLAG_SET(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB) ||
           IS_VALID_STRING_PTR(pcurlici->pcszVerb, CSTR)));
}

#endif


/****************************** Public Functions *****************************/


PUBLIC_CODE HRESULT ParseURL(PCSTR pcszURL, PPARSEDURL ppu)
{
   HRESULT hr = URL_E_INVALID_SYNTAX;
   PCSTR pcszProtocol;
   PCSTR pcszSep;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(IS_VALID_WRITE_PTR(ppu, PARSEDURL));

   ZeroMemory(ppu, sizeof(*ppu));

   pcszProtocol = pcszURL;

   // Skip over any "url:" prefix.

   if (! _strnicmp(pcszProtocol, g_cszURLPrefix, g_ucbURLPrefixLen))
      pcszProtocol += g_ucbURLPrefixLen;

   // Find protocol:suffix separator.

   pcszSep = strstr(pcszProtocol, s_cszURLSeparator);

   if (pcszSep)
   {
      if (pcszSep > pcszProtocol)
      {
         ppu->pcszProtocol = pcszProtocol;
         ppu->ucbProtocolLen = pcszSep - pcszProtocol;

         ppu->pcszSuffix = pcszSep + lstrlen(s_cszURLSeparator);
         ppu->ucbSuffixLen = lstrlen(ppu->pcszSuffix);

         hr = S_OK;

#ifdef DEBUG

         char rgchDebugProtocol[MAX_PATH_LEN];
         char rgchDebugSuffix[MAX_PATH_LEN];

         // (+ 1) for null terminator.

         MyLStrCpyN(rgchDebugProtocol, ppu->pcszProtocol,
                    min(ppu->ucbProtocolLen + 1, sizeof(rgchDebugProtocol)));

         // (+ 1) for null terminator.

         MyLStrCpyN(rgchDebugSuffix, ppu->pcszSuffix,
                    min(ppu->ucbSuffixLen + 1, sizeof(rgchDebugSuffix)));

         TRACE_OUT(("ParseURL(): Parsed protocol \"%s\" and suffix \"%s\" from URL \"%s\".",
                    rgchDebugProtocol,
                    rgchDebugSuffix,
                    pcszURL));

#endif

      }
      else
         // URL starts with separator.
         TRACE_OUT(("ParseURL: No protocol in URL \"%s\".",
                    pcszURL));
   }
   else
      // No protocol.  URL starts with separator.
      TRACE_OUT(("ParseURL: No %s separator in URL \"%s\".",
                 s_cszURLSeparator,
                 pcszURL));

   ASSERT(FAILED(hr) ||
          EVAL(IsValidPCPARSEDURL(pcszURL, ppu)));

   return(hr);
}


/********************************** Methods **********************************/


HRESULT STDMETHODCALLTYPE InternetShortcut::RegisterProtocolHandler(
                                                            HWND hwndParent,
                                                            PSTR pszAppBuf,
                                                            UINT ucAppBufLen)
{
   HRESULT hr;
   DWORD dwFlags = 0;

   DebugEntry(InternetShortcut::RegisterProtocolHandler);

   ASSERT(! hwndParent ||
          IS_VALID_HANDLE(hwndParent, WND));
   ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszAppBuf, STR, ucAppBufLen));

   ASSERT(IS_VALID_STRING_PTR(m_pszURL, STR));

   SET_FLAG(dwFlags, URLASSOCDLG_FL_REGISTER_ASSOC);

   if (! m_pszFile)
      SET_FLAG(dwFlags, URLASSOCDLG_FL_USE_DEFAULT_NAME);

   hr = URLAssociationDialog(hwndParent, dwFlags, m_pszFile, m_pszURL,
                             pszAppBuf, ucAppBufLen);

   switch (hr)
   {
      case S_FALSE:
         TRACE_OUT(("InternetShortcut::RegisterProtocolHandler(): One time execution of %s via %s requested.",
                    m_pszURL,
                    pszAppBuf));
         break;

      case S_OK:
         TRACE_OUT(("InternetShortcut::RegisterProtocolHandler(): Protocol handler registered for %s.",
                    m_pszURL));
         break;

      default:
         ASSERT(FAILED(hr));
         break;
   }

   ASSERT(! ucAppBufLen ||
          (IS_VALID_STRING_PTR(pszAppBuf, STR) &&
           (UINT)lstrlen(pszAppBuf) < ucAppBufLen));

   DebugExitHRESULT(InternetShortcut::RegisterProtocolHandler, hr);

   return(hr);
}


HRESULT STDMETHODCALLTYPE InternetShortcut::SetURL(PCSTR pcszURL,
                                                   DWORD dwInFlags)
{
   HRESULT hr;
   BOOL bChanged;
   PSTR pszNewURL = NULL;

   DebugEntry(InternetShortcut::SetURL);

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(! pcszURL ||
          IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_IURL_SETURL_FLAGS));

   bChanged = ! ((! pcszURL && ! m_pszURL) ||
                 (pcszURL && m_pszURL &&
                  ! lstrcmp(pcszURL, m_pszURL)));

   if (bChanged && pcszURL)
   {
      DWORD dwTranslateURLFlags;
      PSTR pszTranslatedURL;

      dwTranslateURLFlags = 0;

      if (IS_FLAG_SET(dwInFlags, IURL_SETURL_FL_GUESS_PROTOCOL))
         SET_FLAG(dwTranslateURLFlags, TRANSLATEURL_FL_GUESS_PROTOCOL);

      if (IS_FLAG_SET(dwInFlags, IURL_SETURL_FL_USE_DEFAULT_PROTOCOL))
         SET_FLAG(dwTranslateURLFlags, TRANSLATEURL_FL_USE_DEFAULT_PROTOCOL);

      hr = TranslateURL(pcszURL, dwTranslateURLFlags, &pszTranslatedURL);

      if (SUCCEEDED(hr))
      {
         PCSTR pcszURLToUse;

         // Still different?

         if (hr == S_OK)
         {
            bChanged = (lstrcmp(pszTranslatedURL, m_pszURL) != 0);

            pcszURLToUse = pszTranslatedURL;
         }
         else
         {
            ASSERT(hr == S_FALSE);

            pcszURLToUse = pcszURL;
         }

         if (bChanged)
         {
            PARSEDURL pu;

            // Validate URL syntax.

            hr = ParseURL(pcszURLToUse, &pu);

            if (hr == S_OK)
            {
               pszNewURL = new(char[lstrlen(pcszURLToUse) + 1]);

               if (pszNewURL)
                  lstrcpy(pszNewURL, pcszURLToUse);
               else
                  hr = E_OUTOFMEMORY;
            }
         }
         else
            hr = S_OK;
      }

      if (pszTranslatedURL)
      {
         LocalFree(pszTranslatedURL);
         pszTranslatedURL = NULL;
      }
   }
   else
      hr = S_OK;

   if (hr == S_OK)
   {
      if (bChanged)
      {
         if (m_pszURL)
            delete m_pszURL;

         m_pszURL = pszNewURL;

         Dirty(TRUE);

         TRACE_OUT(("InternetShortcut::SetURL(): Set URL to %s.",
                    CHECK_STRING(m_pszURL)));
      }
      else
         TRACE_OUT(("InternetShortcut::SetURL(): URL already %s.",
                    CHECK_STRING(m_pszURL)));
   }

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(hr == S_OK ||
          hr == E_OUTOFMEMORY ||
          hr == URL_E_INVALID_SYNTAX);

   DebugExitHRESULT(InternetShortcut::SetURL, hr);

   return(hr);
}


HRESULT STDMETHODCALLTYPE InternetShortcut::GetURL(PSTR *ppszURL)
{
   HRESULT hr;

   DebugEntry(InternetShortcut::GetURL);

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(IS_VALID_WRITE_PTR(ppszURL, PSTR));

   *ppszURL = NULL;

   if (m_pszURL)
   {
      // (+ 1) for null terminator.
      *ppszURL = (PSTR)SHAlloc(lstrlen(m_pszURL) + 1);

      if (*ppszURL)
      {
         lstrcpy(*ppszURL, m_pszURL);

         hr = S_OK;

         TRACE_OUT(("InternetShortcut::GetURL(): Got URL %s.",
                    *ppszURL));
      }
      else
         hr = E_OUTOFMEMORY;
   }
   else
      // No URL.
      hr = S_FALSE;

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszURL, STR)) ||
          ((hr == S_FALSE ||
            hr == E_OUTOFMEMORY) &&
           ! *ppszURL));

   DebugExitHRESULT(InternetShortcut::GetURL, hr);

   return(hr);
}


HRESULT STDMETHODCALLTYPE InternetShortcut::InvokeCommand(
                                                PURLINVOKECOMMANDINFO purlici)
{
   HRESULT hr;
   char szOneShotApp[MAX_PATH_LEN];
   BOOL bExecFailedWhine = FALSE;

   DebugEntry(InternetShortcut::InvokeCommand);

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(IS_VALID_STRUCT_PTR(purlici, CURLINVOKECOMMANDINFO));

   if (m_pszURL)
   {
      PSTR pszProtocol;

      hr = CopyURLProtocol(m_pszURL, &pszProtocol);

      if (hr == S_OK)
      {
         hr = IsProtocolRegistered(pszProtocol);

         if (hr == URL_E_UNREGISTERED_PROTOCOL &&
             IS_FLAG_SET(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI))
         {
            TRACE_OUT(("InternetShortcut::InvokeCommand(): Unregistered URL protocol %s.  Invoking URL protocol handler association dialog.",
                       pszProtocol));

            hr = RegisterProtocolHandler(purlici->hwndParent, szOneShotApp,
                                         sizeof(szOneShotApp));
         }

         switch (hr)
         {
            case S_OK:
            {
               SHELLEXECUTEINFO sei;
               char szDefaultVerb[MAX_PATH_LEN];

               // Execute URL via registered protocol handler.

               ZeroMemory(&sei, sizeof(sei));

               sei.fMask = (SEE_MASK_CLASSNAME | SEE_MASK_NO_HOOKS);

               if (IS_FLAG_CLEAR(purlici->dwFlags,
                                 IURL_INVOKECOMMAND_FL_ALLOW_UI))
                  SET_FLAG(sei.fMask, SEE_MASK_FLAG_NO_UI);

               if (IS_FLAG_CLEAR(purlici->dwFlags,
                                 IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB))
                  sei.lpVerb = purlici->pcszVerb;
               else
               {
                  if (GetClassDefaultVerb(pszProtocol, szDefaultVerb,
                                          sizeof(szDefaultVerb)))
                     sei.lpVerb = szDefaultVerb;
                  else
                     ASSERT(! sei.lpVerb);
               }

               sei.cbSize = sizeof(sei);
               sei.hwnd = purlici->hwndParent;
               sei.lpFile = m_pszURL;
               sei.lpDirectory = m_pszWorkingDirectory;
               sei.nShow = m_nShowCmd;
               sei.lpClass = pszProtocol;

               TRACE_OUT(("InternetShortcut::InvokeCommand(): Invoking %s verb on URL %s.",
                          sei.lpVerb ? sei.lpVerb : "open",
                          sei.lpFile));

               hr = ShellExecuteEx(&sei) ? S_OK : IS_E_EXEC_FAILED;

               if (hr == S_OK)
                  TRACE_OUT(("InternetShortcut::InvokeCommand(): ShellExecuteEx() via registered protcol handler succeeded for %s.",
                             m_pszURL));
               else
                  WARNING_OUT(("InternetShortcut::InvokeCommand(): ShellExecuteEx() via registered protcol handler failed for %s.",
                               m_pszURL));

               break;
            }

            case S_FALSE:
               hr = MyExecute(szOneShotApp, m_pszURL, 0);
               switch (hr)
               {
                  case E_FAIL:
                     bExecFailedWhine = TRUE;
                     hr = IS_E_EXEC_FAILED;
                     break;

                  default:
                     break;
               }
               break;

            default:
               ASSERT(FAILED(hr));
               break;
         }

         delete pszProtocol;
         pszProtocol = NULL;
      }
   }
   else
      // No URL.  Not an error.
      hr = S_FALSE;

   if (FAILED(hr) &&
       IS_FLAG_SET(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI))
   {
      int nResult;

      switch (hr)
      {
         case IS_E_EXEC_FAILED:
            if (bExecFailedWhine)
            {
               ASSERT(IS_VALID_STRING_PTR(szOneShotApp, STR));

               if (MyMsgBox(purlici->hwndParent,
                            MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
                            MAKEINTRESOURCE(IDS_EXEC_FAILED),
                            (MB_OK | MB_ICONEXCLAMATION), &nResult,
                            szOneShotApp))
                  ASSERT(nResult == IDOK);
            }
            break;

         case URL_E_INVALID_SYNTAX:
            ASSERT(IS_VALID_STRING_PTR(m_pszURL, STR));

            if (MyMsgBox(purlici->hwndParent,
                         MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
                         MAKEINTRESOURCE(IDS_EXEC_INVALID_SYNTAX),
                         (MB_OK | MB_ICONEXCLAMATION), &nResult, m_pszURL))
               ASSERT(nResult == IDOK);

            break;

         case URL_E_UNREGISTERED_PROTOCOL:
         {
            PSTR pszProtocol;

            ASSERT(IS_VALID_STRING_PTR(m_pszURL, STR));

            if (CopyURLProtocol(m_pszURL, &pszProtocol) == S_OK)
            {
               if (MyMsgBox(purlici->hwndParent,
                            MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
                            MAKEINTRESOURCE(IDS_EXEC_UNREGISTERED_PROTOCOL),
                            (MB_OK | MB_ICONEXCLAMATION), &nResult,
                            pszProtocol))
                  ASSERT(nResult == IDOK);

               delete pszProtocol;
               pszProtocol = NULL;
            }

            break;
         }

         case E_OUTOFMEMORY:
            if (MyMsgBox(purlici->hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
                         MAKEINTRESOURCE(IDS_EXEC_OUT_OF_MEMORY),
                         (MB_OK | MB_ICONEXCLAMATION), &nResult))
               ASSERT(nResult == IDOK);
            break;

         default:
            ASSERT(hr == E_ABORT);
            break;
      }
   }

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(hr == S_OK ||
          hr == E_ABORT ||
          hr == E_OUTOFMEMORY ||
          hr == URL_E_INVALID_SYNTAX ||
          hr == URL_E_UNREGISTERED_PROTOCOL ||
          hr == IS_E_EXEC_FAILED);

   DebugExitHRESULT(InternetShortcut::InvokeCommand, hr);

   return(hr);
}


/***************************** Exported Functions ****************************/


INTSHCUTAPI HRESULT WINAPI TranslateURLA(PCSTR pcszURL, DWORD dwInFlags,
                                         PSTR *ppszTranslatedURL)
{
   HRESULT hr;

   DebugEntry(TranslateURLA);

   *ppszTranslatedURL = NULL;

#ifdef EXPV
   /* Verify parameters. */

   if (IS_VALID_STRING_PTR(pcszURL, CSTR) &&
       IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR))
   {
      if (FLAGS_ARE_VALID(dwInFlags, ALL_TRANSLATEURL_FLAGS))
#endif
      {
         PSTR pszTempTranslatedURL;

         hr = MyTranslateURL(pcszURL, dwInFlags, &pszTempTranslatedURL);

         if (hr == S_OK)
         {
            // (+ 1) for null terminator.
            *ppszTranslatedURL = (PSTR)LocalAlloc(
                                            LMEM_FIXED,
                                            lstrlen(pszTempTranslatedURL) + 1);

            if (*ppszTranslatedURL)
            {
               lstrcpy(*ppszTranslatedURL, pszTempTranslatedURL);
               ASSERT(lstrlen(*ppszTranslatedURL) == lstrlen(pszTempTranslatedURL));
            }
            else
               hr = E_OUTOFMEMORY;

            delete pszTempTranslatedURL;
            pszTempTranslatedURL = NULL;
         }
      }
#ifdef EXPV
      else
         hr = E_FLAGS;
   }
   else
      hr = E_POINTER;
#endif

   switch (hr)
   {
      case S_FALSE:
         TRACE_OUT(("TranslateURLA(): URL %s does not require translation.",
                    pcszURL));
         break;

      case S_OK:
         TRACE_OUT(("TranslateURLA(): URL %s translated to URL %s.",
                    pcszURL,
                    *ppszTranslatedURL));
         break;

      default:
         ASSERT(hr == E_OUTOFMEMORY ||
                hr == E_POINTER);
         break;
   }

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) ||
          ((hr == S_FALSE ||
            hr == E_OUTOFMEMORY ||
            hr == E_POINTER ||
            hr == E_FLAGS) &&
           ! *ppszTranslatedURL));

   DebugExitHRESULT(TranslateURLA, hr);

   return(hr);
}


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

INTSHCUTAPI HRESULT WINAPI TranslateURLW(PCWSTR pcszURL, DWORD dwInFlags,
                                         PWSTR *ppszTranslatedURL)
{
   HRESULT hr;

   DebugEntry(TranslateURLW);

   SetLastError(ERROR_NOT_SUPPORTED);
   hr = E_NOTIMPL;

   DebugExitHRESULT(TranslateURLW, hr);

   return(hr);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */

