[SHLWAPI]
[reactos.git] / reactos / dll / win32 / shlwapi / url.c
index 19a6e85..958a842 100644 (file)
@@ -15,7 +15,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include "config.h"
@@ -23,7 +23,6 @@
 #include <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
-#include <ctype.h>
 #include "windef.h"
 #include "winbase.h"
 #include "winnls.h"
 #include "winternl.h"
 #define NO_SHLWAPI_STREAM
 #include "shlwapi.h"
+#include "intshcut.h"
 #include "wine/debug.h"
 
+HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
 BOOL    WINAPI MLFreeLibrary(HMODULE);
 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
 
@@ -44,31 +45,28 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell);
 /* The following schemes were identified in the native version of
  * SHLWAPI.DLL version 5.50
  */
-typedef struct {
+static const struct {
     URL_SCHEME  scheme_number;
-    LPCSTR scheme_name;
-} SHL_2_inet_scheme;
-
-static const SHL_2_inet_scheme shlwapi_schemes[] = {
-  {URL_SCHEME_FTP,        "ftp"},
-  {URL_SCHEME_HTTP,       "http"},
-  {URL_SCHEME_GOPHER,     "gopher"},
-  {URL_SCHEME_MAILTO,     "mailto"},
-  {URL_SCHEME_NEWS,       "news"},
-  {URL_SCHEME_NNTP,       "nntp"},
-  {URL_SCHEME_TELNET,     "telnet"},
-  {URL_SCHEME_WAIS,       "wais"},
-  {URL_SCHEME_FILE,       "file"},
-  {URL_SCHEME_MK,         "mk"},
-  {URL_SCHEME_HTTPS,      "https"},
-  {URL_SCHEME_SHELL,      "shell"},
-  {URL_SCHEME_SNEWS,      "snews"},
-  {URL_SCHEME_LOCAL,      "local"},
-  {URL_SCHEME_JAVASCRIPT, "javascript"},
-  {URL_SCHEME_VBSCRIPT,   "vbscript"},
-  {URL_SCHEME_ABOUT,      "about"},
-  {URL_SCHEME_RES,        "res"},
-  {0, 0}
+    WCHAR scheme_name[12];
+} shlwapi_schemes[] = {
+  {URL_SCHEME_FTP,        {'f','t','p',0}},
+  {URL_SCHEME_HTTP,       {'h','t','t','p',0}},
+  {URL_SCHEME_GOPHER,     {'g','o','p','h','e','r',0}},
+  {URL_SCHEME_MAILTO,     {'m','a','i','l','t','o',0}},
+  {URL_SCHEME_NEWS,       {'n','e','w','s',0}},
+  {URL_SCHEME_NNTP,       {'n','n','t','p',0}},
+  {URL_SCHEME_TELNET,     {'t','e','l','n','e','t',0}},
+  {URL_SCHEME_WAIS,       {'w','a','i','s',0}},
+  {URL_SCHEME_FILE,       {'f','i','l','e',0}},
+  {URL_SCHEME_MK,         {'m','k',0}},
+  {URL_SCHEME_HTTPS,      {'h','t','t','p','s',0}},
+  {URL_SCHEME_SHELL,      {'s','h','e','l','l',0}},
+  {URL_SCHEME_SNEWS,      {'s','n','e','w','s',0}},
+  {URL_SCHEME_LOCAL,      {'l','o','c','a','l',0}},
+  {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
+  {URL_SCHEME_VBSCRIPT,   {'v','b','s','c','r','i','p','t',0}},
+  {URL_SCHEME_ABOUT,      {'a','b','o','u','t',0}},
+  {URL_SCHEME_RES,        {'r','e','s',0}},
 };
 
 typedef struct {
@@ -119,18 +117,18 @@ static const unsigned char HashDataLookup[256] = {
  0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
  0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
 
-static BOOL URL_JustLocation(LPCWSTR str)
+static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
 {
-    while(*str && (*str == L'/')) str++;
-    if (*str) {
-       while (*str && ((*str == L'-') ||
-                       (*str == L'.') ||
-                       isalnumW(*str))) str++;
-       if (*str == L'/') return FALSE;
+    unsigned int i;
+
+    for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
+        if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
+           && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
+            return shlwapi_schemes[i].scheme_number;
     }
-    return TRUE;
-}
 
+    return URL_SCHEME_UNKNOWN;
+}
 
 /*************************************************************************
  *      @      [SHLWAPI.1]
@@ -147,50 +145,32 @@ static BOOL URL_JustLocation(LPCWSTR str)
  */
 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
 {
-    DWORD cnt;
-    const SHL_2_inet_scheme *inet_pro;
+    WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
+    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);
 
-    /* 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(y->cbSize != sizeof(*y))
+        return E_INVALIDARG;
 
-    /* found scheme, set length of remainder */
-    y->cchSuffix = lstrlenA(y->pszSuffix);
+    while(*ptr && (isalnum(*ptr) || *ptr == '-'))
+        ptr++;
 
-    /* see if known scheme and return indicator number */
-    y->nScheme = URL_SCHEME_UNKNOWN;
-    inet_pro = shlwapi_schemes;
-    while (inet_pro->scheme_name) {
-       if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
-                   min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
-           y->nScheme = inet_pro->scheme_number;
-           break;
-       }
-       inet_pro++;
+    if (*ptr != ':' || ptr <= x+1) {
+        y->pszProtocol = NULL;
+        return URL_E_INVALID_SYNTAX;
     }
+
+    y->pszProtocol = x;
+    y->cchProtocol = ptr-x;
+    y->pszSuffix = ptr+1;
+    y->cchSuffix = strlen(y->pszSuffix);
+
+    len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
+            scheme, sizeof(scheme)/sizeof(WCHAR));
+    y->nScheme = get_scheme_code(scheme, len);
+
     return S_OK;
 }
 
@@ -201,56 +181,27 @@ HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
  */
 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
 {
-    DWORD cnt;
-    const SHL_2_inet_scheme *inet_pro;
-    LPSTR cmpstr;
-    INT len;
+    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 <= L' ') return 0x80041001;
-    cnt = 0;
-    y->cchProtocol = 0;
-    y->pszProtocol = x;
-    while (*x) {
-       if (*x == L':') {
-           y->cchProtocol = cnt;
-           cnt = -1;
-           y->pszSuffix = x+1;
-           break;
-       }
-       x++;
-       cnt++;
-    }
+    TRACE("%s %p\n", debugstr_w(x), y);
 
-    /* check for no scheme in string start */
-    /* (apparently schemes *must* be larger than a single character)  */
-    if ((*x == L'\0') || (y->cchProtocol <= 1)) {
-       y->pszProtocol = NULL;
-       return 0x80041001;
-    }
+    if(y->cbSize != sizeof(*y))
+        return E_INVALIDARG;
 
-    /* found scheme, set length of remainder */
-    y->cchSuffix = lstrlenW(y->pszSuffix);
-
-    /* see if known scheme and return indicator number */
-    len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
-    cmpstr = HeapAlloc(GetProcessHeap(), 0, len);
-    WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, cmpstr, len, 0, 0);
-    y->nScheme = URL_SCHEME_UNKNOWN;
-    inet_pro = shlwapi_schemes;
-    while (inet_pro->scheme_name) {
-       if (!strncasecmp(inet_pro->scheme_name, cmpstr,
-                   min(len, lstrlenA(inet_pro->scheme_name)))) {
-           y->nScheme = inet_pro->scheme_number;
-           break;
-       }
-       inet_pro++;
+    while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
+        ptr++;
+
+    if (*ptr != ':' || ptr <= x+1) {
+        y->pszProtocol = NULL;
+        return URL_E_INVALID_SYNTAX;
     }
-    HeapFree(GetProcessHeap(), 0, cmpstr);
+
+    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;
 }
 
@@ -282,11 +233,11 @@ HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
        LPDWORD pcchCanonicalized, DWORD dwFlags)
 {
     LPWSTR base, canonical;
-    DWORD ret, len, len2;
+    HRESULT ret;
+    DWORD   len, len2;
 
-    TRACE("(%s %p %p 0x%08lx) using W version\n",
-         debugstr_a(pszUrl), pszCanonicalized,
-         pcchCanonicalized, dwFlags);
+    TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
+        pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
 
     if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
        return E_INVALIDARG;
@@ -300,19 +251,19 @@ HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
 
     ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
     if (ret != S_OK) {
-       HeapFree(GetProcessHeap(), 0, base);
-       return ret;
+        *pcchCanonicalized = len * 2;
+        HeapFree(GetProcessHeap(), 0, base);
+        return ret;
     }
 
-    len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
+    len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
     if (len2 > *pcchCanonicalized) {
-       *pcchCanonicalized = len;
-       HeapFree(GetProcessHeap(), 0, base);
-       return E_POINTER;
+        *pcchCanonicalized = len2;
+        HeapFree(GetProcessHeap(), 0, base);
+        return E_POINTER;
     }
-    WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
-                       *pcchCanonicalized, 0, 0);
-    *pcchCanonicalized = len2;
+    WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
+    *pcchCanonicalized = len;
     HeapFree(GetProcessHeap(), 0, base);
     return S_OK;
 }
@@ -328,159 +279,216 @@ 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;
-    WCHAR slash = dwFlags & URL_FILE_USE_PATHURL ? '\\' : '/';
+    INT state;
+    DWORD nByteLen, nLen, nWkLen;
+    WCHAR slash = '/';
 
-    TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
-         pcchCanonicalized, dwFlags);
+    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);
 
     if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
        return E_INVALIDARG;
 
-    nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
-    lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
+    if(!*pszUrl) {
+        *pszCanonicalized = 0;
+        return S_OK;
+    }
 
-    if (dwFlags & URL_DONT_SIMPLIFY)
-        memcpy(lpszUrlCpy, pszUrl, nByteLen);
-    else {
+    nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
+    lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
+                           INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
 
-       /*
-        * state =
-        *         0   initial  1,3
-        *         1   have 2[+] alnum  2,3
-        *         2   have scheme (found :)  4,6,3
-        *         3   failed (no location)
-        *         4   have //  5,3
-        *         5   have 1[+] alnum  6,3
-        *         6   have location (found /) save root location
-        */
+    if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
+            && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
+        slash = '\\';
 
-       wk1 = (LPWSTR)pszUrl;
-       wk2 = lpszUrlCpy;
-       state = 0;
-       while (*wk1) {
-           switch (state) {
-           case 0:
-               if (!isalnumW(*wk1)) {state = 3; break;}
-               *wk2++ = *wk1++;
-               if (!isalnumW(*wk1)) {state = 3; break;}
-               *wk2++ = *wk1++;
-               state = 1;
-               break;
-           case 1:
-               *wk2++ = *wk1;
-               if (*wk1++ == L':') state = 2;
-               break;
-           case 2:
-               if (*wk1 != L'/') {state = 3; break;}
-               *wk2++ = *wk1++;
-               if (*wk1 != L'/') {state = 6; break;}
-               *wk2++ = *wk1++;
-                if((dwFlags & URL_FILE_USE_PATHURL) && *wk1 == '/')
-                    wk1++;
-               state = 4;
-               break;
-           case 3:
-               nWkLen = strlenW(wk1);
-               memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
-                mp = wk2;
-               wk1 += nWkLen;
-               wk2 += nWkLen;
+    if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
+        dwFlags &= ~URL_FILE_USE_PATHURL;
+        slash = '\0';
+    }
+
+    /*
+     * state =
+     *         0   initial  1,3
+     *         1   have 2[+] alnum  2,3
+     *         2   have scheme (found :)  4,6,3
+     *         3   failed (no location)
+     *         4   have //  5,3
+     *         5   have 1[+] alnum  6,3
+     *         6   have location (found /) save root location
+     */
 
+    wk1 = (LPWSTR)pszUrl;
+    wk2 = lpszUrlCpy;
+    state = 0;
+
+    if(pszUrl[1] == ':') { /* Assume path */
+        static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
+
+        memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
+        wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
+        if (dwFlags & URL_FILE_USE_PATHURL)
+        {
+            slash = '\\';
+            --wk2;
+        }
+        else
+            dwFlags |= URL_ESCAPE_UNSAFE;
+        state = 5;
+    }
+
+    while (*wk1) {
+        switch (state) {
+        case 0:
+            if (!isalnumW(*wk1)) {state = 3; break;}
+            *wk2++ = *wk1++;
+            if (!isalnumW(*wk1)) {state = 3; break;}
+            *wk2++ = *wk1++;
+            state = 1;
+            break;
+        case 1:
+            *wk2++ = *wk1;
+            if (*wk1++ == ':') state = 2;
+            break;
+        case 2:
+            *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;
+            break;
+        case 3:
+            nWkLen = strlenW(wk1);
+            memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
+            mp = wk2;
+            wk1 += nWkLen;
+            wk2 += nWkLen;
+
+            if(slash) {
                 while(mp < wk2) {
                     if(*mp == '/' || *mp == '\\')
                         *mp = slash;
                     mp++;
                 }
-               break;
-           case 4:
-                if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
-                    {state = 3; break;}
-                while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
-                    *wk2++ = *wk1++;
-                state = 5;
-                break;
-            case 5:
-                if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
-                while(*wk1 == '/') {
+            }
+            break;
+        case 4:
+            if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
+                {state = 3; break;}
+            while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
+                *wk2++ = *wk1++;
+            state = 5;
+            if (!*wk1) {
+                if(slash)
                     *wk2++ = slash;
-                    wk1++;
+                else
+                    *wk2++ = '/';
+            }
+            break;
+        case 5:
+            if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
+            while(*wk1 == '/' || *wk1 == '\\') {
+                if(slash)
+                    *wk2++ = slash;
+                else
+                    *wk2++ = *wk1;
+                wk1++;
+            }
+            state = 6;
+            break;
+        case 6:
+            if(dwFlags & URL_DONT_SIMPLIFY) {
+                state = 3;
+                break;
+            }
+            /* Now at root location, cannot back up any more. */
+            /* "root" will point at the '/' */
+
+            root = wk2-1;
+            while (*wk1) {
+                mp = strchrW(wk1, '/');
+                mp2 = strchrW(wk1, '\\');
+                if(mp2 && (!mp || mp2 < mp))
+                    mp = mp2;
+                if (!mp) {
+                    nWkLen = strlenW(wk1);
+                    memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
+                    wk1 += nWkLen;
+                    wk2 += nWkLen;
+                    continue;
                 }
-                state = 6;
-               break;
-           case 6:
-               /* Now at root location, cannot back up any more. */
-               /* "root" will point at the '/' */
-               root = wk2-1;
-               while (*wk1) {
-                   TRACE("wk1=%c\n", (CHAR)*wk1);
-
-                    mp = strchrW(wk1, '/');
-                    mp2 = strchrW(wk1, '\\');
-                    if(mp2 && mp2 < mp)
-                        mp = mp2;
-                   if (!mp) {
-                       nWkLen = strlenW(wk1);
-                       memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
-                       wk1 += nWkLen;
-                       wk2 += nWkLen;
-                       continue;
-                   }
-                    nLen = mp - wk1;
-                    if(nLen) {
-                        memcpy(wk2, wk1, nLen * sizeof(WCHAR));
-                        wk2 += nLen;
-                        wk1 += nLen;
-                    }
+                nLen = mp - wk1;
+                if(nLen) {
+                    memcpy(wk2, wk1, nLen * sizeof(WCHAR));
+                    wk2 += nLen;
+                    wk1 += nLen;
+                }
+                if(slash)
                     *wk2++ = slash;
-                    wk1++;
-
-                   if (*wk1 == L'.') {
-                       TRACE("found '/.'\n");
-                        if (wk1[1] == '/' || wk1[1] == '\\') {
-                           /* case of /./ -> skip the ./ */
-                           wk1 += 2;
-                       }
-                        else if (wk1[1] == '.') {
-                           /* found /..  look for next / */
-                           TRACE("found '/..'\n");
-                            if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
-                                    || wk1[2] == '#' || !wk1[2]) {
-                               /* case /../ -> need to backup wk2 */
-                               TRACE("found '/../'\n");
-                               *(wk2-1) = L'\0';  /* set end of string */
-                                mp = strrchrW(root, slash);
-                               if (mp && (mp >= root)) {
-                                   /* found valid backup point */
-                                   wk2 = mp + 1;
-                                    if(wk1[2] != '/' && wk1[2] != '\\')
-                                       wk1 += 2;
-                                   else
-                                       wk1 += 3;
-                               }
-                               else {
-                                    /* did not find point, restore '/' */
-                                    *(wk2-1) = slash;
-                               }
-                           }
-                       }
-                   }
-               }
-               *wk2 = L'\0';
-               break;
-           default:
-               FIXME("how did we get here - state=%d\n", state);
-                HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
-               return E_INVALIDARG;
-           }
-       }
-       *wk2 = L'\0';
+                else
+                    *wk2++ = *wk1;
+                wk1++;
+
+                if (*wk1 == '.') {
+                    TRACE("found '/.'\n");
+                    if (wk1[1] == '/' || wk1[1] == '\\') {
+                        /* case of /./ -> skip the ./ */
+                        wk1 += 2;
+                    }
+                    else if (wk1[1] == '.') {
+                        /* found /..  look for next / */
+                        TRACE("found '/..'\n");
+                        if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
+                            || wk1[2] == '#' || !wk1[2]) {
+                            /* case /../ -> need to backup wk2 */
+                            TRACE("found '/../'\n");
+                            *(wk2-1) = '\0';  /* set end of string */
+                            mp = strrchrW(root, '/');
+                            mp2 = strrchrW(root, '\\');
+                            if(mp2 && (!mp || mp2 < mp))
+                                mp = mp2;
+                            if (mp && (mp >= root)) {
+                                /* found valid backup point */
+                                wk2 = mp + 1;
+                                if(wk1[2] != '/' && wk1[2] != '\\')
+                                    wk1 += 2;
+                                else
+                                    wk1 += 3;
+                            }
+                            else {
+                                /* did not find point, restore '/' */
+                                *(wk2-1) = slash;
+                            }
+                        }
+                    }
+                }
+            }
+            *wk2 = '\0';
+            break;
+        default:
+            FIXME("how did we get here - state=%d\n", state);
+            HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
+            return E_INVALIDARG;
+        }
+        *wk2 = '\0';
        TRACE("Simplified, orig <%s>, simple <%s>\n",
              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))
@@ -537,7 +545,7 @@ HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
     LPWSTR base, relative, combined;
     DWORD ret, len, len2;
 
-    TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
+    TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
          debugstr_a(pszBase),debugstr_a(pszRelative),
          pcchCombined?*pcchCombined:0,dwFlags);
 
@@ -587,10 +595,9 @@ 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 %ld, flags %08lx)\n",
+    TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
          debugstr_w(pszBase),debugstr_w(pszRelative),
          pcchCombined?*pcchCombined:0,dwFlags);
 
@@ -605,7 +612,7 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
                            sizeof(WCHAR));
     mbase = preliminary + INTERNET_MAX_URL_LENGTH;
     mrelative = mbase + INTERNET_MAX_URL_LENGTH;
-    *preliminary = L'\0';
+    *preliminary = '\0';
 
     /* Canonicalize the base input prior to looking for the scheme */
     myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
@@ -624,28 +631,78 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
        process_case = 1;
     }
     else do {
+        BOOL manual_search = FALSE;
 
-       /* get size of location field (if it exists) */
-       work = (LPWSTR)base.pszSuffix;
-       sizeloc = 0;
-       if (*work++ == L'/') {
-           if (*work++ == L'/') {
-               /* At this point have start of location and
-                * it ends at next '/' or end of string.
-                */
-               while(*work && (*work != L'/')) work++;
-               sizeloc = (DWORD)(work - base.pszSuffix);
-           }
-       }
+        /* mk is a special case */
+        if(base.nScheme == URL_SCHEME_MK) {
+            static const WCHAR wsz[] = {':',':',0};
+
+            WCHAR *ptr = strstrW(base.pszSuffix, wsz);
+            if(ptr) {
+                int delta;
+
+                ptr += 2;
+                delta = ptr-base.pszSuffix;
+                base.cchProtocol += delta;
+                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);
+                }
+            }
+        }
+
+        /* 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;
+            }
+
+            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;
+            }
+        }
 
-       /* 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), L'/');
-       if (work) {
-           len = (DWORD)(work - base.pszSuffix + 1);
-           base.cchSuffix = len;
-       }
        /*
         * At this point:
         *    .pszSuffix   points to location (starting with '//')
@@ -661,7 +718,7 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
            TRACE("no scheme detected in Relative\n");
            relative.pszSuffix = mrelative;  /* case 3,4,5 depends on this */
            relative.cchSuffix = strlenW(mrelative);
-           if (*pszRelative  == L':') {
+            if (*pszRelative  == ':') {
                /* case that is either left alone or uses pszBase */
                if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
                    process_case = 5;
@@ -670,23 +727,23 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
                process_case = 1;
                break;
            }
-           if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
+            if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
                /* case that becomes "file:///" */
                strcpyW(preliminary, myfilestr);
                process_case = 1;
                break;
            }
-           if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
+            if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
                /* pszRelative has location and rest */
                process_case = 3;
                break;
            }
-           if (*mrelative == L'/') {
+            if (*mrelative == '/') {
                /* case where pszRelative is root to location */
                process_case = 4;
                break;
            }
-           process_case = (*base.pszSuffix == L'/') ? 5 : 3;
+            process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
            break;
        }
 
@@ -695,21 +752,22 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
            (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
 
            /* since the schemes are the same */
-           if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
+            if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
                /* case where pszRelative replaces location and following */
                process_case = 3;
                break;
            }
-           if (*relative.pszSuffix == L'/') {
+            if (*relative.pszSuffix == '/') {
                /* case where pszRelative is root to location */
                process_case = 4;
                break;
            }
-           /* case where scheme is followed by document path */
-           process_case = 5;
+            /* replace either just location if base's location starts with a
+             * slash or otherwise everything */
+            process_case = (*base.pszSuffix == '/') ? 5 : 1;
            break;
        }
-       if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
+        if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
            /* case where pszRelative replaces scheme, location,
             * and following and handles PLUGGABLE
             */
@@ -718,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) {
@@ -731,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:  /*
@@ -748,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:  /*
@@ -761,7 +809,7 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
         memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
        work = preliminary + base.cchProtocol + 1 + sizeloc;
        if (dwFlags & URL_PLUGGABLE_PROTOCOL)
-           *(work++) = L'/';
+            *(work++) = '/';
        strcpyW(work, relative.pszSuffix);
        break;
 
@@ -772,23 +820,23 @@ HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
         memcpy(preliminary, base.pszProtocol,
                (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
        work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
-       if (*work++ != L'/')
-           *(work++) = L'/';
+        if (*work++ != '/')
+            *(work++) = '/';
        strcpyW(work, relative.pszSuffix);
        break;
 
     default:
-       FIXME("How did we get here????? process_case=%ld\n", process_case);
+       FIXME("How did we get here????? process_case=%d\n", process_case);
        ret = E_INVALIDARG;
     }
 
     if (ret == S_OK) {
        /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
-       ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
+       ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
        if(SUCCEEDED(ret) && pszCombined) {
            lstrcpyW(pszCombined, mrelative);
        }
-       TRACE("return-%ld len=%ld, %s\n",
+       TRACE("return-%d len=%d, %s\n",
              process_case, *pcchCombined, debugstr_w(pszCombined));
     }
     HeapFree(GetProcessHeap(), 0, preliminary);
@@ -811,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) {
@@ -945,17 +996,17 @@ HRESULT WINAPI UrlEscapeW(
     DWORD slashes = 0;
     static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
 
-    TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
+    TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
          pcchEscaped, dwFlags);
 
-    if(!pszUrl || !pszEscaped || !pcchEscaped)
-       return E_INVALIDARG;
+    if(!pszUrl || !pcchEscaped)
+        return E_INVALIDARG;
 
     if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
                   URL_ESCAPE_SEGMENT_ONLY |
                   URL_DONT_ESCAPE_EXTRA_INFO |
                   URL_ESCAPE_PERCENT))
-        FIXME("Unimplemented flags: %08lx\n", dwFlags);
+        FIXME("Unimplemented flags: %08x\n", dwFlags);
 
     /* fix up flags */
     if (dwFlags & URL_ESCAPE_SPACES_ONLY)
@@ -1053,7 +1104,7 @@ HRESULT WINAPI UrlEscapeW(
             if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
 
             if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
-                next[0] = L'%';
+                next[0] = '%';
                 next[1] = hexDigits[(cur >> 4) & 0xf];
                 next[2] = hexDigits[cur & 0xf];
                 len = 3;
@@ -1115,16 +1166,18 @@ HRESULT WINAPI UrlUnescapeA(
     DWORD needed;
     BOOL stop_unescaping = FALSE;
 
-    TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
+    TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
          pcchUnescaped, dwFlags);
 
-    if(!pszUrl || !pszUnescaped || !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 &&
@@ -1182,23 +1235,25 @@ HRESULT WINAPI UrlUnescapeW(
     DWORD needed;
     BOOL stop_unescaping = FALSE;
 
-    TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
+    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 &&
-          (*src == L'#' || *src == L'?')) {
+           (*src == '#' || *src == '?')) {
            stop_unescaping = TRUE;
            next = *src;
-       } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
+        } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
                  && stop_unescaping == FALSE) {
            INT ih;
            WCHAR buf[5] = {'0','x',0};
@@ -1215,7 +1270,7 @@ HRESULT WINAPI UrlUnescapeW(
     }
 
     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
-        *dst = L'\0';
+        *dst = '\0';
        ret = S_OK;
     } else {
         needed++; /* add one for the '\0' */
@@ -1294,7 +1349,7 @@ LPCWSTR WINAPI UrlGetLocationW(
     if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
 
     /* Look for '#' and return its addr */
-    return strchrW(base.pszSuffix, L'#');
+    return strchrW(base.pszSuffix, '#');
 }
 
 /*************************************************************************
@@ -1376,13 +1431,12 @@ INT WINAPI UrlCompareW(
  *  Success: TRUE. lpDest is filled with the computed hash value.
  *  Failure: FALSE, if any argument is invalid.
  */
-HRESULT WINAPI HashData(LPBYTE lpSrc, DWORD nSrcLen,
-                     LPBYTE lpDest, DWORD nDestLen)
+HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
+                     unsigned char *lpDest, DWORD nDestLen)
 {
   INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
 
-  if (IsBadReadPtr(lpSrc, nSrcLen) ||
-      IsBadWritePtr(lpDest, nDestLen))
+  if (!lpSrc || !lpDest)
     return E_INVALIDARG;
 
   while (destCount >= 0)
@@ -1423,7 +1477,7 @@ HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
   if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
     return E_INVALIDARG;
 
-  HashData((LPSTR)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
+  HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
   return S_OK;
 }
 
@@ -1436,7 +1490,7 @@ HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
 {
   char szUrl[MAX_PATH];
 
-  TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
+  TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
 
   if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
     return E_INVALIDARG;
@@ -1445,7 +1499,7 @@ HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
    * return the same digests for the same URL.
    */
   WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
-  HashData((BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
+  HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
   return S_OK;
 }
 
@@ -1467,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("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
+            pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
 
-    TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
-         debugstr_a(pszIn), *pcchOut, 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;
 }
@@ -1542,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] = L'@';
-    value[1] = L'\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);
@@ -1584,11 +1646,13 @@ HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DW
     DWORD res1;
     HRESULT ret;
 
-    TRACE("(in %s, out size %ld, flags %08lx)\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(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
+       FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
              debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
        strcpyW(pszOut, pszIn);
        *pcchOut = strlenW(pszOut);
@@ -1601,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;
        }
     }
@@ -1615,7 +1679,7 @@ HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DW
            }
            strcpyW(pszOut, pszIn);
            *pcchOut = strlenW(pszOut);
-           TRACE("valid scheme, returing copy\n");
+           TRACE("valid scheme, returning copy\n");
            return S_OK;
        }
     }
@@ -1630,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;
 }
 
@@ -1824,9 +1880,9 @@ static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
        while (cont) {
            if ( (islowerW(*start) && isalphaW(*start)) ||
                 isdigitW(*start) ||
-                (*start == L'+') ||
-                (*start == L'-') ||
-                (*start == L'.')) {
+                 (*start == '+') ||
+                 (*start == '-') ||
+                 (*start == '.')) {
                start++;
                (*size)++;
            }
@@ -1840,26 +1896,27 @@ static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
            if ( isalphaW(*start) ||
                 isdigitW(*start) ||
                 /* user/password only characters */
-                (*start == L';') ||
-                (*start == L'?') ||
-                (*start == L'&') ||
-                (*start == L'=') ||
+                 (*start == ';') ||
+                 (*start == '?') ||
+                 (*start == '&') ||
+                 (*start == '=') ||
                 /* *extra* characters */
-                (*start == L'!') ||
-                (*start == L'*') ||
-                (*start == L'\'') ||
-                (*start == L'(') ||
-                (*start == L')') ||
-                (*start == L',') ||
+                 (*start == '!') ||
+                 (*start == '*') ||
+                 (*start == '\'') ||
+                 (*start == '(') ||
+                 (*start == ')') ||
+                 (*start == ',') ||
                 /* *safe* characters */
-                (*start == L'$') ||
-                (*start == L'_') ||
-                (*start == L'+') ||
-                (*start == L'-') ||
-                (*start == L'.')) {
+                 (*start == '$') ||
+                 (*start == '_') ||
+                 (*start == '+') ||
+                 (*start == '-') ||
+                 (*start == '.') ||
+                 (*start == ' ')) {
                start++;
                (*size)++;
-           } else if (*start == L'%') {
+            } else if (*start == '%') {
                if (isxdigitW(*(start+1)) &&
                    isxdigitW(*(start+2))) {
                    start += 3;
@@ -1885,8 +1942,9 @@ static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
     case HOST:
        while (cont) {
            if (isalnumW(*start) ||
-               (*start == L'-') ||
-               (*start == L'.') ) {
+                (*start == '-') ||
+                (*start == '.') ||
+                (*start == ' ') ) {
                start++;
                (*size)++;
            }
@@ -1898,7 +1956,7 @@ static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
        FIXME("unknown type %d\n", type);
        return (LPWSTR)&alwayszero;
     }
-    /* TRACE("scanned %ld characters next char %p<%c>\n",
+    /* TRACE("scanned %d characters next char %p<%c>\n",
      *size, start, *start); */
     return start;
 }
@@ -1913,28 +1971,28 @@ static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
     memset(pl, 0, sizeof(WINE_PARSE_URL));
     pl->pScheme = pszUrl;
     work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
-    if (!*work || (*work != L':')) goto ErrorExit;
+    if (!*work || (*work != ':')) goto ErrorExit;
     work++;
-    if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
+    if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
     pl->pUserName = work + 2;
     work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
-    if (*work == L':' ) {
+    if (*work == ':' ) {
        /* parse password */
        work++;
        pl->pPassword = work;
        work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
-       if (*work != L'@') {
+        if (*work != '@') {
            /* what we just parsed must be the hostname and port
             * so reset pointers and clear then let it parse */
            pl->szUserName = pl->szPassword = 0;
            work = pl->pUserName - 1;
            pl->pUserName = pl->pPassword = 0;
        }
-    } else if (*work == L'@') {
+    } else if (*work == '@') {
        /* no password */
        pl->szPassword = 0;
        pl->pPassword = 0;
-    } else if (!*work || (*work == L'/') || (*work == L'.')) {
+    } else if (!*work || (*work == '/') || (*work == '.')) {
        /* what was parsed was hostname, so reset pointers and let it parse */
        pl->szUserName = pl->szPassword = 0;
        work = pl->pUserName - 1;
@@ -1945,18 +2003,19 @@ static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
     work++;
     pl->pHostName = work;
     work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
-    if (*work == L':') {
+    if (*work == ':') {
        /* parse port */
        work++;
        pl->pPort = work;
        work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
     }
-    if (*work == L'/') {
+    if (*work == '/') {
        /* see if query string */
-       pl->pQuery = strchrW(work, L'?');
+        pl->pQuery = strchrW(work, '?');
        if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
     }
-    TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
+  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,
          pl->pPassword, pl->szPassword,
@@ -1978,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"
@@ -1993,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;
@@ -2002,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;
 }
 
 /*************************************************************************
@@ -2029,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(%ld) %08lx %08lx)\n",
+    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));
@@ -2098,8 +2212,8 @@ HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
             pszOut[size] = 0;
            *pcchOut = size;
        }
-       TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
-    }
+       TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
+
     return ret;
 }
 
@@ -2118,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);
 }
 
 /*************************************************************************
@@ -2136,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);
 }
 
 /*************************************************************************
@@ -2205,7 +2323,7 @@ HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUr
     WCHAR three_slashesW[] = {'/','/','/',0};
     PARSEDURLW parsed_url;
 
-    TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
+    TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
 
     /* Validate arguments */
     if (dwReserved != 0)
@@ -2255,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;
 }
 
@@ -2273,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.
  */
@@ -2312,7 +2430,7 @@ HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
   HRESULT hRet = E_FAIL;
 
-  TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
+  TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
         debugstr_w(lpszRes), lpszDest, dwDestLen);
 
   if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
@@ -2346,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;
           }
@@ -2357,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;
+}