| /* |
| * Url functions |
| * |
| * Copyright 2000 Huw D M Davies for CodeWeavers. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| #include <stdarg.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winnls.h" |
| #include "winerror.h" |
| #include "wine/unicode.h" |
| #include "wininet.h" |
| #include "winreg.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); |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(shell); |
| |
| /* The following schemes were identified in the native version of |
| * SHLWAPI.DLL version 5.50 |
| */ |
| static const struct { |
| URL_SCHEME scheme_number; |
| 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 { |
| LPCWSTR pScheme; /* [out] start of scheme */ |
| DWORD szScheme; /* [out] size of scheme (until colon) */ |
| LPCWSTR pUserName; /* [out] start of Username */ |
| DWORD szUserName; /* [out] size of Username (until ":" or "@") */ |
| LPCWSTR pPassword; /* [out] start of Password */ |
| DWORD szPassword; /* [out] size of Password (until "@") */ |
| LPCWSTR pHostName; /* [out] start of Hostname */ |
| DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */ |
| LPCWSTR pPort; /* [out] start of Port */ |
| DWORD szPort; /* [out] size of Port (until "/" or eos) */ |
| LPCWSTR pQuery; /* [out] start of Query */ |
| DWORD szQuery; /* [out] size of Query (until eos) */ |
| } WINE_PARSE_URL; |
| |
| typedef enum { |
| SCHEME, |
| HOST, |
| PORT, |
| USERPASS, |
| } WINE_URL_SCAN_TYPE; |
| |
| static const CHAR hexDigits[] = "0123456789ABCDEF"; |
| |
| static const WCHAR fileW[] = {'f','i','l','e','\0'}; |
| |
| static const unsigned char HashDataLookup[256] = { |
| 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B, |
| 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07, |
| 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, |
| 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46, |
| 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0, |
| 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, |
| 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F, |
| 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9, |
| 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF, |
| 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40, |
| 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, |
| 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4, |
| 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB, |
| 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, |
| 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D, |
| 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47, |
| 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD, |
| 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17, |
| 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, |
| 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 }; |
| |
| static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len) |
| { |
| 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 URL_SCHEME_UNKNOWN; |
| } |
| |
| /************************************************************************* |
| * @ [SHLWAPI.1] |
| * |
| * Parse a Url into its constituent parts. |
| * |
| * PARAMS |
| * x [I] Url to parse |
| * y [O] Undocumented structure holding the parsed information |
| * |
| * RETURNS |
| * Success: S_OK. y contains the parsed Url details. |
| * Failure: An HRESULT error code. |
| */ |
| HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y) |
| { |
| WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH]; |
| const char *ptr = x; |
| int len; |
| |
| TRACE("%s %p\n", debugstr_a(x), y); |
| |
| if(y->cbSize != sizeof(*y)) |
| return E_INVALIDARG; |
| |
| while(*ptr && (isalnum(*ptr) || *ptr == '-')) |
| ptr++; |
| |
| 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; |
| } |
| |
| /************************************************************************* |
| * @ [SHLWAPI.2] |
| * |
| * Unicode version of ParseURLA. |
| */ |
| HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y) |
| { |
| const WCHAR *ptr = x; |
| |
| TRACE("%s %p\n", debugstr_w(x), y); |
| |
| if(y->cbSize != sizeof(*y)) |
| return E_INVALIDARG; |
| |
| while(*ptr && (isalnumW(*ptr) || *ptr == '-')) |
| ptr++; |
| |
| 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 = strlenW(y->pszSuffix); |
| y->nScheme = get_scheme_code(x, ptr-x); |
| |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * UrlCanonicalizeA [SHLWAPI.@] |
| * |
| * Canonicalize a Url. |
| * |
| * PARAMS |
| * pszUrl [I] Url to cCanonicalize |
| * pszCanonicalized [O] Destination for converted Url. |
| * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized |
| * dwFlags [I] Flags controlling the conversion. |
| * |
| * RETURNS |
| * Success: S_OK. The pszCanonicalized contains the converted Url. |
| * Failure: E_POINTER, if *pcchCanonicalized is too small. |
| * |
| * MSDN incorrectly describes the flags for this function. They should be: |
| *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000 |
| *| URL_ESCAPE_SPACES_ONLY 0x04000000 |
| *| URL_ESCAPE_PERCENT 0x00001000 |
| *| URL_ESCAPE_UNSAFE 0x10000000 |
| *| URL_UNESCAPE 0x10000000 |
| *| URL_DONT_SIMPLIFY 0x08000000 |
| *| URL_ESCAPE_SEGMENT_ONLY 0x00002000 |
| */ |
| HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized, |
| LPDWORD pcchCanonicalized, DWORD dwFlags) |
| { |
| LPWSTR url, canonical; |
| HRESULT ret; |
| DWORD len; |
| |
| TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized, |
| pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1); |
| |
| if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized) |
| return E_INVALIDARG; |
| |
| len = strlen(pszUrl)+1; |
| url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); |
| canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR)); |
| if(!url || !canonical) { |
| HeapFree(GetProcessHeap(), 0, url); |
| HeapFree(GetProcessHeap(), 0, canonical); |
| return E_OUTOFMEMORY; |
| } |
| |
| MultiByteToWideChar(0, 0, pszUrl, -1, url, len); |
| |
| ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags); |
| if(ret == S_OK) |
| WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, |
| *pcchCanonicalized+1, 0, 0); |
| |
| HeapFree(GetProcessHeap(), 0, canonical); |
| return ret; |
| } |
| |
| /************************************************************************* |
| * UrlCanonicalizeW [SHLWAPI.@] |
| * |
| * See UrlCanonicalizeA. |
| */ |
| HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized, |
| LPDWORD pcchCanonicalized, DWORD dwFlags) |
| { |
| HRESULT hr = S_OK; |
| DWORD EscapeFlags; |
| LPCWSTR wk1, root; |
| LPWSTR lpszUrlCpy, wk2, mp, mp2; |
| 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'}; |
| static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'}; |
| |
| TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized, |
| pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1); |
| |
| if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized) |
| return E_INVALIDARG; |
| |
| if(!*pszUrl) { |
| *pszCanonicalized = 0; |
| return S_OK; |
| } |
| |
| nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */ |
| /* Allocate memory for simplified URL (before escaping) */ |
| lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, |
| nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR)); |
| |
| if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile) |
| && !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 |
| * 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 = pszUrl; |
| wk2 = lpszUrlCpy; |
| state = 0; |
| |
| if(pszUrl[1] == ':') { /* Assume path */ |
| 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 != '-') && (*wk1 != '.') && (*wk1 != ':')) |
| {state = 3; break;} |
| while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':')) |
| *wk2++ = *wk1++; |
| state = 5; |
| if (!*wk1) { |
| if(slash) |
| *wk2++ = slash; |
| 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; |
| } |
| nLen = mp - wk1; |
| if(nLen) { |
| memcpy(wk2, wk1, nLen * sizeof(WCHAR)); |
| wk2 += nLen; |
| wk1 += nLen; |
| } |
| if(slash) |
| *wk2++ = slash; |
| 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] <= ' '))) |
| lpszUrlCpy[--nLen]=0; |
| |
| if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL)) |
| UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE); |
| |
| if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE | |
| URL_ESCAPE_SPACES_ONLY | |
| URL_ESCAPE_PERCENT | |
| URL_DONT_ESCAPE_EXTRA_INFO | |
| URL_ESCAPE_SEGMENT_ONLY ))) { |
| EscapeFlags &= ~URL_ESCAPE_UNSAFE; |
| hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized, |
| EscapeFlags); |
| } else { /* No escaping needed, just copy the string */ |
| nLen = lstrlenW(lpszUrlCpy); |
| if(nLen < *pcchCanonicalized) |
| memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR)); |
| else { |
| hr = E_POINTER; |
| nLen++; |
| } |
| *pcchCanonicalized = nLen; |
| } |
| |
| HeapFree(GetProcessHeap(), 0, lpszUrlCpy); |
| |
| if (hr == S_OK) |
| TRACE("result %s\n", debugstr_w(pszCanonicalized)); |
| |
| return hr; |
| } |
| |
| /************************************************************************* |
| * UrlCombineA [SHLWAPI.@] |
| * |
| * Combine two Urls. |
| * |
| * PARAMS |
| * pszBase [I] Base Url |
| * pszRelative [I] Url to combine with pszBase |
| * pszCombined [O] Destination for combined Url |
| * pcchCombined [O] Destination for length of pszCombined |
| * dwFlags [I] URL_ flags from "shlwapi.h" |
| * |
| * RETURNS |
| * Success: S_OK. pszCombined contains the combined Url, pcchCombined |
| * contains its length. |
| * Failure: An HRESULT error code indicating the error. |
| */ |
| HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative, |
| LPSTR pszCombined, LPDWORD pcchCombined, |
| DWORD dwFlags) |
| { |
| LPWSTR base, relative, combined; |
| DWORD ret, len, len2; |
| |
| TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n", |
| debugstr_a(pszBase),debugstr_a(pszRelative), |
| pcchCombined?*pcchCombined:0,dwFlags); |
| |
| if(!pszBase || !pszRelative || !pcchCombined) |
| return E_INVALIDARG; |
| |
| base = HeapAlloc(GetProcessHeap(), 0, |
| (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); |
| relative = base + INTERNET_MAX_URL_LENGTH; |
| combined = relative + INTERNET_MAX_URL_LENGTH; |
| |
| MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH); |
| MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH); |
| len = *pcchCombined; |
| |
| ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags); |
| if (ret != S_OK) { |
| *pcchCombined = len; |
| HeapFree(GetProcessHeap(), 0, base); |
| return ret; |
| } |
| |
| len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0); |
| if (len2 > *pcchCombined) { |
| *pcchCombined = len2; |
| HeapFree(GetProcessHeap(), 0, base); |
| return E_POINTER; |
| } |
| WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1, |
| 0, 0); |
| *pcchCombined = len2; |
| HeapFree(GetProcessHeap(), 0, base); |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * UrlCombineW [SHLWAPI.@] |
| * |
| * See UrlCombineA. |
| */ |
| HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative, |
| LPWSTR pszCombined, LPDWORD pcchCombined, |
| DWORD dwFlags) |
| { |
| PARSEDURLW base, relative; |
| DWORD myflags, sizeloc = 0; |
| DWORD len, res1, res2, process_case = 0; |
| LPWSTR work, preliminary, mbase, mrelative; |
| static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'}; |
| HRESULT ret; |
| |
| TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n", |
| debugstr_w(pszBase),debugstr_w(pszRelative), |
| pcchCombined?*pcchCombined:0,dwFlags); |
| |
| if(!pszBase || !pszRelative || !pcchCombined) |
| return E_INVALIDARG; |
| |
| base.cbSize = sizeof(base); |
| relative.cbSize = sizeof(relative); |
| |
| /* Get space for duplicates of the input and the output */ |
| preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) * |
| sizeof(WCHAR)); |
| mbase = preliminary + INTERNET_MAX_URL_LENGTH; |
| mrelative = mbase + INTERNET_MAX_URL_LENGTH; |
| *preliminary = '\0'; |
| |
| /* Canonicalize the base input prior to looking for the scheme */ |
| myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE); |
| len = INTERNET_MAX_URL_LENGTH; |
| ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags); |
| |
| /* Canonicalize the relative input prior to looking for the scheme */ |
| len = INTERNET_MAX_URL_LENGTH; |
| ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags); |
| |
| /* See if the base has a scheme */ |
| res1 = ParseURLW(mbase, &base); |
| if (res1) { |
| /* if pszBase has no scheme, then return pszRelative */ |
| TRACE("no scheme detected in Base\n"); |
| 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}; |
| |
| 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 preceding 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; |
| } |
| } |
| |
| /* |
| * At this point: |
| * .pszSuffix points to location (starting with '//') |
| * .cchSuffix length of location (above) and rest less the last |
| * leaf (if any) |
| * sizeloc length of location (above) up to but not including |
| * the last '/' |
| */ |
| |
| res2 = ParseURLW(mrelative, &relative); |
| if (res2) { |
| /* no scheme in pszRelative */ |
| TRACE("no scheme detected in Relative\n"); |
| relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */ |
| relative.cchSuffix = strlenW(mrelative); |
| if (*pszRelative == ':') { |
| /* case that is either left alone or uses pszBase */ |
| if (dwFlags & URL_PLUGGABLE_PROTOCOL) { |
| process_case = 5; |
| break; |
| } |
| process_case = 1; |
| break; |
| } |
| if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) { |
| /* case that becomes "file:///" */ |
| strcpyW(preliminary, myfilestr); |
| process_case = 1; |
| break; |
| } |
| if ((*mrelative == '/') && (*(mrelative+1) == '/')) { |
| /* pszRelative has location and rest */ |
| process_case = 3; |
| break; |
| } |
| if (*mrelative == '/') { |
| /* case where pszRelative is root to location */ |
| process_case = 4; |
| break; |
| } |
| process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3; |
| break; |
| } |
| |
| /* handle cases where pszRelative has scheme */ |
| if ((base.cchProtocol == relative.cchProtocol) && |
| (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) { |
| |
| /* since the schemes are the same */ |
| if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) { |
| /* case where pszRelative replaces location and following */ |
| process_case = 3; |
| break; |
| } |
| if (*relative.pszSuffix == '/') { |
| /* case where pszRelative is root to location */ |
| process_case = 4; |
| break; |
| } |
| /* 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 == '/') && (*(relative.pszSuffix+1) == '/')) { |
| /* case where pszRelative replaces scheme, location, |
| * and following and handles PLUGGABLE |
| */ |
| process_case = 2; |
| break; |
| } |
| process_case = 1; |
| break; |
| } while(FALSE); /* a little trick to allow easy exit from nested if's */ |
| |
| ret = S_OK; |
| switch (process_case) { |
| |
| case 1: /* |
| * Return pszRelative appended to what ever is in pszCombined, |
| * (which may the string "file:///" |
| */ |
| strcatW(preliminary, mrelative); |
| break; |
| |
| case 2: /* case where pszRelative replaces scheme, and location */ |
| strcpyW(preliminary, mrelative); |
| break; |
| |
| case 3: /* |
| * Return the pszBase scheme with pszRelative. Basically |
| * keeps the scheme and replaces the domain and following. |
| */ |
| memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR)); |
| work = preliminary + base.cchProtocol + 1; |
| strcpyW(work, relative.pszSuffix); |
| break; |
| |
| case 4: /* |
| * Return the pszBase scheme and location but everything |
| * after the location is pszRelative. (Replace document |
| * from root on.) |
| */ |
| memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR)); |
| work = preliminary + base.cchProtocol + 1 + sizeloc; |
| if (dwFlags & URL_PLUGGABLE_PROTOCOL) |
| *(work++) = '/'; |
| strcpyW(work, relative.pszSuffix); |
| break; |
| |
| case 5: /* |
| * Return the pszBase without its document (if any) and |
| * append pszRelative after its scheme. |
| */ |
| memcpy(preliminary, base.pszProtocol, |
| (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR)); |
| work = preliminary + base.cchProtocol+1+base.cchSuffix - 1; |
| if (*work++ != '/') |
| *(work++) = '/'; |
| strcpyW(work, relative.pszSuffix); |
| break; |
| |
| default: |
| 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 */ |
| if(*pcchCombined == 0) |
| *pcchCombined = 1; |
| ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL)); |
| if(SUCCEEDED(ret) && pszCombined) { |
| lstrcpyW(pszCombined, mrelative); |
| } |
| TRACE("return-%d len=%d, %s\n", |
| process_case, *pcchCombined, debugstr_w(pszCombined)); |
| } |
| HeapFree(GetProcessHeap(), 0, preliminary); |
| return ret; |
| } |
| |
| /************************************************************************* |
| * UrlEscapeA [SHLWAPI.@] |
| */ |
| |
| HRESULT WINAPI UrlEscapeA( |
| LPCSTR pszUrl, |
| LPSTR pszEscaped, |
| LPDWORD pcchEscaped, |
| DWORD dwFlags) |
| { |
| WCHAR bufW[INTERNET_MAX_URL_LENGTH]; |
| WCHAR *escapedW = bufW; |
| UNICODE_STRING urlW; |
| 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) { |
| escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); |
| ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags); |
| } |
| if(ret == S_OK) { |
| RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR)); |
| if(*pcchEscaped > lenA) { |
| RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR)); |
| pszEscaped[lenA] = 0; |
| *pcchEscaped = lenA; |
| } else { |
| *pcchEscaped = lenA + 1; |
| ret = E_POINTER; |
| } |
| } |
| if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW); |
| RtlFreeUnicodeString(&urlW); |
| return ret; |
| } |
| |
| #define WINE_URL_BASH_AS_SLASH 0x01 |
| #define WINE_URL_COLLAPSE_SLASHES 0x02 |
| #define WINE_URL_ESCAPE_SLASH 0x04 |
| #define WINE_URL_ESCAPE_HASH 0x08 |
| #define WINE_URL_ESCAPE_QUESTION 0x10 |
| #define WINE_URL_STOP_ON_HASH 0x20 |
| #define WINE_URL_STOP_ON_QUESTION 0x40 |
| |
| static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags) |
| { |
| |
| if (isalnumW(ch)) |
| return FALSE; |
| |
| if(dwFlags & URL_ESCAPE_SPACES_ONLY) { |
| if(ch == ' ') |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%')) |
| return TRUE; |
| |
| if (ch <= 31 || ch >= 127) |
| return TRUE; |
| |
| else { |
| switch (ch) { |
| case ' ': |
| case '<': |
| case '>': |
| case '\"': |
| case '{': |
| case '}': |
| case '|': |
| case '\\': |
| case '^': |
| case ']': |
| case '[': |
| case '`': |
| case '&': |
| return TRUE; |
| |
| case '/': |
| if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE; |
| return FALSE; |
| |
| case '?': |
| if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE; |
| return FALSE; |
| |
| case '#': |
| if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE; |
| return FALSE; |
| |
| default: |
| return FALSE; |
| } |
| } |
| } |
| |
| |
| /************************************************************************* |
| * UrlEscapeW [SHLWAPI.@] |
| * |
| * Converts unsafe characters in a Url into escape sequences. |
| * |
| * PARAMS |
| * pszUrl [I] Url to modify |
| * pszEscaped [O] Destination for modified Url |
| * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped |
| * dwFlags [I] URL_ flags from "shlwapi.h" |
| * |
| * RETURNS |
| * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped |
| * contains its length. |
| * Failure: E_POINTER, if pszEscaped is not large enough. In this case |
| * pcchEscaped is set to the required length. |
| * |
| * Converts unsafe characters into their escape sequences. |
| * |
| * NOTES |
| * - By default this function stops converting at the first '?' or |
| * '#' character. |
| * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are |
| * converted, but the conversion continues past a '?' or '#'. |
| * - Note that this function did not work well (or at all) in shlwapi version 4. |
| * |
| * BUGS |
| * Only the following flags are implemented: |
| *| URL_ESCAPE_SPACES_ONLY |
| *| URL_DONT_ESCAPE_EXTRA_INFO |
| *| URL_ESCAPE_SEGMENT_ONLY |
| *| URL_ESCAPE_PERCENT |
| */ |
| HRESULT WINAPI UrlEscapeW( |
| LPCWSTR pszUrl, |
| LPWSTR pszEscaped, |
| LPDWORD pcchEscaped, |
| DWORD dwFlags) |
| { |
| LPCWSTR src; |
| DWORD needed = 0, ret; |
| BOOL stop_escaping = FALSE; |
| WCHAR next[5], *dst = pszEscaped; |
| INT len; |
| PARSEDURLW parsed_url; |
| DWORD int_flags; |
| DWORD slashes = 0; |
| static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0}; |
| |
| TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped, |
| pcchEscaped, dwFlags); |
| |
| 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: %08x\n", dwFlags); |
| |
| /* fix up flags */ |
| if (dwFlags & URL_ESCAPE_SPACES_ONLY) |
| /* if SPACES_ONLY specified, reset the other controls */ |
| dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO | |
| URL_ESCAPE_PERCENT | |
| URL_ESCAPE_SEGMENT_ONLY); |
| |
| else |
| /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */ |
| dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO; |
| |
| |
| int_flags = 0; |
| if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) { |
| int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH; |
| } else { |
| parsed_url.cbSize = sizeof(parsed_url); |
| if(ParseURLW(pszUrl, &parsed_url) != S_OK) |
| parsed_url.nScheme = URL_SCHEME_INVALID; |
| |
| TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol)); |
| |
| if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) |
| int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION; |
| |
| switch(parsed_url.nScheme) { |
| case URL_SCHEME_FILE: |
| int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH; |
| int_flags &= ~WINE_URL_STOP_ON_HASH; |
| break; |
| |
| case URL_SCHEME_HTTP: |
| case URL_SCHEME_HTTPS: |
| int_flags |= WINE_URL_BASH_AS_SLASH; |
| if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\') |
| int_flags |= WINE_URL_ESCAPE_SLASH; |
| break; |
| |
| case URL_SCHEME_MAILTO: |
| int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH; |
| int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH); |
| break; |
| |
| case URL_SCHEME_INVALID: |
| break; |
| |
| case URL_SCHEME_FTP: |
| default: |
| if(parsed_url.pszSuffix[0] != '/') |
| int_flags |= WINE_URL_ESCAPE_SLASH; |
| break; |
| } |
| } |
| |
| for(src = pszUrl; *src; ) { |
| WCHAR cur = *src; |
| len = 0; |
| |
| if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) { |
| int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1; |
| while(cur == '/' || cur == '\\') { |
| slashes++; |
| cur = *++src; |
| } |
| if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */ |
| if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\') |
| src += localhost_len + 1; |
| slashes = 3; |
| } |
| |
| switch(slashes) { |
| case 1: |
| case 3: |
| next[0] = next[1] = next[2] = '/'; |
| len = 3; |
| break; |
| case 0: |
| len = 0; |
| break; |
| default: |
| next[0] = next[1] = '/'; |
| len = 2; |
| break; |
| } |
| } |
| if(len == 0) { |
| |
| if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH)) |
| stop_escaping = TRUE; |
| |
| if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION)) |
| stop_escaping = TRUE; |
| |
| if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/'; |
| |
| if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) { |
| next[0] = '%'; |
| next[1] = hexDigits[(cur >> 4) & 0xf]; |
| next[2] = hexDigits[cur & 0xf]; |
| len = 3; |
| } else { |
| next[0] = cur; |
| len = 1; |
| } |
| src++; |
| } |
| |
| if(needed + len <= *pcchEscaped) { |
| memcpy(dst, next, len*sizeof(WCHAR)); |
| dst += len; |
| } |
| needed += len; |
| } |
| |
| if(needed < *pcchEscaped) { |
| *dst = '\0'; |
| ret = S_OK; |
| } else { |
| needed++; /* add one for the '\0' */ |
| ret = E_POINTER; |
| } |
| *pcchEscaped = needed; |
| return ret; |
| } |
| |
| |
| /************************************************************************* |
| * UrlUnescapeA [SHLWAPI.@] |
| * |
| * Converts Url escape sequences back to ordinary characters. |
| * |
| * PARAMS |
| * pszUrl [I/O] Url to convert |
| * pszUnescaped [O] Destination for converted Url |
| * pcchUnescaped [I/O] Size of output string |
| * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h" |
| * |
| * RETURNS |
| * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if |
| * dwFlags includes URL_ESCAPE_INPLACE. |
| * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In |
| * this case pcchUnescaped is set to the size required. |
| * NOTES |
| * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at |
| * the first occurrence of either a '?' or '#' character. |
| */ |
| HRESULT WINAPI UrlUnescapeA( |
| LPSTR pszUrl, |
| LPSTR pszUnescaped, |
| LPDWORD pcchUnescaped, |
| DWORD dwFlags) |
| { |
| char *dst, next; |
| LPCSTR src; |
| HRESULT ret; |
| DWORD needed; |
| BOOL stop_unescaping = FALSE; |
| |
| TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped, |
| pcchUnescaped, dwFlags); |
| |
| 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 == '#' || *src == '?')) { |
| stop_unescaping = TRUE; |
| next = *src; |
| } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) |
| && stop_unescaping == FALSE) { |
| INT ih; |
| char buf[3]; |
| memcpy(buf, src + 1, 2); |
| buf[2] = '\0'; |
| ih = strtol(buf, NULL, 16); |
| next = (CHAR) ih; |
| src += 2; /* Advance to end of escape */ |
| } else |
| next = *src; |
| |
| if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) |
| *dst++ = next; |
| } |
| |
| if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) { |
| *dst = '\0'; |
| ret = S_OK; |
| } else { |
| needed++; /* add one for the '\0' */ |
| ret = E_POINTER; |
| } |
| if(!(dwFlags & URL_UNESCAPE_INPLACE)) |
| *pcchUnescaped = needed; |
| |
| if (ret == S_OK) { |
| TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ? |
| debugstr_a(pszUrl) : debugstr_a(pszUnescaped)); |
| } |
| |
| return ret; |
| } |
| |
| /************************************************************************* |
| * UrlUnescapeW [SHLWAPI.@] |
| * |
| * See UrlUnescapeA. |
| */ |
| HRESULT WINAPI UrlUnescapeW( |
| LPWSTR pszUrl, |
| LPWSTR pszUnescaped, |
| LPDWORD pcchUnescaped, |
| DWORD dwFlags) |
| { |
| WCHAR *dst, next; |
| LPCWSTR src; |
| HRESULT ret; |
| DWORD needed; |
| BOOL stop_unescaping = FALSE; |
| |
| TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped, |
| pcchUnescaped, dwFlags); |
| |
| 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 == '#' || *src == '?')) { |
| stop_unescaping = TRUE; |
| next = *src; |
| } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2)) |
| && stop_unescaping == FALSE) { |
| INT ih; |
| WCHAR buf[5] = {'0','x',0}; |
| memcpy(buf + 2, src + 1, 2*sizeof(WCHAR)); |
| buf[4] = 0; |
| StrToIntExW(buf, STIF_SUPPORT_HEX, &ih); |
| next = (WCHAR) ih; |
| src += 2; /* Advance to end of escape */ |
| } else |
| next = *src; |
| |
| if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) |
| *dst++ = next; |
| } |
| |
| if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) { |
| *dst = '\0'; |
| ret = S_OK; |
| } else { |
| needed++; /* add one for the '\0' */ |
| ret = E_POINTER; |
| } |
| if(!(dwFlags & URL_UNESCAPE_INPLACE)) |
| *pcchUnescaped = needed; |
| |
| if (ret == S_OK) { |
| TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ? |
| debugstr_w(pszUrl) : debugstr_w(pszUnescaped)); |
| } |
| |
| return ret; |
| } |
| |
| /************************************************************************* |
| * UrlGetLocationA [SHLWAPI.@] |
| * |
| * Get the location from a Url. |
| * |
| * PARAMS |
| * pszUrl [I] Url to get the location from |
| * |
| * RETURNS |
| * A pointer to the start of the location in pszUrl, or NULL if there is |
| * no location. |
| * |
| * NOTES |
| * - MSDN erroneously states that "The location is the segment of the Url |
| * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll |
| * stop at '?' and always return a NULL in this case. |
| * - MSDN also erroneously states that "If a file URL has a query string, |
| * the returned string is the query string". In all tested cases, if the |
| * Url starts with "fi" then a NULL is returned. V5 gives the following results: |
| *| Result Url |
| *| ------ --- |
| *| NULL file://aa/b/cd#hohoh |
| *| #hohoh http://aa/b/cd#hohoh |
| *| NULL fi://aa/b/cd#hohoh |
| *| #hohoh ff://aa/b/cd#hohoh |
| */ |
| LPCSTR WINAPI UrlGetLocationA( |
| LPCSTR pszUrl) |
| { |
| PARSEDURLA base; |
| DWORD res1; |
| |
| base.cbSize = sizeof(base); |
| res1 = ParseURLA(pszUrl, &base); |
| if (res1) return NULL; /* invalid scheme */ |
| |
| /* if scheme is file: then never return pointer */ |
| if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL; |
| |
| /* Look for '#' and return its addr */ |
| return strchr(base.pszSuffix, '#'); |
| } |
| |
| /************************************************************************* |
| * UrlGetLocationW [SHLWAPI.@] |
| * |
| * See UrlGetLocationA. |
| */ |
| LPCWSTR WINAPI UrlGetLocationW( |
| LPCWSTR pszUrl) |
| { |
| PARSEDURLW base; |
| DWORD res1; |
| |
| base.cbSize = sizeof(base); |
| res1 = ParseURLW(pszUrl, &base); |
| if (res1) return NULL; /* invalid scheme */ |
| |
| /* if scheme is file: then never return pointer */ |
| if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL; |
| |
| /* Look for '#' and return its addr */ |
| return strchrW(base.pszSuffix, '#'); |
| } |
| |
| /************************************************************************* |
| * UrlCompareA [SHLWAPI.@] |
| * |
| * Compare two Urls. |
| * |
| * PARAMS |
| * pszUrl1 [I] First Url to compare |
| * pszUrl2 [I] Url to compare to pszUrl1 |
| * fIgnoreSlash [I] TRUE = compare only up to a final slash |
| * |
| * RETURNS |
| * less than zero, zero, or greater than zero indicating pszUrl2 is greater |
| * than, equal to, or less than pszUrl1 respectively. |
| */ |
| INT WINAPI UrlCompareA( |
| LPCSTR pszUrl1, |
| LPCSTR pszUrl2, |
| BOOL fIgnoreSlash) |
| { |
| INT ret, len, len1, len2; |
| |
| if (!fIgnoreSlash) |
| return strcmp(pszUrl1, pszUrl2); |
| len1 = strlen(pszUrl1); |
| if (pszUrl1[len1-1] == '/') len1--; |
| len2 = strlen(pszUrl2); |
| if (pszUrl2[len2-1] == '/') len2--; |
| if (len1 == len2) |
| return strncmp(pszUrl1, pszUrl2, len1); |
| len = min(len1, len2); |
| ret = strncmp(pszUrl1, pszUrl2, len); |
| if (ret) return ret; |
| if (len1 > len2) return 1; |
| return -1; |
| } |
| |
| /************************************************************************* |
| * UrlCompareW [SHLWAPI.@] |
| * |
| * See UrlCompareA. |
| */ |
| INT WINAPI UrlCompareW( |
| LPCWSTR pszUrl1, |
| LPCWSTR pszUrl2, |
| BOOL fIgnoreSlash) |
| { |
| INT ret; |
| size_t len, len1, len2; |
| |
| if (!fIgnoreSlash) |
| return strcmpW(pszUrl1, pszUrl2); |
| len1 = strlenW(pszUrl1); |
| if (pszUrl1[len1-1] == '/') len1--; |
| len2 = strlenW(pszUrl2); |
| if (pszUrl2[len2-1] == '/') len2--; |
| if (len1 == len2) |
| return strncmpW(pszUrl1, pszUrl2, len1); |
| len = min(len1, len2); |
| ret = strncmpW(pszUrl1, pszUrl2, len); |
| if (ret) return ret; |
| if (len1 > len2) return 1; |
| return -1; |
| } |
| |
| /************************************************************************* |
| * HashData [SHLWAPI.@] |
| * |
| * Hash an input block into a variable sized digest. |
| * |
| * PARAMS |
| * lpSrc [I] Input block |
| * nSrcLen [I] Length of lpSrc |
| * lpDest [I] Output for hash digest |
| * nDestLen [I] Length of lpDest |
| * |
| * RETURNS |
| * Success: TRUE. lpDest is filled with the computed hash value. |
| * Failure: FALSE, if any argument is invalid. |
| */ |
| HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen, |
| unsigned char *lpDest, DWORD nDestLen) |
| { |
| INT srcCount = nSrcLen - 1, destCount = nDestLen - 1; |
| |
| if (!lpSrc || !lpDest) |
| return E_INVALIDARG; |
| |
| while (destCount >= 0) |
| { |
| lpDest[destCount] = (destCount & 0xff); |
| destCount--; |
| } |
| |
| while (srcCount >= 0) |
| { |
| destCount = nDestLen - 1; |
| while (destCount >= 0) |
| { |
| lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]]; |
| destCount--; |
| } |
| srcCount--; |
| } |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * UrlHashA [SHLWAPI.@] |
| * |
| * Produce a Hash from a Url. |
| * |
| * PARAMS |
| * pszUrl [I] Url to hash |
| * lpDest [O] Destinationh for hash |
| * nDestLen [I] Length of lpDest |
| * |
| * RETURNS |
| * Success: S_OK. lpDest is filled with the computed hash value. |
| * Failure: E_INVALIDARG, if any argument is invalid. |
| */ |
| HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen) |
| { |
| if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen)) |
| return E_INVALIDARG; |
| |
| HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen); |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * UrlHashW [SHLWAPI.@] |
| * |
| * See UrlHashA. |
| */ |
| HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen) |
| { |
| char szUrl[MAX_PATH]; |
| |
| TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen); |
| |
| if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen)) |
| return E_INVALIDARG; |
| |
| /* Win32 hashes the data as an ASCII string, presumably so that both A+W |
| * return the same digests for the same URL. |
| */ |
| WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0); |
| HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen); |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * UrlApplySchemeA [SHLWAPI.@] |
| * |
| * Apply a scheme to a Url. |
| * |
| * PARAMS |
| * pszIn [I] Url to apply scheme to |
| * pszOut [O] Destination for modified Url |
| * pcchOut [I/O] Length of pszOut/destination for length of pszOut |
| * dwFlags [I] URL_ flags from "shlwapi.h" |
| * |
| * RETURNS |
| * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length. |
| * Failure: An HRESULT error code describing the error. |
| */ |
| HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags) |
| { |
| LPWSTR in, out; |
| HRESULT ret; |
| DWORD len; |
| |
| 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)); |
| out = 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) { |
| HeapFree(GetProcessHeap(), 0, in); |
| return ret; |
| } |
| |
| len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL); |
| if (len > *pcchOut) { |
| ret = E_POINTER; |
| goto cleanup; |
| } |
| |
| WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL); |
| len--; |
| |
| cleanup: |
| *pcchOut = len; |
| HeapFree(GetProcessHeap(), 0, in); |
| return ret; |
| } |
| |
| static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut) |
| { |
| HKEY newkey; |
| BOOL j; |
| INT index; |
| DWORD value_len, data_len, dwType, i; |
| WCHAR reg_path[MAX_PATH]; |
| WCHAR value[MAX_PATH], data[MAX_PATH]; |
| WCHAR Wxx, Wyy; |
| |
| MultiByteToWideChar(0, 0, |
| "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", |
| -1, reg_path, MAX_PATH); |
| RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey); |
| index = 0; |
| while(value_len = data_len = MAX_PATH, |
| RegEnumValueW(newkey, index, value, &value_len, |
| 0, &dwType, (LPVOID)data, &data_len) == 0) { |
| TRACE("guess %d %s is %s\n", |
| index, debugstr_w(value), debugstr_w(data)); |
| |
| j = FALSE; |
| for(i=0; i<value_len; i++) { |
| Wxx = pszIn[i]; |
| Wyy = value[i]; |
| /* remember that TRUE is not-equal */ |
| j = ChrCmpIW(Wxx, Wyy); |
| if (j) break; |
| } |
| if ((i == value_len) && !j) { |
| if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) { |
| *pcchOut = strlenW(data) + strlenW(pszIn) + 1; |
| RegCloseKey(newkey); |
| return E_POINTER; |
| } |
| strcpyW(pszOut, data); |
| strcatW(pszOut, pszIn); |
| *pcchOut = strlenW(pszOut); |
| TRACE("matched and set to %s\n", debugstr_w(pszOut)); |
| RegCloseKey(newkey); |
| return S_OK; |
| } |
| index++; |
| } |
| RegCloseKey(newkey); |
| return E_FAIL; |
| } |
| |
| static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut) |
| { |
| HKEY newkey; |
| DWORD data_len, dwType; |
| 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 */ |
| 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; |
| } |
| strcpyW(pszOut, data); |
| strcatW(pszOut, pszIn); |
| *pcchOut = strlenW(pszOut); |
| TRACE("used default %s\n", debugstr_w(pszOut)); |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * UrlApplySchemeW [SHLWAPI.@] |
| * |
| * See UrlApplySchemeA. |
| */ |
| HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags) |
| { |
| PARSEDURLW in_scheme; |
| DWORD res1; |
| HRESULT ret; |
| |
| 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", |
| debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags); |
| strcpyW(pszOut, pszIn); |
| *pcchOut = strlenW(pszOut); |
| return S_FALSE; |
| } |
| |
| in_scheme.cbSize = sizeof(in_scheme); |
| /* See if the base has a scheme */ |
| res1 = ParseURLW(pszIn, &in_scheme); |
| 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)) != E_FAIL) |
| return ret; |
| } |
| } |
| else { |
| /* we have a scheme, see if valid (known scheme) */ |
| if (in_scheme.nScheme) { |
| /* have valid scheme, so just copy and exit */ |
| if (strlenW(pszIn) + 1 > *pcchOut) { |
| *pcchOut = strlenW(pszIn) + 1; |
| return E_POINTER; |
| } |
| strcpyW(pszOut, pszIn); |
| *pcchOut = strlenW(pszOut); |
| TRACE("valid scheme, returning copy\n"); |
| return S_OK; |
| } |
| } |
| |
| /* If we are here, then either invalid scheme, |
| * or no scheme and can't/failed guess. |
| */ |
| if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) || |
| ((res1 != 0)) ) && |
| (dwFlags & URL_APPLY_DEFAULT)) { |
| /* find and apply default scheme */ |
| return URL_ApplyDefault(pszIn, pszOut, pcchOut); |
| } |
| |
| return S_FALSE; |
| } |
| |
| /************************************************************************* |
| * UrlIsA [SHLWAPI.@] |
| * |
| * Determine if a Url is of a certain class. |
| * |
| * PARAMS |
| * pszUrl [I] Url to check |
| * Urlis [I] URLIS_ constant from "shlwapi.h" |
| * |
| * RETURNS |
| * TRUE if pszUrl belongs to the class type in Urlis. |
| * FALSE Otherwise. |
| */ |
| BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis) |
| { |
| PARSEDURLA base; |
| DWORD res1; |
| LPCSTR last; |
| |
| TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis); |
| |
| if(!pszUrl) |
| return FALSE; |
| |
| switch (Urlis) { |
| |
| case URLIS_OPAQUE: |
| base.cbSize = sizeof(base); |
| res1 = ParseURLA(pszUrl, &base); |
| if (res1) return FALSE; /* invalid scheme */ |
| switch (base.nScheme) |
| { |
| case URL_SCHEME_MAILTO: |
| case URL_SCHEME_SHELL: |
| case URL_SCHEME_JAVASCRIPT: |
| case URL_SCHEME_VBSCRIPT: |
| case URL_SCHEME_ABOUT: |
| return TRUE; |
| } |
| return FALSE; |
| |
| case URLIS_FILEURL: |
| return !StrCmpNA("file:", pszUrl, 5); |
| |
| case URLIS_DIRECTORY: |
| last = pszUrl + strlen(pszUrl) - 1; |
| return (last >= pszUrl && (*last == '/' || *last == '\\' )); |
| |
| case URLIS_URL: |
| return PathIsURLA(pszUrl); |
| |
| case URLIS_NOHISTORY: |
| case URLIS_APPLIABLE: |
| case URLIS_HASQUERY: |
| default: |
| FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis); |
| } |
| return FALSE; |
| } |
| |
| /************************************************************************* |
| * UrlIsW [SHLWAPI.@] |
| * |
| * See UrlIsA. |
| */ |
| BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis) |
| { |
| static const WCHAR stemp[] = { 'f','i','l','e',':',0 }; |
| PARSEDURLW base; |
| DWORD res1; |
| LPCWSTR last; |
| |
| TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis); |
| |
| if(!pszUrl) |
| return FALSE; |
| |
| switch (Urlis) { |
| |
| case URLIS_OPAQUE: |
| base.cbSize = sizeof(base); |
| res1 = ParseURLW(pszUrl, &base); |
| if (res1) return FALSE; /* invalid scheme */ |
| switch (base.nScheme) |
| { |
| case URL_SCHEME_MAILTO: |
| case URL_SCHEME_SHELL: |
| case URL_SCHEME_JAVASCRIPT: |
| case URL_SCHEME_VBSCRIPT: |
| case URL_SCHEME_ABOUT: |
| return TRUE; |
| } |
| return FALSE; |
| |
| case URLIS_FILEURL: |
| return !strncmpW(stemp, pszUrl, 5); |
| |
| case URLIS_DIRECTORY: |
| last = pszUrl + strlenW(pszUrl) - 1; |
| return (last >= pszUrl && (*last == '/' || *last == '\\')); |
| |
| case URLIS_URL: |
| return PathIsURLW(pszUrl); |
| |
| case URLIS_NOHISTORY: |
| case URLIS_APPLIABLE: |
| case URLIS_HASQUERY: |
| default: |
| FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis); |
| } |
| return FALSE; |
| } |
| |
| /************************************************************************* |
| * UrlIsNoHistoryA [SHLWAPI.@] |
| * |
| * Determine if a Url should not be stored in the users history list. |
| * |
| * PARAMS |
| * pszUrl [I] Url to check |
| * |
| * RETURNS |
| * TRUE, if pszUrl should be excluded from the history list, |
| * FALSE otherwise. |
| */ |
| BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl) |
| { |
| return UrlIsA(pszUrl, URLIS_NOHISTORY); |
| } |
| |
| /************************************************************************* |
| * UrlIsNoHistoryW [SHLWAPI.@] |
| * |
| * See UrlIsNoHistoryA. |
| */ |
| BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl) |
| { |
| return UrlIsW(pszUrl, URLIS_NOHISTORY); |
| } |
| |
| /************************************************************************* |
| * UrlIsOpaqueA [SHLWAPI.@] |
| * |
| * Determine if a Url is opaque. |
| * |
| * PARAMS |
| * pszUrl [I] Url to check |
| * |
| * RETURNS |
| * TRUE if pszUrl is opaque, |
| * FALSE Otherwise. |
| * |
| * NOTES |
| * An opaque Url is one that does not start with "<protocol>://". |
| */ |
| BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl) |
| { |
| return UrlIsA(pszUrl, URLIS_OPAQUE); |
| } |
| |
| /************************************************************************* |
| * UrlIsOpaqueW [SHLWAPI.@] |
| * |
| * See UrlIsOpaqueA. |
| */ |
| BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl) |
| { |
| return UrlIsW(pszUrl, URLIS_OPAQUE); |
| } |
| |
| /************************************************************************* |
| * Scans for characters of type "type" and when not matching found, |
| * returns pointer to it and length in size. |
| * |
| * Characters tested based on RFC 1738 |
| */ |
| static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type) |
| { |
| static DWORD alwayszero = 0; |
| BOOL cont = TRUE; |
| |
| *size = 0; |
| |
| switch(type){ |
| |
| case SCHEME: |
| while (cont) { |
| if ( (islowerW(*start) && isalphaW(*start)) || |
| isdigitW(*start) || |
| (*start == '+') || |
| (*start == '-') || |
| (*start == '.')) { |
| start++; |
| (*size)++; |
| } |
| else |
| cont = FALSE; |
| } |
| break; |
| |
| case USERPASS: |
| while (cont) { |
| if ( isalphaW(*start) || |
| isdigitW(*start) || |
| /* user/password only characters */ |
| (*start == ';') || |
| (*start == '?') || |
| (*start == '&') || |
| (*start == '=') || |
| /* *extra* characters */ |
| (*start == '!') || |
| (*start == '*') || |
| (*start == '\'') || |
| (*start == '(') || |
| (*start == ')') || |
| (*start == ',') || |
| /* *safe* characters */ |
| (*start == '$') || |
| (*start == '_') || |
| (*start == '+') || |
| (*start == '-') || |
| (*start == '.') || |
| (*start == ' ')) { |
| start++; |
| (*size)++; |
| } else if (*start == '%') { |
| if (isxdigitW(*(start+1)) && |
| isxdigitW(*(start+2))) { |
| start += 3; |
| *size += 3; |
| } else |
| cont = FALSE; |
| } else |
| cont = FALSE; |
| } |
| break; |
| |
| case PORT: |
| while (cont) { |
| if (isdigitW(*start)) { |
| start++; |
| (*size)++; |
| } |
| else |
| cont = FALSE; |
| } |
| break; |
| |
| case HOST: |
| while (cont) { |
| if (isalnumW(*start) || |
| (*start == '-') || |
| (*start == '.') || |
| (*start == ' ') ) { |
| start++; |
| (*size)++; |
| } |
| else |
| cont = FALSE; |
| } |
| break; |
| default: |
| FIXME("unknown type %d\n", type); |
| return (LPWSTR)&alwayszero; |
| } |
| /* TRACE("scanned %d characters next char %p<%c>\n", |
| *size, start, *start); */ |
| return start; |
| } |
| |
| /************************************************************************* |
| * Attempt to parse URL into pieces. |
| */ |
| static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl) |
| { |
| LPCWSTR work; |
| |
| memset(pl, 0, sizeof(WINE_PARSE_URL)); |
| pl->pScheme = pszUrl; |
| work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME); |
| if (!*work || (*work != ':')) goto ErrorExit; |
| work++; |
| if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit; |
| pl->pUserName = work + 2; |
| work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS); |
| if (*work == ':' ) { |
| /* parse password */ |
| work++; |
| pl->pPassword = work; |
| work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS); |
| 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 == '@') { |
| /* no password */ |
| pl->szPassword = 0; |
| pl->pPassword = 0; |
| } 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; |
| pl->pUserName = pl->pPassword = 0; |
| } else goto ErrorExit; |
| |
| /* now start parsing hostname or hostnumber */ |
| work++; |
| pl->pHostName = work; |
| work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST); |
| if (*work == ':') { |
| /* parse port */ |
| work++; |
| pl->pPort = work; |
| work = URL_ScanID(pl->pPort, &pl->szPort, PORT); |
| } |
| if (*work == '/') { |
| /* see if query string */ |
| 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, |
| pl->pPassword, pl->szPassword, |
| pl->pHostName, pl->szHostName, |
| pl->pPort, pl->szPort, |
| pl->pQuery, pl->szQuery); |
| return S_OK; |
| ErrorExit: |
| FIXME("failed to parse %s\n", debugstr_w(pszUrl)); |
| return E_INVALIDARG; |
| } |
| |
| /************************************************************************* |
| * UrlGetPartA [SHLWAPI.@] |
| * |
| * Retrieve part of a Url. |
| * |
| * PARAMS |
| * pszIn [I] Url to parse |
| * pszOut [O] Destination for part of pszIn requested |
| * pcchOut [I] Size of pszOut |
| * [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" |
| * |
| * RETURNS |
| * Success: S_OK. pszOut contains the part requested, pcchOut contains its length. |
| * Failure: An HRESULT error code describing the error. |
| */ |
| HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, |
| DWORD dwPart, DWORD dwFlags) |
| { |
| 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; |
| |
| MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH); |
| |
| len = INTERNET_MAX_URL_LENGTH; |
| ret = UrlGetPartW(in, out, &len, dwPart, dwFlags); |
| |
| if (FAILED(ret)) { |
| HeapFree(GetProcessHeap(), 0, in); |
| return ret; |
| } |
| |
| len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0); |
| if (len2 > *pcchOut) { |
| *pcchOut = len2+1; |
| HeapFree(GetProcessHeap(), 0, in); |
| return E_POINTER; |
| } |
| len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0); |
| *pcchOut = len2-1; |
| HeapFree(GetProcessHeap(), 0, in); |
| return ret; |
| } |
| |
| /************************************************************************* |
| * UrlGetPartW [SHLWAPI.@] |
| * |
| * See UrlGetPartA. |
| */ |
| HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, |
| DWORD dwPart, DWORD dwFlags) |
| { |
| WINE_PARSE_URL pl; |
| HRESULT ret; |
| 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); |
| |
| switch (dwPart) { |
| case URL_PART_SCHEME: |
| if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) { |
| *pcchOut = 0; |
| return S_FALSE; |
| } |
| addr = pl.pScheme; |
| size = pl.szScheme; |
| break; |
| |
| case URL_PART_HOSTNAME: |
| 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) { |
| *pcchOut = 0; |
| return S_FALSE; |
| } |
| addr = pl.pUserName; |
| size = pl.szUserName; |
| break; |
| |
| case URL_PART_PASSWORD: |
| if (!pl.szPassword) { |
| *pcchOut = 0; |
| return S_FALSE; |
| } |
| addr = pl.pPassword; |
| size = pl.szPassword; |
| break; |
| |
| case URL_PART_PORT: |
| if (!pl.szPort) { |
| *pcchOut = 0; |
| return S_FALSE; |
| } |
| addr = pl.pPort; |
| size = pl.szPort; |
| break; |
| |
| case URL_PART_QUERY: |
| 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; |
| } |
| memcpy(pszOut, schaddr, schsize*sizeof(WCHAR)); |
| pszOut[schsize] = ':'; |
| memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR)); |
| pszOut[schsize+1+size] = 0; |
| *pcchOut = schsize + 1 + size; |
| } |
| else { |
| if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;} |
| memcpy(pszOut, addr, size*sizeof(WCHAR)); |
| pszOut[size] = 0; |
| *pcchOut = size; |
| } |
| TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut)); |
| |
| return ret; |
| } |
| |
| /************************************************************************* |
| * PathIsURLA [SHLWAPI.@] |
| * |
| * Check if the given path is a Url. |
| * |
| * PARAMS |
| * lpszPath [I] Path to check. |
| * |
| * RETURNS |
| * TRUE if lpszPath is a Url. |
| * FALSE if lpszPath is NULL or not a Url. |
| */ |
| BOOL WINAPI PathIsURLA(LPCSTR lpstrPath) |
| { |
| PARSEDURLA base; |
| HRESULT hres; |
| |
| TRACE("%s\n", debugstr_a(lpstrPath)); |
| |
| if (!lpstrPath || !*lpstrPath) return FALSE; |
| |
| /* get protocol */ |
| base.cbSize = sizeof(base); |
| hres = ParseURLA(lpstrPath, &base); |
| return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID); |
| } |
| |
| /************************************************************************* |
| * PathIsURLW [SHLWAPI.@] |
| * |
| * See PathIsURLA. |
| */ |
| BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath) |
| { |
| PARSEDURLW base; |
| HRESULT hres; |
| |
| TRACE("%s\n", debugstr_w(lpstrPath)); |
| |
| if (!lpstrPath || !*lpstrPath) return FALSE; |
| |
| /* get protocol */ |
| base.cbSize = sizeof(base); |
| hres = ParseURLW(lpstrPath, &base); |
| return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID); |
| } |
| |
| /************************************************************************* |
| * UrlCreateFromPathA [SHLWAPI.@] |
| * |
| * See UrlCreateFromPathW |
| */ |
| HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved) |
| { |
| WCHAR bufW[INTERNET_MAX_URL_LENGTH]; |
| WCHAR *urlW = bufW; |
| UNICODE_STRING pathW; |
| HRESULT ret; |
| DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA; |
| |
| if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath)) |
| return E_INVALIDARG; |
| if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) { |
| urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); |
| ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved); |
| } |
| if(ret == S_OK || ret == S_FALSE) { |
| RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR)); |
| if(*pcchUrl > lenA) { |
| RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR)); |
| pszUrl[lenA] = 0; |
| *pcchUrl = lenA; |
| } else { |
| *pcchUrl = lenA + 1; |
| ret = E_POINTER; |
| } |
| } |
| if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW); |
| RtlFreeUnicodeString(&pathW); |
| return ret; |
| } |
| |
| /************************************************************************* |
| * UrlCreateFromPathW [SHLWAPI.@] |
| * |
| * Create a Url from a file path. |
| * |
| * PARAMS |
| * pszPath [I] Path to convert |
| * pszUrl [O] Destination for the converted Url |
| * pcchUrl [I/O] Length of pszUrl |
| * dwReserved [I] Reserved, must be 0 |
| * |
| * RETURNS |
| * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url |
| * Failure: An HRESULT error code. |
| */ |
| HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved) |
| { |
| DWORD needed; |
| HRESULT ret; |
| WCHAR *pszNewUrl; |
| WCHAR file_colonW[] = {'f','i','l','e',':',0}; |
| WCHAR three_slashesW[] = {'/','/','/',0}; |
| PARSEDURLW parsed_url; |
| |
| TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved); |
| |
| /* Validate arguments */ |
| if (dwReserved != 0) |
| return E_INVALIDARG; |
| if (!pszUrl || !pcchUrl) |
| return E_INVALIDARG; |
| |
| |
| parsed_url.cbSize = sizeof(parsed_url); |
| if(ParseURLW(pszPath, &parsed_url) == S_OK) { |
| if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) { |
| needed = strlenW(pszPath); |
| if (needed >= *pcchUrl) { |
| *pcchUrl = needed + 1; |
| return E_POINTER; |
| } else { |
| *pcchUrl = needed; |
| strcpyW(pszUrl, pszPath); |
| return S_FALSE; |
| } |
| } |
| } |
| |
| pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */ |
| strcpyW(pszNewUrl, file_colonW); |
| if(isalphaW(pszPath[0]) && pszPath[1] == ':') |
| strcatW(pszNewUrl, three_slashesW); |
| strcatW(pszNewUrl, pszPath); |
| ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT); |
| |
| HeapFree(GetProcessHeap(), 0, pszNewUrl); |
| return ret; |
| } |
| |
| /************************************************************************* |
| * SHAutoComplete [SHLWAPI.@] |
| * |
| * Enable auto-completion for an edit control. |
| * |
| * PARAMS |
| * hwndEdit [I] Handle of control to enable auto-completion for |
| * dwFlags [I] SHACF_ flags from "shlwapi.h" |
| * |
| * RETURNS |
| * Success: S_OK. Auto-completion is enabled for the control. |
| * Failure: An HRESULT error code indicating the error. |
| */ |
| HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags) |
| { |
| FIXME("stub\n"); |
| return S_FALSE; |
| } |
| |
| /************************************************************************* |
| * MLBuildResURLA [SHLWAPI.405] |
| * |
| * Create a Url pointing to a resource in a module. |
| * |
| * PARAMS |
| * lpszLibName [I] Name of the module containing the resource |
| * hMod [I] Callers module handle |
| * dwFlags [I] Undocumented flags for loading the module |
| * lpszRes [I] Resource name |
| * lpszDest [O] Destination for resulting Url |
| * dwDestLen [I] Length of lpszDest |
| * |
| * RETURNS |
| * Success: S_OK. lpszDest contains the resource Url. |
| * Failure: E_INVALIDARG, if any argument is invalid, or |
| * E_FAIL if dwDestLen is too small. |
| */ |
| HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags, |
| LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen) |
| { |
| WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH]; |
| HRESULT hRet; |
| |
| if (lpszLibName) |
| MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR)); |
| |
| if (lpszRes) |
| MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR)); |
| |
| if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR)) |
| dwDestLen = sizeof(szLibName)/sizeof(WCHAR); |
| |
| hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags, |
| lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen); |
| if (SUCCEEDED(hRet) && lpszDest) |
| WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0); |
| |
| return hRet; |
| } |
| |
| /************************************************************************* |
| * MLBuildResURLA [SHLWAPI.406] |
| * |
| * See MLBuildResURLA. |
| */ |
| HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags, |
| LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen) |
| { |
| static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' }; |
| #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR)) |
| HRESULT hRet = E_FAIL; |
| |
| 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 || |
| !lpszDest || (dwFlags && dwFlags != 2)) |
| return E_INVALIDARG; |
| |
| if (dwDestLen >= szResLen + 1) |
| { |
| dwDestLen -= (szResLen + 1); |
| memcpy(lpszDest, szRes, sizeof(szRes)); |
| |
| hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags); |
| |
| if (hMod) |
| { |
| WCHAR szBuff[MAX_PATH]; |
| DWORD len; |
| |
| len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR)); |
| if (len && len < sizeof(szBuff)/sizeof(WCHAR)) |
| { |
| DWORD dwPathLen = strlenW(szBuff) + 1; |
| |
| if (dwDestLen >= dwPathLen) |
| { |
| DWORD dwResLen; |
| |
| dwDestLen -= dwPathLen; |
| memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR)); |
| |
| dwResLen = strlenW(lpszRes) + 1; |
| if (dwDestLen >= dwResLen + 1) |
| { |
| lpszDest[szResLen + dwPathLen-1] = '/'; |
| memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR)); |
| hRet = S_OK; |
| } |
| } |
| } |
| MLFreeLibrary(hMod); |
| } |
| } |
| 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; |
| } |