[SHLWAPI]
[reactos.git] / reactos / dll / win32 / shlwapi / url.c
index d1f64fe..958a842 100644 (file)
@@ -33,6 +33,7 @@
 #include "winternl.h"
 #define NO_SHLWAPI_STREAM
 #include "shlwapi.h"
+#include "intshcut.h"
 #include "wine/debug.h"
 
 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
@@ -118,7 +119,7 @@ static const unsigned char HashDataLookup[256] = {
 
 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
 {
-    int i;
+    unsigned int i;
 
     for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
         if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
@@ -129,19 +130,6 @@ static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
     return URL_SCHEME_UNKNOWN;
 }
 
-static BOOL URL_JustLocation(LPCWSTR str)
-{
-    while(*str && (*str == '/')) str++;
-    if (*str) {
-        while (*str && ((*str == '-') ||
-                        (*str == '.') ||
-                       isalnumW(*str))) str++;
-        if (*str == '/') return FALSE;
-    }
-    return TRUE;
-}
-
-
 /*************************************************************************
  *      @      [SHLWAPI.1]
  *
@@ -158,40 +146,29 @@ static BOOL URL_JustLocation(LPCWSTR str)
 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
 {
     WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
-    DWORD cnt, len;
+    const char *ptr = x;
+    int len;
 
-    y->nScheme = URL_SCHEME_INVALID;
-    if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
-    /* FIXME: leading white space generates error of 0x80041001 which
-     *        is undefined
-     */
-    if (*x <= ' ') return 0x80041001;
-    cnt = 0;
-    y->cchProtocol = 0;
-    y->pszProtocol = x;
-    while (*x) {
-       if (*x == ':') {
-           y->cchProtocol = cnt;
-           cnt = -1;
-           y->pszSuffix = x+1;
-           break;
-       }
-       x++;
-       cnt++;
-    }
+    TRACE("%s %p\n", debugstr_a(x), y);
+
+    if(y->cbSize != sizeof(*y))
+        return E_INVALIDARG;
+
+    while(*ptr && (isalnum(*ptr) || *ptr == '-'))
+        ptr++;
 
-    /* check for no scheme in string start */
-    /* (apparently schemes *must* be larger than a single character)  */
-    if ((*x == '\0') || (y->cchProtocol <= 1)) {
-       y->pszProtocol = NULL;
-       return 0x80041001;
+    if (*ptr != ':' || ptr <= x+1) {
+        y->pszProtocol = NULL;
+        return URL_E_INVALID_SYNTAX;
     }
 
-    /* found scheme, set length of remainder */
-    y->cchSuffix = lstrlenA(y->pszSuffix);
+    y->pszProtocol = x;
+    y->cchProtocol = ptr-x;
+    y->pszSuffix = ptr+1;
+    y->cchSuffix = strlen(y->pszSuffix);
 
-    len = MultiByteToWideChar(CP_ACP, 0, y->pszProtocol, y->cchProtocol,
-                              scheme, sizeof(scheme)/sizeof(WCHAR));
+    len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
+            scheme, sizeof(scheme)/sizeof(WCHAR));
     y->nScheme = get_scheme_code(scheme, len);
 
     return S_OK;
@@ -204,38 +181,26 @@ HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
  */
 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
 {
-    DWORD cnt;
+    const WCHAR *ptr = x;
 
-    y->nScheme = URL_SCHEME_INVALID;
-    if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
-    /* FIXME: leading white space generates error of 0x80041001 which
-     *        is undefined
-     */
-    if (*x <= ' ') return 0x80041001;
-    cnt = 0;
-    y->cchProtocol = 0;
-    y->pszProtocol = x;
-    while (*x) {
-        if (*x == ':') {
-           y->cchProtocol = cnt;
-           cnt = -1;
-           y->pszSuffix = x+1;
-           break;
-       }
-       x++;
-       cnt++;
-    }
+    TRACE("%s %p\n", debugstr_w(x), y);
+
+    if(y->cbSize != sizeof(*y))
+        return E_INVALIDARG;
 
-    /* check for no scheme in string start */
-    /* (apparently schemes *must* be larger than a single character)  */
-    if ((*x == '\0') || (y->cchProtocol <= 1)) {
-       y->pszProtocol = NULL;
-       return 0x80041001;
+    while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
+        ptr++;
+
+    if (*ptr != ':' || ptr <= x+1) {
+        y->pszProtocol = NULL;
+        return URL_E_INVALID_SYNTAX;
     }
 
-    /* found scheme, set length of remainder */
-    y->cchSuffix = lstrlenW(y->pszSuffix);
-    y->nScheme = get_scheme_code(y->pszProtocol, y->cchProtocol);
+    y->pszProtocol = x;
+    y->cchProtocol = ptr-x;
+    y->pszSuffix = ptr+1;
+    y->cchSuffix = strlenW(y->pszSuffix);
+    y->nScheme = get_scheme_code(x, ptr-x);
 
     return S_OK;
 }
@@ -314,11 +279,13 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
     HRESULT hr = S_OK;
     DWORD EscapeFlags;
     LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
-    INT nByteLen, state;
-    DWORD nLen, nWkLen;
+    INT state;
+    DWORD nByteLen, nLen, nWkLen;
     WCHAR slash = '/';
 
     static const WCHAR wszFile[] = {'f','i','l','e',':'};
+    static const WCHAR wszRes[] = {'r','e','s',':'};
+    static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
 
     TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
         pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
@@ -331,7 +298,7 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
         return S_OK;
     }
 
-    nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
+    nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
     lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
                            INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
 
@@ -339,6 +306,11 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
             && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
         slash = '\\';
 
+    if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
+        dwFlags &= ~URL_FILE_USE_PATHURL;
+        slash = '\0';
+    }
+
     /*
      * state =
      *         0   initial  1,3
@@ -383,10 +355,16 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
             if (*wk1++ == ':') state = 2;
             break;
         case 2:
-            if (*wk1 != '/') {state = 3; break;}
             *wk2++ = *wk1++;
             if (*wk1 != '/') {state = 6; break;}
             *wk2++ = *wk1++;
+            if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
+                        && !strncmpW(wszFile, pszUrl, sizeof(wszFile)/sizeof(WCHAR))
+                        && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
+                wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
+                while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
+                    wk1++;
+            }
             if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
                 wk1++;
             state = 4;
@@ -398,10 +376,12 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
             wk1 += nWkLen;
             wk2 += nWkLen;
 
-            while(mp < wk2) {
-                if(*mp == '/' || *mp == '\\')
-                    *mp = slash;
-                mp++;
+            if(slash) {
+                while(mp < wk2) {
+                    if(*mp == '/' || *mp == '\\')
+                        *mp = slash;
+                    mp++;
+                }
             }
             break;
         case 4:
@@ -410,13 +390,20 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
             while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
                 *wk2++ = *wk1++;
             state = 5;
-            if (!*wk1)
-                *wk2++ = slash;
+            if (!*wk1) {
+                if(slash)
+                    *wk2++ = slash;
+                else
+                    *wk2++ = '/';
+            }
             break;
         case 5:
             if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
             while(*wk1 == '/' || *wk1 == '\\') {
-                *wk2++ = slash;
+                if(slash)
+                    *wk2++ = slash;
+                else
+                    *wk2++ = *wk1;
                 wk1++;
             }
             state = 6;
@@ -449,7 +436,10 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
                     wk2 += nLen;
                     wk1 += nLen;
                 }
-                *wk2++ = slash;
+                if(slash)
+                    *wk2++ = slash;
+                else
+                    *wk2++ = *wk1;
                 wk1++;
 
                 if (*wk1 == '.') {
@@ -466,7 +456,10 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
                             /* case /../ -> need to backup wk2 */
                             TRACE("found '/../'\n");
                             *(wk2-1) = '\0';  /* set end of string */
-                            mp = strrchrW(root, slash);
+                            mp = strrchrW(root, '/');
+                            mp2 = strrchrW(root, '\\');
+                            if(mp2 && (!mp || mp2 < mp))
+                                mp = mp2;
                             if (mp && (mp >= root)) {
                                 /* found valid backup point */
                                 wk2 = mp + 1;
@@ -495,7 +488,7 @@ HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
              debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
     }
     nLen = lstrlenW(lpszUrlCpy);
-    while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
+    while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
         lpszUrlCpy[--nLen]=0;
 
     if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
@@ -602,7 +595,6 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
     DWORD len, res1, res2, process_case = 0;
     LPWSTR work, preliminary, mbase, mrelative;
     static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
-    static const WCHAR single_slash[] = {'/','\0'};
     HRESULT ret;
 
     TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
@@ -639,6 +631,8 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
        process_case = 1;
     }
     else do {
+        BOOL manual_search = FALSE;
+
         /* mk is a special case */
         if(base.nScheme == URL_SCHEME_MK) {
             static const WCHAR wsz[] = {':',':',0};
@@ -653,29 +647,61 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
                 base.pszSuffix += delta;
                 base.cchSuffix -= delta;
             }
+        }else {
+            /* get size of location field (if it exists) */
+            work = (LPWSTR)base.pszSuffix;
+            sizeloc = 0;
+            if (*work++ == '/') {
+                if (*work++ == '/') {
+                    /* At this point have start of location and
+                     * it ends at next '/' or end of string.
+                     */
+                    while(*work && (*work != '/')) work++;
+                    sizeloc = (DWORD)(work - base.pszSuffix);
+                }
+            }
         }
 
-       /* get size of location field (if it exists) */
-       work = (LPWSTR)base.pszSuffix;
-       sizeloc = 0;
-        if (*work++ == '/') {
-            if (*work++ == '/') {
-               /* At this point have start of location and
-                * it ends at next '/' or end of string.
-                */
-                while(*work && (*work != '/')) work++;
-               sizeloc = (DWORD)(work - base.pszSuffix);
-           }
-       }
+        /* If there is a '#' and the characters immediately preceeding it are
+         * ".htm[l]", then begin looking for the last leaf starting from
+         * the '#'. Otherwise the '#' is not meaningful and just start
+         * looking from the end. */
+        if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
+            const WCHAR htmlW[] = {'.','h','t','m','l',0};
+            const int len_htmlW = 5;
+            const WCHAR htmW[] = {'.','h','t','m',0};
+            const int len_htmW = 4;
+
+            if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
+                work -= len_htmW;
+                if (strncmpiW(work, htmW, len_htmW) == 0)
+                    manual_search = TRUE;
+                work += len_htmW;
+            }
 
-       /* Change .sizep2 to not have the last leaf in it,
-        * Note: we need to start after the location (if it exists)
-        */
-        work = strrchrW((base.pszSuffix+sizeloc), '/');
-       if (work) {
-           len = (DWORD)(work - base.pszSuffix + 1);
-           base.cchSuffix = len;
-       }
+            if (!manual_search &&
+                    work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
+                work -= len_htmlW;
+                if (strncmpiW(work, htmlW, len_htmlW) == 0)
+                    manual_search = TRUE;
+                work += len_htmlW;
+            }
+        }
+
+        if (manual_search) {
+            /* search backwards starting from the current position */
+            while (*work != '/' && work > base.pszSuffix + sizeloc)
+                --work;
+            if (work > base.pszSuffix + sizeloc)
+                base.cchSuffix = work - base.pszSuffix + 1;
+        }else {
+            /* search backwards starting from the end of the string */
+            work = strrchrW((base.pszSuffix+sizeloc), '/');
+            if (work) {
+                len = (DWORD)(work - base.pszSuffix + 1);
+                base.cchSuffix = len;
+            }
+        }
 
        /*
         * At this point:
@@ -717,7 +743,7 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
                process_case = 4;
                break;
            }
-            process_case = (*base.pszSuffix == '/') ? 5 : 3;
+            process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
            break;
        }
 
@@ -750,8 +776,7 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
        }
        process_case = 1;
        break;
-    } while(FALSE); /* a litte trick to allow easy exit from nested if's */
-
+    } while(FALSE); /* a little trick to allow easy exit from nested if's */
 
     ret = S_OK;
     switch (process_case) {
@@ -763,14 +788,8 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
        strcatW(preliminary, mrelative);
        break;
 
-    case 2:  /*
-             * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
-             * and pszRelative starts with "//", then append a "/"
-             */
+    case 2:  /* case where pszRelative replaces scheme, and location */
        strcpyW(preliminary, mrelative);
-       if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
-           URL_JustLocation(relative.pszSuffix))
-           strcatW(preliminary, single_slash);
        break;
 
     case 3:  /*
@@ -780,9 +799,6 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
         memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
        work = preliminary + base.cchProtocol + 1;
        strcpyW(work, relative.pszSuffix);
-       if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
-           URL_JustLocation(relative.pszSuffix))
-           strcatW(work, single_slash);
        break;
 
     case 4:  /*
@@ -843,6 +859,9 @@ HRESULT WINAPI UrlEscapeA(
     HRESULT ret;
     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
 
+    if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
+        return E_INVALIDARG;
+
     if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
         return E_INVALIDARG;
     if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
@@ -851,13 +870,13 @@ HRESULT WINAPI UrlEscapeA(
     }
     if(ret == S_OK) {
         RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
-        if(pszEscaped && *pcchEscaped > lenA) {
+        if(*pcchEscaped > lenA) {
             RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
             pszEscaped[lenA] = 0;
             *pcchEscaped = lenA;
         } else {
             *pcchEscaped = lenA + 1;
-            ret = E_INVALIDARG;
+            ret = E_POINTER;
         }
     }
     if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
@@ -1150,13 +1169,15 @@ HRESULT WINAPI UrlUnescapeA(
     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
          pcchUnescaped, dwFlags);
 
-    if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
-       return E_INVALIDARG;
+    if (!pszUrl) return E_INVALIDARG;
 
     if(dwFlags & URL_UNESCAPE_INPLACE)
         dst = pszUrl;
     else
+    {
+        if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
         dst = pszUnescaped;
+    }
 
     for(src = pszUrl, needed = 0; *src; src++, needed++) {
         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
@@ -1217,13 +1238,15 @@ HRESULT WINAPI UrlUnescapeW(
     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
          pcchUnescaped, dwFlags);
 
-    if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
-       return E_INVALIDARG;
+    if(!pszUrl) return E_INVALIDARG;
 
     if(dwFlags & URL_UNESCAPE_INPLACE)
         dst = pszUrl;
     else
+    {
+        if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
         dst = pszUnescaped;
+    }
 
     for(src = pszUrl, needed = 0; *src; src++, needed++) {
         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
@@ -1413,8 +1436,7 @@ HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
 {
   INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
 
-  if (IsBadReadPtr(lpSrc, nSrcLen) ||
-      IsBadWritePtr(lpDest, nDestLen))
+  if (!lpSrc || !lpDest)
     return E_INVALIDARG;
 
   while (destCount >= 0)
@@ -1499,32 +1521,38 @@ HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
 {
     LPWSTR in, out;
-    DWORD ret, len, len2;
+    HRESULT ret;
+    DWORD len;
 
-    TRACE("(in %s, out size %d, flags %08x) using W version\n",
-         debugstr_a(pszIn), *pcchOut, dwFlags);
+    TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
+            pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
+
+    if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
 
     in = HeapAlloc(GetProcessHeap(), 0,
-                             (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
+                  (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
     out = in + INTERNET_MAX_URL_LENGTH;
 
-    MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
+    MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
     len = INTERNET_MAX_URL_LENGTH;
 
     ret = UrlApplySchemeW(in, out, &len, dwFlags);
-    if ((ret != S_OK) && (ret != S_FALSE)) {
-       HeapFree(GetProcessHeap(), 0, in);
-       return ret;
+    if (ret != S_OK) {
+        HeapFree(GetProcessHeap(), 0, in);
+        return ret;
     }
 
-    len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
-    if (len2 > *pcchOut) {
-       *pcchOut = len2;
-       HeapFree(GetProcessHeap(), 0, in);
-       return E_POINTER;
+    len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
+    if (len > *pcchOut) {
+        ret = E_POINTER;
+        goto cleanup;
     }
-    WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
-    *pcchOut = len2;
+
+    WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
+    len--;
+
+cleanup:
+    *pcchOut = len;
     HeapFree(GetProcessHeap(), 0, in);
     return ret;
 }
@@ -1574,29 +1602,31 @@ static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
        index++;
     }
     RegCloseKey(newkey);
-    return -1;
+    return E_FAIL;
 }
 
 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
 {
     HKEY newkey;
     DWORD data_len, dwType;
-    WCHAR reg_path[MAX_PATH];
-    WCHAR value[MAX_PATH], data[MAX_PATH];
+    WCHAR data[MAX_PATH];
+
+    static const WCHAR prefix_keyW[] =
+        {'S','o','f','t','w','a','r','e',
+         '\\','M','i','c','r','o','s','o','f','t',
+         '\\','W','i','n','d','o','w','s',
+         '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
+         '\\','U','R','L',
+         '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
 
     /* get and prepend default */
-    MultiByteToWideChar(0, 0,
-        "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
-                       -1, reg_path, MAX_PATH);
-    RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
-    data_len = MAX_PATH;
-    value[0] = '@';
-    value[1] = '\0';
-    RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
+    RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
+    data_len = sizeof(data);
+    RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
     RegCloseKey(newkey);
     if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
-       *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
-       return E_POINTER;
+        *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
+        return E_POINTER;
     }
     strcpyW(pszOut, data);
     strcatW(pszOut, pszIn);
@@ -1616,8 +1646,10 @@ HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DW
     DWORD res1;
     HRESULT ret;
 
-    TRACE("(in %s, out size %d, flags %08x)\n",
-         debugstr_w(pszIn), *pcchOut, dwFlags);
+    TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
+            pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
+
+    if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
 
     if (dwFlags & URL_APPLY_GUESSFILE) {
        FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
@@ -1633,7 +1665,7 @@ HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DW
     if (res1) {
        /* no scheme in input, need to see if we need to guess */
        if (dwFlags & URL_APPLY_GUESSSCHEME) {
-           if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
+           if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
                return ret;
        }
     }
@@ -1662,14 +1694,6 @@ HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DW
        return URL_ApplyDefault(pszIn, pszOut, pcchOut);
     }
 
-    /* just copy and give proper return code */
-    if (strlenW(pszIn) + 1 > *pcchOut) {
-       *pcchOut = strlenW(pszIn) + 1;
-       return E_POINTER;
-    }
-    strcpyW(pszOut, pszIn);
-    *pcchOut = strlenW(pszOut);
-    TRACE("returning copy, left alone\n");
     return S_FALSE;
 }
 
@@ -1888,7 +1912,8 @@ static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
                  (*start == '_') ||
                  (*start == '+') ||
                  (*start == '-') ||
-                 (*start == '.')) {
+                 (*start == '.') ||
+                 (*start == ' ')) {
                start++;
                (*size)++;
             } else if (*start == '%') {
@@ -1918,7 +1943,8 @@ static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
        while (cont) {
            if (isalnumW(*start) ||
                 (*start == '-') ||
-                (*start == '.') ) {
+                (*start == '.') ||
+                (*start == ' ') ) {
                start++;
                (*size)++;
            }
@@ -1947,7 +1973,7 @@ static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
     work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
     if (!*work || (*work != ':')) goto ErrorExit;
     work++;
-    if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
+    if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
     pl->pUserName = work + 2;
     work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
     if (*work == ':' ) {
@@ -1988,6 +2014,7 @@ static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
         pl->pQuery = strchrW(work, '?');
        if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
     }
+  SuccessExit:
     TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
          pl->pScheme, pl->szScheme,
          pl->pUserName, pl->szUserName,
@@ -2010,7 +2037,7 @@ static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
  *  pszIn   [I]   Url to parse
  *  pszOut  [O]   Destination for part of pszIn requested
  *  pcchOut [I]   Size of pszOut
- *          [O]   length of pszOut string EXLUDING '\0' if S_OK, otherwise
+ *          [O]   length of pszOut string EXCLUDING '\0' if S_OK, otherwise
  *                needed size of pszOut INCLUDING '\0'.
  *  dwPart  [I]   URL_PART_ enum from "shlwapi.h"
  *  dwFlags [I]   URL_ flags from "shlwapi.h"
@@ -2025,6 +2052,9 @@ HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
     LPWSTR in, out;
     DWORD ret, len, len2;
 
+    if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
+        return E_INVALIDARG;
+
     in = HeapAlloc(GetProcessHeap(), 0,
                              (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
     out = in + INTERNET_MAX_URL_LENGTH;
@@ -2034,21 +2064,21 @@ HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
     len = INTERNET_MAX_URL_LENGTH;
     ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
 
-    if (ret != S_OK) {
+    if (FAILED(ret)) {
        HeapFree(GetProcessHeap(), 0, in);
        return ret;
     }
 
     len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
     if (len2 > *pcchOut) {
-       *pcchOut = len2;
+       *pcchOut = len2+1;
        HeapFree(GetProcessHeap(), 0, in);
        return E_POINTER;
     }
-    WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
-    *pcchOut = len2;
+    len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
+    *pcchOut = len2-1;
     HeapFree(GetProcessHeap(), 0, in);
-    return S_OK;
+    return ret;
 }
 
 /*************************************************************************
@@ -2061,63 +2091,115 @@ HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
 {
     WINE_PARSE_URL pl;
     HRESULT ret;
-    DWORD size, schsize;
+    DWORD scheme, size, schsize;
     LPCWSTR addr, schaddr;
 
     TRACE("(%s %p %p(%d) %08x %08x)\n",
          debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
 
+    if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
+        return E_INVALIDARG;
+
+    *pszOut = '\0';
+
+    addr = strchrW(pszIn, ':');
+    if(!addr)
+        scheme = URL_SCHEME_UNKNOWN;
+    else
+        scheme = get_scheme_code(pszIn, addr-pszIn);
+
     ret = URL_ParseUrl(pszIn, &pl);
-    if (!ret) {
-       schaddr = pl.pScheme;
-       schsize = pl.szScheme;
 
        switch (dwPart) {
        case URL_PART_SCHEME:
-           if (!pl.szScheme) return E_INVALIDARG;
+           if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
+               *pcchOut = 0;
+               return S_FALSE;
+           }
            addr = pl.pScheme;
            size = pl.szScheme;
            break;
 
        case URL_PART_HOSTNAME:
-           if (!pl.szHostName) return E_INVALIDARG;
+            switch(scheme) {
+            case URL_SCHEME_FTP:
+            case URL_SCHEME_HTTP:
+            case URL_SCHEME_GOPHER:
+            case URL_SCHEME_TELNET:
+            case URL_SCHEME_FILE:
+            case URL_SCHEME_HTTPS:
+                break;
+            default:
+                *pcchOut = 0;
+                return E_FAIL;
+            }
+
+            if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
+                        (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
+                *pcchOut = 0;
+                return S_FALSE;
+            }
+
+           if (!pl.szHostName) {
+               *pcchOut = 0;
+               return S_FALSE;
+           }
            addr = pl.pHostName;
            size = pl.szHostName;
            break;
 
        case URL_PART_USERNAME:
-           if (!pl.szUserName) return E_INVALIDARG;
+           if (!pl.szUserName) {
+               *pcchOut = 0;
+               return S_FALSE;
+           }
            addr = pl.pUserName;
            size = pl.szUserName;
            break;
 
        case URL_PART_PASSWORD:
-           if (!pl.szPassword) return E_INVALIDARG;
+           if (!pl.szPassword) {
+               *pcchOut = 0;
+               return S_FALSE;
+           }
            addr = pl.pPassword;
            size = pl.szPassword;
            break;
 
        case URL_PART_PORT:
-           if (!pl.szPort) return E_INVALIDARG;
+           if (!pl.szPort) {
+               *pcchOut = 0;
+               return S_FALSE;
+           }
            addr = pl.pPort;
            size = pl.szPort;
            break;
 
        case URL_PART_QUERY:
-           if (!pl.szQuery) return E_INVALIDARG;
+           if (!pl.szQuery) {
+               *pcchOut = 0;
+               return S_FALSE;
+           }
            addr = pl.pQuery;
            size = pl.szQuery;
            break;
 
        default:
+           *pcchOut = 0;
            return E_INVALIDARG;
        }
 
        if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
+            if(!pl.pScheme || !pl.szScheme) {
+                *pcchOut = 0;
+                return E_FAIL;
+            }
+            schaddr = pl.pScheme;
+            schsize = pl.szScheme;
             if (*pcchOut < schsize + size + 2) {
                 *pcchOut = schsize + size + 2;
-               return E_POINTER;
-           }
+                return E_POINTER;
+            }
             memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
             pszOut[schsize] = ':';
             memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
@@ -2131,7 +2213,7 @@ HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
            *pcchOut = size;
        }
        TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
-    }
+
     return ret;
 }
 
@@ -2150,14 +2232,16 @@ HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
 {
     PARSEDURLA base;
-    DWORD res1;
+    HRESULT hres;
+
+    TRACE("%s\n", debugstr_a(lpstrPath));
 
     if (!lpstrPath || !*lpstrPath) return FALSE;
 
     /* get protocol        */
     base.cbSize = sizeof(base);
-    res1 = ParseURLA(lpstrPath, &base);
-    return (base.nScheme != URL_SCHEME_INVALID);
+    hres = ParseURLA(lpstrPath, &base);
+    return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
 }
 
 /*************************************************************************
@@ -2168,14 +2252,16 @@ BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
 {
     PARSEDURLW base;
-    DWORD res1;
+    HRESULT hres;
+
+    TRACE("%s\n", debugstr_w(lpstrPath));
 
     if (!lpstrPath || !*lpstrPath) return FALSE;
 
     /* get protocol        */
     base.cbSize = sizeof(base);
-    res1 = ParseURLW(lpstrPath, &base);
-    return (base.nScheme != URL_SCHEME_INVALID);
+    hres = ParseURLW(lpstrPath, &base);
+    return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
 }
 
 /*************************************************************************
@@ -2287,7 +2373,7 @@ HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUr
  */
 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
 {
-  FIXME("SHAutoComplete stub\n");
+  FIXME("stub\n");
   return S_FALSE;
 }
 
@@ -2305,7 +2391,7 @@ HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
  *  dwDestLen   [I] Length of lpszDest
  *
  * RETURNS
- *  Success: S_OK. lpszDest constains the resource Url.
+ *  Success: S_OK. lpszDest contains the resource Url.
  *  Failure: E_INVALIDARG, if any argument is invalid, or
  *           E_FAIL if dwDestLen is too small.
  */
@@ -2378,7 +2464,7 @@ HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
           dwResLen = strlenW(lpszRes) + 1;
           if (dwDestLen >= dwResLen + 1)
           {
-            lpszDest[szResLen + dwPathLen + dwResLen] = '/';
+            lpszDest[szResLen + dwPathLen-1] = '/';
             memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
             hRet = S_OK;
           }
@@ -2389,3 +2475,35 @@ HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
   }
   return hRet;
 }
+
+/***********************************************************************
+ *             UrlFixupW [SHLWAPI.462]
+ *
+ * Checks the scheme part of a URL and attempts to correct misspellings.
+ *
+ * PARAMS
+ *  lpszUrl           [I] Pointer to the URL to be corrected
+ *  lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
+ *  dwMaxChars        [I] Maximum size of corrected URL
+ *
+ * RETURNS
+ *  success: S_OK if URL corrected or already correct
+ *  failure: S_FALSE if unable to correct / COM error code if other error
+ *
+ */
+HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
+{
+    DWORD srcLen;
+
+    FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
+
+    if (!url)
+        return E_FAIL;
+
+    srcLen = lstrlenW(url) + 1;
+
+    /* For now just copy the URL directly */
+    lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
+
+    return S_OK;
+}