url.c 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322
  1. /*
  2. * Url functions
  3. *
  4. * Copyright 2000 Huw D M Davies for CodeWeavers.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #include "config.h"
  21. #include "wine/port.h"
  22. #include <stdarg.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include "windef.h"
  26. #include "winbase.h"
  27. #include "winnls.h"
  28. #include "winerror.h"
  29. #include "wine/unicode.h"
  30. #include "wininet.h"
  31. #include "winreg.h"
  32. #include "winternl.h"
  33. #define NO_SHLWAPI_STREAM
  34. #include "shlwapi.h"
  35. #include "wine/debug.h"
  36. HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
  37. BOOL WINAPI MLFreeLibrary(HMODULE);
  38. HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
  39. WINE_DEFAULT_DEBUG_CHANNEL(shell);
  40. /* The following schemes were identified in the native version of
  41. * SHLWAPI.DLL version 5.50
  42. */
  43. typedef struct {
  44. URL_SCHEME scheme_number;
  45. LPCSTR scheme_name;
  46. } SHL_2_inet_scheme;
  47. static const SHL_2_inet_scheme shlwapi_schemes[] = {
  48. {URL_SCHEME_FTP, "ftp"},
  49. {URL_SCHEME_HTTP, "http"},
  50. {URL_SCHEME_GOPHER, "gopher"},
  51. {URL_SCHEME_MAILTO, "mailto"},
  52. {URL_SCHEME_NEWS, "news"},
  53. {URL_SCHEME_NNTP, "nntp"},
  54. {URL_SCHEME_TELNET, "telnet"},
  55. {URL_SCHEME_WAIS, "wais"},
  56. {URL_SCHEME_FILE, "file"},
  57. {URL_SCHEME_MK, "mk"},
  58. {URL_SCHEME_HTTPS, "https"},
  59. {URL_SCHEME_SHELL, "shell"},
  60. {URL_SCHEME_SNEWS, "snews"},
  61. {URL_SCHEME_LOCAL, "local"},
  62. {URL_SCHEME_JAVASCRIPT, "javascript"},
  63. {URL_SCHEME_VBSCRIPT, "vbscript"},
  64. {URL_SCHEME_ABOUT, "about"},
  65. {URL_SCHEME_RES, "res"},
  66. {0, 0}
  67. };
  68. typedef struct {
  69. LPCWSTR pScheme; /* [out] start of scheme */
  70. DWORD szScheme; /* [out] size of scheme (until colon) */
  71. LPCWSTR pUserName; /* [out] start of Username */
  72. DWORD szUserName; /* [out] size of Username (until ":" or "@") */
  73. LPCWSTR pPassword; /* [out] start of Password */
  74. DWORD szPassword; /* [out] size of Password (until "@") */
  75. LPCWSTR pHostName; /* [out] start of Hostname */
  76. DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
  77. LPCWSTR pPort; /* [out] start of Port */
  78. DWORD szPort; /* [out] size of Port (until "/" or eos) */
  79. LPCWSTR pQuery; /* [out] start of Query */
  80. DWORD szQuery; /* [out] size of Query (until eos) */
  81. } WINE_PARSE_URL;
  82. typedef enum {
  83. SCHEME,
  84. HOST,
  85. PORT,
  86. USERPASS,
  87. } WINE_URL_SCAN_TYPE;
  88. static const CHAR hexDigits[] = "0123456789ABCDEF";
  89. static const WCHAR fileW[] = {'f','i','l','e','\0'};
  90. static const unsigned char HashDataLookup[256] = {
  91. 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
  92. 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
  93. 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
  94. 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
  95. 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
  96. 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
  97. 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
  98. 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
  99. 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
  100. 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
  101. 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
  102. 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
  103. 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
  104. 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
  105. 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
  106. 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
  107. 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
  108. 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
  109. 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
  110. 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
  111. static BOOL URL_JustLocation(LPCWSTR str)
  112. {
  113. while(*str && (*str == L'/')) str++;
  114. if (*str) {
  115. while (*str && ((*str == L'-') ||
  116. (*str == L'.') ||
  117. isalnumW(*str))) str++;
  118. if (*str == L'/') return FALSE;
  119. }
  120. return TRUE;
  121. }
  122. /*************************************************************************
  123. * @ [SHLWAPI.1]
  124. *
  125. * Parse a Url into its constituent parts.
  126. *
  127. * PARAMS
  128. * x [I] Url to parse
  129. * y [O] Undocumented structure holding the parsed information
  130. *
  131. * RETURNS
  132. * Success: S_OK. y contains the parsed Url details.
  133. * Failure: An HRESULT error code.
  134. */
  135. HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
  136. {
  137. DWORD cnt;
  138. const SHL_2_inet_scheme *inet_pro;
  139. y->nScheme = URL_SCHEME_INVALID;
  140. if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
  141. /* FIXME: leading white space generates error of 0x80041001 which
  142. * is undefined
  143. */
  144. if (*x <= ' ') return 0x80041001;
  145. cnt = 0;
  146. y->cchProtocol = 0;
  147. y->pszProtocol = x;
  148. while (*x) {
  149. if (*x == ':') {
  150. y->cchProtocol = cnt;
  151. cnt = -1;
  152. y->pszSuffix = x+1;
  153. break;
  154. }
  155. x++;
  156. cnt++;
  157. }
  158. /* check for no scheme in string start */
  159. /* (apparently schemes *must* be larger than a single character) */
  160. if ((*x == '\0') || (y->cchProtocol <= 1)) {
  161. y->pszProtocol = NULL;
  162. return 0x80041001;
  163. }
  164. /* found scheme, set length of remainder */
  165. y->cchSuffix = lstrlenA(y->pszSuffix);
  166. /* see if known scheme and return indicator number */
  167. y->nScheme = URL_SCHEME_UNKNOWN;
  168. inet_pro = shlwapi_schemes;
  169. while (inet_pro->scheme_name) {
  170. if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
  171. min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
  172. y->nScheme = inet_pro->scheme_number;
  173. break;
  174. }
  175. inet_pro++;
  176. }
  177. return S_OK;
  178. }
  179. /*************************************************************************
  180. * @ [SHLWAPI.2]
  181. *
  182. * Unicode version of ParseURLA.
  183. */
  184. HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
  185. {
  186. DWORD cnt;
  187. const SHL_2_inet_scheme *inet_pro;
  188. LPSTR cmpstr;
  189. INT len;
  190. y->nScheme = URL_SCHEME_INVALID;
  191. if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
  192. /* FIXME: leading white space generates error of 0x80041001 which
  193. * is undefined
  194. */
  195. if (*x <= L' ') return 0x80041001;
  196. cnt = 0;
  197. y->cchProtocol = 0;
  198. y->pszProtocol = x;
  199. while (*x) {
  200. if (*x == L':') {
  201. y->cchProtocol = cnt;
  202. cnt = -1;
  203. y->pszSuffix = x+1;
  204. break;
  205. }
  206. x++;
  207. cnt++;
  208. }
  209. /* check for no scheme in string start */
  210. /* (apparently schemes *must* be larger than a single character) */
  211. if ((*x == L'\0') || (y->cchProtocol <= 1)) {
  212. y->pszProtocol = NULL;
  213. return 0x80041001;
  214. }
  215. /* found scheme, set length of remainder */
  216. y->cchSuffix = lstrlenW(y->pszSuffix);
  217. /* see if known scheme and return indicator number */
  218. len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
  219. cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len);
  220. WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, cmpstr, len, 0, 0);
  221. y->nScheme = URL_SCHEME_UNKNOWN;
  222. inet_pro = shlwapi_schemes;
  223. while (inet_pro->scheme_name) {
  224. if (!strncasecmp(inet_pro->scheme_name, cmpstr,
  225. min(len, lstrlenA(inet_pro->scheme_name)))) {
  226. y->nScheme = inet_pro->scheme_number;
  227. break;
  228. }
  229. inet_pro++;
  230. }
  231. HeapFree(GetProcessHeap(), 0, cmpstr);
  232. return S_OK;
  233. }
  234. /*************************************************************************
  235. * UrlCanonicalizeA [SHLWAPI.@]
  236. *
  237. * Canonicalize a Url.
  238. *
  239. * PARAMS
  240. * pszUrl [I] Url to cCanonicalize
  241. * pszCanonicalized [O] Destination for converted Url.
  242. * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
  243. * dwFlags [I] Flags controlling the conversion.
  244. *
  245. * RETURNS
  246. * Success: S_OK. The pszCanonicalized contains the converted Url.
  247. * Failure: E_POINTER, if *pcchCanonicalized is too small.
  248. *
  249. * MSDN incorrectly describes the flags for this function. They should be:
  250. *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
  251. *| URL_ESCAPE_SPACES_ONLY 0x04000000
  252. *| URL_ESCAPE_PERCENT 0x00001000
  253. *| URL_ESCAPE_UNSAFE 0x10000000
  254. *| URL_UNESCAPE 0x10000000
  255. *| URL_DONT_SIMPLIFY 0x08000000
  256. *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
  257. */
  258. HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
  259. LPDWORD pcchCanonicalized, DWORD dwFlags)
  260. {
  261. LPWSTR base, canonical;
  262. DWORD ret, len, len2;
  263. TRACE("(%s %p %p 0x%08lx) using W version\n",
  264. debugstr_a(pszUrl), pszCanonicalized,
  265. pcchCanonicalized, dwFlags);
  266. if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
  267. return E_INVALIDARG;
  268. base = HeapAlloc(GetProcessHeap(), 0,
  269. (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
  270. canonical = base + INTERNET_MAX_URL_LENGTH;
  271. MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
  272. len = INTERNET_MAX_URL_LENGTH;
  273. ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
  274. if (ret != S_OK) {
  275. HeapFree(GetProcessHeap(), 0, base);
  276. return ret;
  277. }
  278. len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
  279. if (len2 > *pcchCanonicalized) {
  280. *pcchCanonicalized = len;
  281. HeapFree(GetProcessHeap(), 0, base);
  282. return E_POINTER;
  283. }
  284. WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
  285. *pcchCanonicalized, 0, 0);
  286. *pcchCanonicalized = len2;
  287. HeapFree(GetProcessHeap(), 0, base);
  288. return S_OK;
  289. }
  290. /*************************************************************************
  291. * UrlCanonicalizeW [SHLWAPI.@]
  292. *
  293. * See UrlCanonicalizeA.
  294. */
  295. HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
  296. LPDWORD pcchCanonicalized, DWORD dwFlags)
  297. {
  298. HRESULT hr = S_OK;
  299. DWORD EscapeFlags;
  300. LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
  301. INT nByteLen, state;
  302. DWORD nLen;
  303. TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
  304. pcchCanonicalized, dwFlags);
  305. if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
  306. return E_INVALIDARG;
  307. nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
  308. lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
  309. if (dwFlags & URL_DONT_SIMPLIFY)
  310. memcpy(lpszUrlCpy, pszUrl, nByteLen);
  311. else {
  312. /*
  313. * state =
  314. * 0 initial 1,3
  315. * 1 have 2[+] alnum 2,3
  316. * 2 have scheme (found :) 4,6,3
  317. * 3 failed (no location)
  318. * 4 have // 5,3
  319. * 5 have 1[+] alnum 6,3
  320. * 6 have location (found /) save root location
  321. */
  322. wk1 = (LPWSTR)pszUrl;
  323. wk2 = lpszUrlCpy;
  324. state = 0;
  325. while (*wk1) {
  326. switch (state) {
  327. case 0:
  328. if (!isalnumW(*wk1)) {state = 3; break;}
  329. *wk2++ = *wk1++;
  330. if (!isalnumW(*wk1)) {state = 3; break;}
  331. *wk2++ = *wk1++;
  332. state = 1;
  333. break;
  334. case 1:
  335. *wk2++ = *wk1;
  336. if (*wk1++ == L':') state = 2;
  337. break;
  338. case 2:
  339. if (*wk1 != L'/') {state = 3; break;}
  340. *wk2++ = *wk1++;
  341. if (*wk1 != L'/') {state = 6; break;}
  342. *wk2++ = *wk1++;
  343. state = 4;
  344. break;
  345. case 3:
  346. strcpyW(wk2, wk1);
  347. wk1 += strlenW(wk1);
  348. wk2 += strlenW(wk2);
  349. break;
  350. case 4:
  351. if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.')) {state = 3; break;}
  352. while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.')) *wk2++ = *wk1++;
  353. state = 5;
  354. break;
  355. case 5:
  356. if (*wk1 != L'/') {state = 3; break;}
  357. *wk2++ = *wk1++;
  358. state = 6;
  359. break;
  360. case 6:
  361. /* Now at root location, cannot back up any more. */
  362. /* "root" will point at the '/' */
  363. root = wk2-1;
  364. while (*wk1) {
  365. TRACE("wk1=%c\n", (CHAR)*wk1);
  366. mp = strchrW(wk1, L'/');
  367. if (!mp) {
  368. strcpyW(wk2, wk1);
  369. wk1 += strlenW(wk1);
  370. wk2 += strlenW(wk2);
  371. continue;
  372. }
  373. nLen = mp - wk1 + 1;
  374. strncpyW(wk2, wk1, nLen);
  375. wk2 += nLen;
  376. wk1 += nLen;
  377. if (*wk1 == L'.') {
  378. TRACE("found '/.'\n");
  379. if (*(wk1+1) == L'/') {
  380. /* case of /./ -> skip the ./ */
  381. wk1 += 2;
  382. }
  383. else if (*(wk1+1) == L'.') {
  384. /* found /.. look for next / */
  385. TRACE("found '/..'\n");
  386. if (*(wk1+2) == L'/' || *(wk1+2) == L'?' || *(wk1+2) == L'#' || *(wk1+2) == 0) {
  387. /* case /../ -> need to backup wk2 */
  388. TRACE("found '/../'\n");
  389. *(wk2-1) = L'\0'; /* set end of string */
  390. mp = strrchrW(root, L'/');
  391. if (mp && (mp >= root)) {
  392. /* found valid backup point */
  393. wk2 = mp + 1;
  394. if(*(wk1+2) != L'/')
  395. wk1 += 2;
  396. else
  397. wk1 += 3;
  398. }
  399. else {
  400. /* did not find point, restore '/' */
  401. *(wk2-1) = L'/';
  402. }
  403. }
  404. }
  405. }
  406. }
  407. *wk2 = L'\0';
  408. break;
  409. default:
  410. FIXME("how did we get here - state=%d\n", state);
  411. HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
  412. return E_INVALIDARG;
  413. }
  414. }
  415. *wk2 = L'\0';
  416. TRACE("Simplified, orig <%s>, simple <%s>\n",
  417. debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
  418. }
  419. nLen = lstrlenW(lpszUrlCpy);
  420. while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
  421. lpszUrlCpy[--nLen]=0;
  422. if(dwFlags & URL_UNESCAPE)
  423. UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
  424. if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
  425. URL_ESCAPE_SPACES_ONLY |
  426. URL_ESCAPE_PERCENT |
  427. URL_DONT_ESCAPE_EXTRA_INFO |
  428. URL_ESCAPE_SEGMENT_ONLY ))) {
  429. EscapeFlags &= ~URL_ESCAPE_UNSAFE;
  430. hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
  431. EscapeFlags);
  432. } else { /* No escaping needed, just copy the string */
  433. nLen = lstrlenW(lpszUrlCpy);
  434. if(nLen < *pcchCanonicalized)
  435. memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
  436. else {
  437. hr = E_POINTER;
  438. nLen++;
  439. }
  440. *pcchCanonicalized = nLen;
  441. }
  442. HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
  443. if (hr == S_OK)
  444. TRACE("result %s\n", debugstr_w(pszCanonicalized));
  445. return hr;
  446. }
  447. /*************************************************************************
  448. * UrlCombineA [SHLWAPI.@]
  449. *
  450. * Combine two Urls.
  451. *
  452. * PARAMS
  453. * pszBase [I] Base Url
  454. * pszRelative [I] Url to combine with pszBase
  455. * pszCombined [O] Destination for combined Url
  456. * pcchCombined [O] Destination for length of pszCombined
  457. * dwFlags [I] URL_ flags from "shlwapi.h"
  458. *
  459. * RETURNS
  460. * Success: S_OK. pszCombined contains the combined Url, pcchCombined
  461. * contains its length.
  462. * Failure: An HRESULT error code indicating the error.
  463. */
  464. HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
  465. LPSTR pszCombined, LPDWORD pcchCombined,
  466. DWORD dwFlags)
  467. {
  468. LPWSTR base, relative, combined;
  469. DWORD ret, len, len2;
  470. TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
  471. debugstr_a(pszBase),debugstr_a(pszRelative),
  472. pcchCombined?*pcchCombined:0,dwFlags);
  473. if(!pszBase || !pszRelative || !pcchCombined)
  474. return E_INVALIDARG;
  475. base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
  476. (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
  477. relative = base + INTERNET_MAX_URL_LENGTH;
  478. combined = relative + INTERNET_MAX_URL_LENGTH;
  479. MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
  480. MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
  481. len = *pcchCombined;
  482. ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
  483. if (ret != S_OK) {
  484. *pcchCombined = len;
  485. HeapFree(GetProcessHeap(), 0, base);
  486. return ret;
  487. }
  488. len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
  489. if (len2 > *pcchCombined) {
  490. *pcchCombined = len2;
  491. HeapFree(GetProcessHeap(), 0, base);
  492. return E_POINTER;
  493. }
  494. WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
  495. 0, 0);
  496. *pcchCombined = len2;
  497. HeapFree(GetProcessHeap(), 0, base);
  498. return S_OK;
  499. }
  500. /*************************************************************************
  501. * UrlCombineW [SHLWAPI.@]
  502. *
  503. * See UrlCombineA.
  504. */
  505. HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
  506. LPWSTR pszCombined, LPDWORD pcchCombined,
  507. DWORD dwFlags)
  508. {
  509. PARSEDURLW base, relative;
  510. DWORD myflags, sizeloc = 0;
  511. DWORD len, res1, res2, process_case = 0;
  512. LPWSTR work, preliminary, mbase, mrelative;
  513. static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
  514. static const WCHAR single_slash[] = {'/','\0'};
  515. HRESULT ret;
  516. TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
  517. debugstr_w(pszBase),debugstr_w(pszRelative),
  518. pcchCombined?*pcchCombined:0,dwFlags);
  519. if(!pszBase || !pszRelative || !pcchCombined)
  520. return E_INVALIDARG;
  521. base.cbSize = sizeof(base);
  522. relative.cbSize = sizeof(relative);
  523. /* Get space for duplicates of the input and the output */
  524. preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
  525. sizeof(WCHAR));
  526. mbase = preliminary + INTERNET_MAX_URL_LENGTH;
  527. mrelative = mbase + INTERNET_MAX_URL_LENGTH;
  528. *preliminary = L'\0';
  529. /* Canonicalize the base input prior to looking for the scheme */
  530. myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
  531. len = INTERNET_MAX_URL_LENGTH;
  532. ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
  533. /* Canonicalize the relative input prior to looking for the scheme */
  534. len = INTERNET_MAX_URL_LENGTH;
  535. ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
  536. /* See if the base has a scheme */
  537. res1 = ParseURLW(mbase, &base);
  538. if (res1) {
  539. /* if pszBase has no scheme, then return pszRelative */
  540. TRACE("no scheme detected in Base\n");
  541. process_case = 1;
  542. }
  543. else do {
  544. /* get size of location field (if it exists) */
  545. work = (LPWSTR)base.pszSuffix;
  546. sizeloc = 0;
  547. if (*work++ == L'/') {
  548. if (*work++ == L'/') {
  549. /* At this point have start of location and
  550. * it ends at next '/' or end of string.
  551. */
  552. while(*work && (*work != L'/')) work++;
  553. sizeloc = (DWORD)(work - base.pszSuffix);
  554. }
  555. }
  556. /* Change .sizep2 to not have the last leaf in it,
  557. * Note: we need to start after the location (if it exists)
  558. */
  559. work = strrchrW((base.pszSuffix+sizeloc), L'/');
  560. if (work) {
  561. len = (DWORD)(work - base.pszSuffix + 1);
  562. base.cchSuffix = len;
  563. }
  564. /*
  565. * At this point:
  566. * .pszSuffix points to location (starting with '//')
  567. * .cchSuffix length of location (above) and rest less the last
  568. * leaf (if any)
  569. * sizeloc length of location (above) up to but not including
  570. * the last '/'
  571. */
  572. res2 = ParseURLW(mrelative, &relative);
  573. if (res2) {
  574. /* no scheme in pszRelative */
  575. TRACE("no scheme detected in Relative\n");
  576. relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
  577. relative.cchSuffix = strlenW(mrelative);
  578. if (*pszRelative == L':') {
  579. /* case that is either left alone or uses pszBase */
  580. if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
  581. process_case = 5;
  582. break;
  583. }
  584. process_case = 1;
  585. break;
  586. }
  587. if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
  588. /* case that becomes "file:///" */
  589. strcpyW(preliminary, myfilestr);
  590. process_case = 1;
  591. break;
  592. }
  593. if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
  594. /* pszRelative has location and rest */
  595. process_case = 3;
  596. break;
  597. }
  598. if (*mrelative == L'/') {
  599. /* case where pszRelative is root to location */
  600. process_case = 4;
  601. break;
  602. }
  603. process_case = (*base.pszSuffix == L'/') ? 5 : 3;
  604. break;
  605. }
  606. /* handle cases where pszRelative has scheme */
  607. if ((base.cchProtocol == relative.cchProtocol) &&
  608. (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
  609. /* since the schemes are the same */
  610. if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
  611. /* case where pszRelative replaces location and following */
  612. process_case = 3;
  613. break;
  614. }
  615. if (*relative.pszSuffix == L'/') {
  616. /* case where pszRelative is root to location */
  617. process_case = 4;
  618. break;
  619. }
  620. /* case where scheme is followed by document path */
  621. process_case = 5;
  622. break;
  623. }
  624. if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
  625. /* case where pszRelative replaces scheme, location,
  626. * and following and handles PLUGGABLE
  627. */
  628. process_case = 2;
  629. break;
  630. }
  631. process_case = 1;
  632. break;
  633. } while(FALSE); /* a litte trick to allow easy exit from nested if's */
  634. ret = S_OK;
  635. switch (process_case) {
  636. case 1: /*
  637. * Return pszRelative appended to what ever is in pszCombined,
  638. * (which may the string "file:///"
  639. */
  640. strcatW(preliminary, mrelative);
  641. break;
  642. case 2: /*
  643. * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
  644. * and pszRelative starts with "//", then append a "/"
  645. */
  646. strcpyW(preliminary, mrelative);
  647. if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
  648. URL_JustLocation(relative.pszSuffix))
  649. strcatW(preliminary, single_slash);
  650. break;
  651. case 3: /*
  652. * Return the pszBase scheme with pszRelative. Basically
  653. * keeps the scheme and replaces the domain and following.
  654. */
  655. strncpyW(preliminary, base.pszProtocol, base.cchProtocol + 1);
  656. work = preliminary + base.cchProtocol + 1;
  657. strcpyW(work, relative.pszSuffix);
  658. if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
  659. URL_JustLocation(relative.pszSuffix))
  660. strcatW(work, single_slash);
  661. break;
  662. case 4: /*
  663. * Return the pszBase scheme and location but everything
  664. * after the location is pszRelative. (Replace document
  665. * from root on.)
  666. */
  667. strncpyW(preliminary, base.pszProtocol, base.cchProtocol+1+sizeloc);
  668. work = preliminary + base.cchProtocol + 1 + sizeloc;
  669. if (dwFlags & URL_PLUGGABLE_PROTOCOL)
  670. *(work++) = L'/';
  671. strcpyW(work, relative.pszSuffix);
  672. break;
  673. case 5: /*
  674. * Return the pszBase without its document (if any) and
  675. * append pszRelative after its scheme.
  676. */
  677. strncpyW(preliminary, base.pszProtocol, base.cchProtocol+1+base.cchSuffix);
  678. work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
  679. if (*work++ != L'/')
  680. *(work++) = L'/';
  681. strcpyW(work, relative.pszSuffix);
  682. break;
  683. default:
  684. FIXME("How did we get here????? process_case=%ld\n", process_case);
  685. ret = E_INVALIDARG;
  686. }
  687. if (ret == S_OK) {
  688. /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
  689. ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
  690. if(SUCCEEDED(ret) && pszCombined) {
  691. lstrcpyW(pszCombined, mrelative);
  692. }
  693. TRACE("return-%ld len=%ld, %s\n",
  694. process_case, *pcchCombined, debugstr_w(pszCombined));
  695. }
  696. HeapFree(GetProcessHeap(), 0, preliminary);
  697. return ret;
  698. }
  699. /*************************************************************************
  700. * UrlEscapeA [SHLWAPI.@]
  701. */
  702. HRESULT WINAPI UrlEscapeA(
  703. LPCSTR pszUrl,
  704. LPSTR pszEscaped,
  705. LPDWORD pcchEscaped,
  706. DWORD dwFlags)
  707. {
  708. WCHAR bufW[INTERNET_MAX_URL_LENGTH];
  709. WCHAR *escapedW = bufW;
  710. UNICODE_STRING urlW;
  711. HRESULT ret;
  712. DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
  713. if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
  714. return E_INVALIDARG;
  715. if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
  716. escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
  717. ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
  718. }
  719. if(ret == S_OK) {
  720. RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
  721. if(*pcchEscaped > lenA) {
  722. RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
  723. pszEscaped[lenA] = 0;
  724. *pcchEscaped = lenA;
  725. } else {
  726. *pcchEscaped = lenA + 1;
  727. ret = E_POINTER;
  728. }
  729. }
  730. if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
  731. RtlFreeUnicodeString(&urlW);
  732. return ret;
  733. }
  734. #define WINE_URL_BASH_AS_SLASH 0x01
  735. #define WINE_URL_COLLAPSE_SLASHES 0x02
  736. #define WINE_URL_ESCAPE_SLASH 0x04
  737. #define WINE_URL_ESCAPE_HASH 0x08
  738. #define WINE_URL_ESCAPE_QUESTION 0x10
  739. #define WINE_URL_STOP_ON_HASH 0x20
  740. #define WINE_URL_STOP_ON_QUESTION 0x40
  741. static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
  742. {
  743. if (isalnumW(ch))
  744. return FALSE;
  745. if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
  746. if(ch == ' ')
  747. return TRUE;
  748. else
  749. return FALSE;
  750. }
  751. if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
  752. return TRUE;
  753. if (ch <= 31 || ch >= 127)
  754. return TRUE;
  755. else {
  756. switch (ch) {
  757. case ' ':
  758. case '<':
  759. case '>':
  760. case '\"':
  761. case '{':
  762. case '}':
  763. case '|':
  764. case '\\':
  765. case '^':
  766. case ']':
  767. case '[':
  768. case '`':
  769. case '&':
  770. return TRUE;
  771. case '/':
  772. if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
  773. return FALSE;
  774. case '?':
  775. if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
  776. return FALSE;
  777. case '#':
  778. if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
  779. return FALSE;
  780. default:
  781. return FALSE;
  782. }
  783. }
  784. }
  785. /*************************************************************************
  786. * UrlEscapeW [SHLWAPI.@]
  787. *
  788. * Converts unsafe characters in a Url into escape sequences.
  789. *
  790. * PARAMS
  791. * pszUrl [I] Url to modify
  792. * pszEscaped [O] Destination for modified Url
  793. * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
  794. * dwFlags [I] URL_ flags from "shlwapi.h"
  795. *
  796. * RETURNS
  797. * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
  798. * contains its length.
  799. * Failure: E_POINTER, if pszEscaped is not large enough. In this case
  800. * pcchEscaped is set to the required length.
  801. *
  802. * Converts unsafe characters into their escape sequences.
  803. *
  804. * NOTES
  805. * - By default this function stops converting at the first '?' or
  806. * '#' character.
  807. * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
  808. * converted, but the conversion continues past a '?' or '#'.
  809. * - Note that this function did not work well (or at all) in shlwapi version 4.
  810. *
  811. * BUGS
  812. * Only the following flags are implemented:
  813. *| URL_ESCAPE_SPACES_ONLY
  814. *| URL_DONT_ESCAPE_EXTRA_INFO
  815. *| URL_ESCAPE_SEGMENT_ONLY
  816. *| URL_ESCAPE_PERCENT
  817. */
  818. HRESULT WINAPI UrlEscapeW(
  819. LPCWSTR pszUrl,
  820. LPWSTR pszEscaped,
  821. LPDWORD pcchEscaped,
  822. DWORD dwFlags)
  823. {
  824. LPCWSTR src;
  825. DWORD needed = 0, ret;
  826. BOOL stop_escaping = FALSE;
  827. WCHAR next[5], *dst = pszEscaped;
  828. INT len;
  829. PARSEDURLW parsed_url;
  830. DWORD int_flags;
  831. DWORD slashes = 0;
  832. static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
  833. TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
  834. pcchEscaped, dwFlags);
  835. if(!pszUrl || !pszEscaped || !pcchEscaped)
  836. return E_INVALIDARG;
  837. if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
  838. URL_ESCAPE_SEGMENT_ONLY |
  839. URL_DONT_ESCAPE_EXTRA_INFO |
  840. URL_ESCAPE_PERCENT))
  841. FIXME("Unimplemented flags: %08lx\n", dwFlags);
  842. /* fix up flags */
  843. if (dwFlags & URL_ESCAPE_SPACES_ONLY)
  844. /* if SPACES_ONLY specified, reset the other controls */
  845. dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
  846. URL_ESCAPE_PERCENT |
  847. URL_ESCAPE_SEGMENT_ONLY);
  848. else
  849. /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
  850. dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
  851. int_flags = 0;
  852. if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
  853. int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
  854. } else {
  855. parsed_url.cbSize = sizeof(parsed_url);
  856. if(ParseURLW(pszUrl, &parsed_url) != S_OK)
  857. parsed_url.nScheme = URL_SCHEME_INVALID;
  858. TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
  859. if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
  860. int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
  861. switch(parsed_url.nScheme) {
  862. case URL_SCHEME_FILE:
  863. int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
  864. int_flags &= ~WINE_URL_STOP_ON_HASH;
  865. break;
  866. case URL_SCHEME_HTTP:
  867. case URL_SCHEME_HTTPS:
  868. int_flags |= WINE_URL_BASH_AS_SLASH;
  869. if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
  870. int_flags |= WINE_URL_ESCAPE_SLASH;
  871. break;
  872. case URL_SCHEME_MAILTO:
  873. int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
  874. int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
  875. break;
  876. case URL_SCHEME_INVALID:
  877. break;
  878. case URL_SCHEME_FTP:
  879. default:
  880. if(parsed_url.pszSuffix[0] != '/')
  881. int_flags |= WINE_URL_ESCAPE_SLASH;
  882. break;
  883. }
  884. }
  885. for(src = pszUrl; *src; ) {
  886. WCHAR cur = *src;
  887. len = 0;
  888. if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
  889. int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
  890. while(cur == '/' || cur == '\\') {
  891. slashes++;
  892. cur = *++src;
  893. }
  894. if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
  895. if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
  896. src += localhost_len + 1;
  897. slashes = 3;
  898. }
  899. switch(slashes) {
  900. case 1:
  901. case 3:
  902. next[0] = next[1] = next[2] = '/';
  903. len = 3;
  904. break;
  905. case 0:
  906. len = 0;
  907. break;
  908. default:
  909. next[0] = next[1] = '/';
  910. len = 2;
  911. break;
  912. }
  913. }
  914. if(len == 0) {
  915. if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
  916. stop_escaping = TRUE;
  917. if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
  918. stop_escaping = TRUE;
  919. if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
  920. if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
  921. next[0] = L'%';
  922. next[1] = hexDigits[(cur >> 4) & 0xf];
  923. next[2] = hexDigits[cur & 0xf];
  924. len = 3;
  925. } else {
  926. next[0] = cur;
  927. len = 1;
  928. }
  929. src++;
  930. }
  931. if(needed + len <= *pcchEscaped) {
  932. memcpy(dst, next, len*sizeof(WCHAR));
  933. dst += len;
  934. }
  935. needed += len;
  936. }
  937. if(needed < *pcchEscaped) {
  938. *dst = '\0';
  939. ret = S_OK;
  940. } else {
  941. needed++; /* add one for the '\0' */
  942. ret = E_POINTER;
  943. }
  944. *pcchEscaped = needed;
  945. return ret;
  946. }
  947. /*************************************************************************
  948. * UrlUnescapeA [SHLWAPI.@]
  949. *
  950. * Converts Url escape sequences back to ordinary characters.
  951. *
  952. * PARAMS
  953. * pszUrl [I/O] Url to convert
  954. * pszUnescaped [O] Destination for converted Url
  955. * pcchUnescaped [I/O] Size of output string
  956. * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
  957. *
  958. * RETURNS
  959. * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
  960. * dwFlags includes URL_ESCAPE_INPLACE.
  961. * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
  962. * this case pcchUnescaped is set to the size required.
  963. * NOTES
  964. * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
  965. * the first occurrence of either a '?' or '#' character.
  966. */
  967. HRESULT WINAPI UrlUnescapeA(
  968. LPSTR pszUrl,
  969. LPSTR pszUnescaped,
  970. LPDWORD pcchUnescaped,
  971. DWORD dwFlags)
  972. {
  973. char *dst, next;
  974. LPCSTR src;
  975. HRESULT ret;
  976. DWORD needed;
  977. BOOL stop_unescaping = FALSE;
  978. TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
  979. pcchUnescaped, dwFlags);
  980. if(!pszUrl || !pszUnescaped || !pcchUnescaped)
  981. return E_INVALIDARG;
  982. if(dwFlags & URL_UNESCAPE_INPLACE)
  983. dst = pszUrl;
  984. else
  985. dst = pszUnescaped;
  986. for(src = pszUrl, needed = 0; *src; src++, needed++) {
  987. if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
  988. (*src == '#' || *src == '?')) {
  989. stop_unescaping = TRUE;
  990. next = *src;
  991. } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
  992. && stop_unescaping == FALSE) {
  993. INT ih;
  994. char buf[3];
  995. memcpy(buf, src + 1, 2);
  996. buf[2] = '\0';
  997. ih = strtol(buf, NULL, 16);
  998. next = (CHAR) ih;
  999. src += 2; /* Advance to end of escape */
  1000. } else
  1001. next = *src;
  1002. if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
  1003. *dst++ = next;
  1004. }
  1005. if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
  1006. *dst = '\0';
  1007. ret = S_OK;
  1008. } else {
  1009. needed++; /* add one for the '\0' */
  1010. ret = E_POINTER;
  1011. }
  1012. if(!(dwFlags & URL_UNESCAPE_INPLACE))
  1013. *pcchUnescaped = needed;
  1014. if (ret == S_OK) {
  1015. TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
  1016. debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
  1017. }
  1018. return ret;
  1019. }
  1020. /*************************************************************************
  1021. * UrlUnescapeW [SHLWAPI.@]
  1022. *
  1023. * See UrlUnescapeA.
  1024. */
  1025. HRESULT WINAPI UrlUnescapeW(
  1026. LPWSTR pszUrl,
  1027. LPWSTR pszUnescaped,
  1028. LPDWORD pcchUnescaped,
  1029. DWORD dwFlags)
  1030. {
  1031. WCHAR *dst, next;
  1032. LPCWSTR src;
  1033. HRESULT ret;
  1034. DWORD needed;
  1035. BOOL stop_unescaping = FALSE;
  1036. TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
  1037. pcchUnescaped, dwFlags);
  1038. if(!pszUrl || !pszUnescaped || !pcchUnescaped)
  1039. return E_INVALIDARG;
  1040. if(dwFlags & URL_UNESCAPE_INPLACE)
  1041. dst = pszUrl;
  1042. else
  1043. dst = pszUnescaped;
  1044. for(src = pszUrl, needed = 0; *src; src++, needed++) {
  1045. if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
  1046. (*src == L'#' || *src == L'?')) {
  1047. stop_unescaping = TRUE;
  1048. next = *src;
  1049. } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
  1050. && stop_unescaping == FALSE) {
  1051. INT ih;
  1052. WCHAR buf[5] = {'0','x',0};
  1053. memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
  1054. buf[4] = 0;
  1055. StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
  1056. next = (WCHAR) ih;
  1057. src += 2; /* Advance to end of escape */
  1058. } else
  1059. next = *src;
  1060. if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
  1061. *dst++ = next;
  1062. }
  1063. if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
  1064. *dst = L'\0';
  1065. ret = S_OK;
  1066. } else {
  1067. needed++; /* add one for the '\0' */
  1068. ret = E_POINTER;
  1069. }
  1070. if(!(dwFlags & URL_UNESCAPE_INPLACE))
  1071. *pcchUnescaped = needed;
  1072. if (ret == S_OK) {
  1073. TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
  1074. debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
  1075. }
  1076. return ret;
  1077. }
  1078. /*************************************************************************
  1079. * UrlGetLocationA [SHLWAPI.@]
  1080. *
  1081. * Get the location from a Url.
  1082. *
  1083. * PARAMS
  1084. * pszUrl [I] Url to get the location from
  1085. *
  1086. * RETURNS
  1087. * A pointer to the start of the location in pszUrl, or NULL if there is
  1088. * no location.
  1089. *
  1090. * NOTES
  1091. * - MSDN erroneously states that "The location is the segment of the Url
  1092. * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
  1093. * stop at '?' and always return a NULL in this case.
  1094. * - MSDN also erroneously states that "If a file URL has a query string,
  1095. * the returned string is the query string". In all tested cases, if the
  1096. * Url starts with "fi" then a NULL is returned. V5 gives the following results:
  1097. *| Result Url
  1098. *| ------ ---
  1099. *| NULL file://aa/b/cd#hohoh
  1100. *| #hohoh http://aa/b/cd#hohoh
  1101. *| NULL fi://aa/b/cd#hohoh
  1102. *| #hohoh ff://aa/b/cd#hohoh
  1103. */
  1104. LPCSTR WINAPI UrlGetLocationA(
  1105. LPCSTR pszUrl)
  1106. {
  1107. PARSEDURLA base;
  1108. DWORD res1;
  1109. base.cbSize = sizeof(base);
  1110. res1 = ParseURLA(pszUrl, &base);
  1111. if (res1) return NULL; /* invalid scheme */
  1112. /* if scheme is file: then never return pointer */
  1113. if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
  1114. /* Look for '#' and return its addr */
  1115. return strchr(base.pszSuffix, '#');
  1116. }
  1117. /*************************************************************************
  1118. * UrlGetLocationW [SHLWAPI.@]
  1119. *
  1120. * See UrlGetLocationA.
  1121. */
  1122. LPCWSTR WINAPI UrlGetLocationW(
  1123. LPCWSTR pszUrl)
  1124. {
  1125. PARSEDURLW base;
  1126. DWORD res1;
  1127. base.cbSize = sizeof(base);
  1128. res1 = ParseURLW(pszUrl, &base);
  1129. if (res1) return NULL; /* invalid scheme */
  1130. /* if scheme is file: then never return pointer */
  1131. if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
  1132. /* Look for '#' and return its addr */
  1133. return strchrW(base.pszSuffix, L'#');
  1134. }
  1135. /*************************************************************************
  1136. * UrlCompareA [SHLWAPI.@]
  1137. *
  1138. * Compare two Urls.
  1139. *
  1140. * PARAMS
  1141. * pszUrl1 [I] First Url to compare
  1142. * pszUrl2 [I] Url to compare to pszUrl1
  1143. * fIgnoreSlash [I] TRUE = compare only up to a final slash
  1144. *
  1145. * RETURNS
  1146. * less than zero, zero, or greater than zero indicating pszUrl2 is greater
  1147. * than, equal to, or less than pszUrl1 respectively.
  1148. */
  1149. INT WINAPI UrlCompareA(
  1150. LPCSTR pszUrl1,
  1151. LPCSTR pszUrl2,
  1152. BOOL fIgnoreSlash)
  1153. {
  1154. INT ret, len, len1, len2;
  1155. if (!fIgnoreSlash)
  1156. return strcmp(pszUrl1, pszUrl2);
  1157. len1 = strlen(pszUrl1);
  1158. if (pszUrl1[len1-1] == '/') len1--;
  1159. len2 = strlen(pszUrl2);
  1160. if (pszUrl2[len2-1] == '/') len2--;
  1161. if (len1 == len2)
  1162. return strncmp(pszUrl1, pszUrl2, len1);
  1163. len = min(len1, len2);
  1164. ret = strncmp(pszUrl1, pszUrl2, len);
  1165. if (ret) return ret;
  1166. if (len1 > len2) return 1;
  1167. return -1;
  1168. }
  1169. /*************************************************************************
  1170. * UrlCompareW [SHLWAPI.@]
  1171. *
  1172. * See UrlCompareA.
  1173. */
  1174. INT WINAPI UrlCompareW(
  1175. LPCWSTR pszUrl1,
  1176. LPCWSTR pszUrl2,
  1177. BOOL fIgnoreSlash)
  1178. {
  1179. INT ret;
  1180. size_t len, len1, len2;
  1181. if (!fIgnoreSlash)
  1182. return strcmpW(pszUrl1, pszUrl2);
  1183. len1 = strlenW(pszUrl1);
  1184. if (pszUrl1[len1-1] == '/') len1--;
  1185. len2 = strlenW(pszUrl2);
  1186. if (pszUrl2[len2-1] == '/') len2--;
  1187. if (len1 == len2)
  1188. return strncmpW(pszUrl1, pszUrl2, len1);
  1189. len = min(len1, len2);
  1190. ret = strncmpW(pszUrl1, pszUrl2, len);
  1191. if (ret) return ret;
  1192. if (len1 > len2) return 1;
  1193. return -1;
  1194. }
  1195. /*************************************************************************
  1196. * HashData [SHLWAPI.@]
  1197. *
  1198. * Hash an input block into a variable sized digest.
  1199. *
  1200. * PARAMS
  1201. * lpSrc [I] Input block
  1202. * nSrcLen [I] Length of lpSrc
  1203. * lpDest [I] Output for hash digest
  1204. * nDestLen [I] Length of lpDest
  1205. *
  1206. * RETURNS
  1207. * Success: TRUE. lpDest is filled with the computed hash value.
  1208. * Failure: FALSE, if any argument is invalid.
  1209. */
  1210. HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
  1211. unsigned char *lpDest, DWORD nDestLen)
  1212. {
  1213. INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
  1214. if (IsBadReadPtr(lpSrc, nSrcLen) ||
  1215. IsBadWritePtr(lpDest, nDestLen))
  1216. return E_INVALIDARG;
  1217. while (destCount >= 0)
  1218. {
  1219. lpDest[destCount] = (destCount & 0xff);
  1220. destCount--;
  1221. }
  1222. while (srcCount >= 0)
  1223. {
  1224. destCount = nDestLen - 1;
  1225. while (destCount >= 0)
  1226. {
  1227. lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
  1228. destCount--;
  1229. }
  1230. srcCount--;
  1231. }
  1232. return S_OK;
  1233. }
  1234. /*************************************************************************
  1235. * UrlHashA [SHLWAPI.@]
  1236. *
  1237. * Produce a Hash from a Url.
  1238. *
  1239. * PARAMS
  1240. * pszUrl [I] Url to hash
  1241. * lpDest [O] Destinationh for hash
  1242. * nDestLen [I] Length of lpDest
  1243. *
  1244. * RETURNS
  1245. * Success: S_OK. lpDest is filled with the computed hash value.
  1246. * Failure: E_INVALIDARG, if any argument is invalid.
  1247. */
  1248. HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
  1249. {
  1250. if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
  1251. return E_INVALIDARG;
  1252. HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
  1253. return S_OK;
  1254. }
  1255. /*************************************************************************
  1256. * UrlHashW [SHLWAPI.@]
  1257. *
  1258. * See UrlHashA.
  1259. */
  1260. HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
  1261. {
  1262. char szUrl[MAX_PATH];
  1263. TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
  1264. if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
  1265. return E_INVALIDARG;
  1266. /* Win32 hashes the data as an ASCII string, presumably so that both A+W
  1267. * return the same digests for the same URL.
  1268. */
  1269. WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
  1270. HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
  1271. return S_OK;
  1272. }
  1273. /*************************************************************************
  1274. * UrlApplySchemeA [SHLWAPI.@]
  1275. *
  1276. * Apply a scheme to a Url.
  1277. *
  1278. * PARAMS
  1279. * pszIn [I] Url to apply scheme to
  1280. * pszOut [O] Destination for modified Url
  1281. * pcchOut [I/O] Length of pszOut/destination for length of pszOut
  1282. * dwFlags [I] URL_ flags from "shlwapi.h"
  1283. *
  1284. * RETURNS
  1285. * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
  1286. * Failure: An HRESULT error code describing the error.
  1287. */
  1288. HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
  1289. {
  1290. LPWSTR in, out;
  1291. DWORD ret, len, len2;
  1292. TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
  1293. debugstr_a(pszIn), *pcchOut, dwFlags);
  1294. in = HeapAlloc(GetProcessHeap(), 0,
  1295. (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
  1296. out = in + INTERNET_MAX_URL_LENGTH;
  1297. MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
  1298. len = INTERNET_MAX_URL_LENGTH;
  1299. ret = UrlApplySchemeW(in, out, &len, dwFlags);
  1300. if ((ret != S_OK) && (ret != S_FALSE)) {
  1301. HeapFree(GetProcessHeap(), 0, in);
  1302. return ret;
  1303. }
  1304. len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
  1305. if (len2 > *pcchOut) {
  1306. *pcchOut = len2;
  1307. HeapFree(GetProcessHeap(), 0, in);
  1308. return E_POINTER;
  1309. }
  1310. WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
  1311. *pcchOut = len2;
  1312. HeapFree(GetProcessHeap(), 0, in);
  1313. return ret;
  1314. }
  1315. static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
  1316. {
  1317. HKEY newkey;
  1318. BOOL j;
  1319. INT index;
  1320. DWORD value_len, data_len, dwType, i;
  1321. WCHAR reg_path[MAX_PATH];
  1322. WCHAR value[MAX_PATH], data[MAX_PATH];
  1323. WCHAR Wxx, Wyy;
  1324. MultiByteToWideChar(0, 0,
  1325. "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
  1326. -1, reg_path, MAX_PATH);
  1327. RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
  1328. index = 0;
  1329. while(value_len = data_len = MAX_PATH,
  1330. RegEnumValueW(newkey, index, value, &value_len,
  1331. 0, &dwType, (LPVOID)data, &data_len) == 0) {
  1332. TRACE("guess %d %s is %s\n",
  1333. index, debugstr_w(value), debugstr_w(data));
  1334. j = FALSE;
  1335. for(i=0; i<value_len; i++) {
  1336. Wxx = pszIn[i];
  1337. Wyy = value[i];
  1338. /* remember that TRUE is not-equal */
  1339. j = ChrCmpIW(Wxx, Wyy);
  1340. if (j) break;
  1341. }
  1342. if ((i == value_len) && !j) {
  1343. if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
  1344. *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
  1345. RegCloseKey(newkey);
  1346. return E_POINTER;
  1347. }
  1348. strcpyW(pszOut, data);
  1349. strcatW(pszOut, pszIn);
  1350. *pcchOut = strlenW(pszOut);
  1351. TRACE("matched and set to %s\n", debugstr_w(pszOut));
  1352. RegCloseKey(newkey);
  1353. return S_OK;
  1354. }
  1355. index++;
  1356. }
  1357. RegCloseKey(newkey);
  1358. return -1;
  1359. }
  1360. static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
  1361. {
  1362. HKEY newkey;
  1363. DWORD data_len, dwType;
  1364. WCHAR reg_path[MAX_PATH];
  1365. WCHAR value[MAX_PATH], data[MAX_PATH];
  1366. /* get and prepend default */
  1367. MultiByteToWideChar(0, 0,
  1368. "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
  1369. -1, reg_path, MAX_PATH);
  1370. RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
  1371. data_len = MAX_PATH;
  1372. value[0] = L'@';
  1373. value[1] = L'\0';
  1374. RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
  1375. RegCloseKey(newkey);
  1376. if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
  1377. *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
  1378. return E_POINTER;
  1379. }
  1380. strcpyW(pszOut, data);
  1381. strcatW(pszOut, pszIn);
  1382. *pcchOut = strlenW(pszOut);
  1383. TRACE("used default %s\n", debugstr_w(pszOut));
  1384. return S_OK;
  1385. }
  1386. /*************************************************************************
  1387. * UrlApplySchemeW [SHLWAPI.@]
  1388. *
  1389. * See UrlApplySchemeA.
  1390. */
  1391. HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
  1392. {
  1393. PARSEDURLW in_scheme;
  1394. DWORD res1;
  1395. HRESULT ret;
  1396. TRACE("(in %s, out size %ld, flags %08lx)\n",
  1397. debugstr_w(pszIn), *pcchOut, dwFlags);
  1398. if (dwFlags & URL_APPLY_GUESSFILE) {
  1399. FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
  1400. debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
  1401. strcpyW(pszOut, pszIn);
  1402. *pcchOut = strlenW(pszOut);
  1403. return S_FALSE;
  1404. }
  1405. in_scheme.cbSize = sizeof(in_scheme);
  1406. /* See if the base has a scheme */
  1407. res1 = ParseURLW(pszIn, &in_scheme);
  1408. if (res1) {
  1409. /* no scheme in input, need to see if we need to guess */
  1410. if (dwFlags & URL_APPLY_GUESSSCHEME) {
  1411. if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
  1412. return ret;
  1413. }
  1414. }
  1415. else {
  1416. /* we have a scheme, see if valid (known scheme) */
  1417. if (in_scheme.nScheme) {
  1418. /* have valid scheme, so just copy and exit */
  1419. if (strlenW(pszIn) + 1 > *pcchOut) {
  1420. *pcchOut = strlenW(pszIn) + 1;
  1421. return E_POINTER;
  1422. }
  1423. strcpyW(pszOut, pszIn);
  1424. *pcchOut = strlenW(pszOut);
  1425. TRACE("valid scheme, returing copy\n");
  1426. return S_OK;
  1427. }
  1428. }
  1429. /* If we are here, then either invalid scheme,
  1430. * or no scheme and can't/failed guess.
  1431. */
  1432. if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
  1433. ((res1 != 0)) ) &&
  1434. (dwFlags & URL_APPLY_DEFAULT)) {
  1435. /* find and apply default scheme */
  1436. return URL_ApplyDefault(pszIn, pszOut, pcchOut);
  1437. }
  1438. /* just copy and give proper return code */
  1439. if (strlenW(pszIn) + 1 > *pcchOut) {
  1440. *pcchOut = strlenW(pszIn) + 1;
  1441. return E_POINTER;
  1442. }
  1443. strcpyW(pszOut, pszIn);
  1444. *pcchOut = strlenW(pszOut);
  1445. TRACE("returning copy, left alone\n");
  1446. return S_FALSE;
  1447. }
  1448. /*************************************************************************
  1449. * UrlIsA [SHLWAPI.@]
  1450. *
  1451. * Determine if a Url is of a certain class.
  1452. *
  1453. * PARAMS
  1454. * pszUrl [I] Url to check
  1455. * Urlis [I] URLIS_ constant from "shlwapi.h"
  1456. *
  1457. * RETURNS
  1458. * TRUE if pszUrl belongs to the class type in Urlis.
  1459. * FALSE Otherwise.
  1460. */
  1461. BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
  1462. {
  1463. PARSEDURLA base;
  1464. DWORD res1;
  1465. LPCSTR last;
  1466. TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
  1467. switch (Urlis) {
  1468. case URLIS_OPAQUE:
  1469. base.cbSize = sizeof(base);
  1470. res1 = ParseURLA(pszUrl, &base);
  1471. if (res1) return FALSE; /* invalid scheme */
  1472. if ((*base.pszSuffix == '/') && (*(base.pszSuffix+1) == '/'))
  1473. /* has scheme followed by 2 '/' */
  1474. return FALSE;
  1475. return TRUE;
  1476. case URLIS_FILEURL:
  1477. return !StrCmpNA("file://", pszUrl, 7);
  1478. case URLIS_DIRECTORY:
  1479. last = pszUrl + strlen(pszUrl) - 1;
  1480. return (last >= pszUrl && (*last == '/' || *last == '\\' ));
  1481. case URLIS_URL:
  1482. return PathIsURLA(pszUrl);
  1483. case URLIS_NOHISTORY:
  1484. case URLIS_APPLIABLE:
  1485. case URLIS_HASQUERY:
  1486. default:
  1487. FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
  1488. }
  1489. return FALSE;
  1490. }
  1491. /*************************************************************************
  1492. * UrlIsW [SHLWAPI.@]
  1493. *
  1494. * See UrlIsA.
  1495. */
  1496. BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
  1497. {
  1498. static const WCHAR stemp[] = { 'f','i','l','e',':','/','/',0 };
  1499. PARSEDURLW base;
  1500. DWORD res1;
  1501. LPCWSTR last;
  1502. TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
  1503. switch (Urlis) {
  1504. case URLIS_OPAQUE:
  1505. base.cbSize = sizeof(base);
  1506. res1 = ParseURLW(pszUrl, &base);
  1507. if (res1) return FALSE; /* invalid scheme */
  1508. if ((*base.pszSuffix == '/') && (*(base.pszSuffix+1) == '/'))
  1509. /* has scheme followed by 2 '/' */
  1510. return FALSE;
  1511. return TRUE;
  1512. case URLIS_FILEURL:
  1513. return !strncmpW(stemp, pszUrl, 7);
  1514. case URLIS_DIRECTORY:
  1515. last = pszUrl + strlenW(pszUrl) - 1;
  1516. return (last >= pszUrl && (*last == '/' || *last == '\\'));
  1517. case URLIS_URL:
  1518. return PathIsURLW(pszUrl);
  1519. case URLIS_NOHISTORY:
  1520. case URLIS_APPLIABLE:
  1521. case URLIS_HASQUERY:
  1522. default:
  1523. FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
  1524. }
  1525. return FALSE;
  1526. }
  1527. /*************************************************************************
  1528. * UrlIsNoHistoryA [SHLWAPI.@]
  1529. *
  1530. * Determine if a Url should not be stored in the users history list.
  1531. *
  1532. * PARAMS
  1533. * pszUrl [I] Url to check
  1534. *
  1535. * RETURNS
  1536. * TRUE, if pszUrl should be excluded from the history list,
  1537. * FALSE otherwise.
  1538. */
  1539. BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
  1540. {
  1541. return UrlIsA(pszUrl, URLIS_NOHISTORY);
  1542. }
  1543. /*************************************************************************
  1544. * UrlIsNoHistoryW [SHLWAPI.@]
  1545. *
  1546. * See UrlIsNoHistoryA.
  1547. */
  1548. BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
  1549. {
  1550. return UrlIsW(pszUrl, URLIS_NOHISTORY);
  1551. }
  1552. /*************************************************************************
  1553. * UrlIsOpaqueA [SHLWAPI.@]
  1554. *
  1555. * Determine if a Url is opaque.
  1556. *
  1557. * PARAMS
  1558. * pszUrl [I] Url to check
  1559. *
  1560. * RETURNS
  1561. * TRUE if pszUrl is opaque,
  1562. * FALSE Otherwise.
  1563. *
  1564. * NOTES
  1565. * An opaque Url is one that does not start with "<protocol>://".
  1566. */
  1567. BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
  1568. {
  1569. return UrlIsA(pszUrl, URLIS_OPAQUE);
  1570. }
  1571. /*************************************************************************
  1572. * UrlIsOpaqueW [SHLWAPI.@]
  1573. *
  1574. * See UrlIsOpaqueA.
  1575. */
  1576. BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
  1577. {
  1578. return UrlIsW(pszUrl, URLIS_OPAQUE);
  1579. }
  1580. /*************************************************************************
  1581. * Scans for characters of type "type" and when not matching found,
  1582. * returns pointer to it and length in size.
  1583. *
  1584. * Characters tested based on RFC 1738
  1585. */
  1586. static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
  1587. {
  1588. static DWORD alwayszero = 0;
  1589. BOOL cont = TRUE;
  1590. *size = 0;
  1591. switch(type){
  1592. case SCHEME:
  1593. while (cont) {
  1594. if ( (islowerW(*start) && isalphaW(*start)) ||
  1595. isdigitW(*start) ||
  1596. (*start == L'+') ||
  1597. (*start == L'-') ||
  1598. (*start == L'.')) {
  1599. start++;
  1600. (*size)++;
  1601. }
  1602. else
  1603. cont = FALSE;
  1604. }
  1605. break;
  1606. case USERPASS:
  1607. while (cont) {
  1608. if ( isalphaW(*start) ||
  1609. isdigitW(*start) ||
  1610. /* user/password only characters */
  1611. (*start == L';') ||
  1612. (*start == L'?') ||
  1613. (*start == L'&') ||
  1614. (*start == L'=') ||
  1615. /* *extra* characters */
  1616. (*start == L'!') ||
  1617. (*start == L'*') ||
  1618. (*start == L'\'') ||
  1619. (*start == L'(') ||
  1620. (*start == L')') ||
  1621. (*start == L',') ||
  1622. /* *safe* characters */
  1623. (*start == L'$') ||
  1624. (*start == L'_') ||
  1625. (*start == L'+') ||
  1626. (*start == L'-') ||
  1627. (*start == L'.')) {
  1628. start++;
  1629. (*size)++;
  1630. } else if (*start == L'%') {
  1631. if (isxdigitW(*(start+1)) &&
  1632. isxdigitW(*(start+2))) {
  1633. start += 3;
  1634. *size += 3;
  1635. } else
  1636. cont = FALSE;
  1637. } else
  1638. cont = FALSE;
  1639. }
  1640. break;
  1641. case PORT:
  1642. while (cont) {
  1643. if (isdigitW(*start)) {
  1644. start++;
  1645. (*size)++;
  1646. }
  1647. else
  1648. cont = FALSE;
  1649. }
  1650. break;
  1651. case HOST:
  1652. while (cont) {
  1653. if (isalnumW(*start) ||
  1654. (*start == L'-') ||
  1655. (*start == L'.') ) {
  1656. start++;
  1657. (*size)++;
  1658. }
  1659. else
  1660. cont = FALSE;
  1661. }
  1662. break;
  1663. default:
  1664. FIXME("unknown type %d\n", type);
  1665. return (LPWSTR)&alwayszero;
  1666. }
  1667. /* TRACE("scanned %ld characters next char %p<%c>\n",
  1668. *size, start, *start); */
  1669. return start;
  1670. }
  1671. /*************************************************************************
  1672. * Attempt to parse URL into pieces.
  1673. */
  1674. static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
  1675. {
  1676. LPCWSTR work;
  1677. memset(pl, 0, sizeof(WINE_PARSE_URL));
  1678. pl->pScheme = pszUrl;
  1679. work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
  1680. if (!*work || (*work != L':')) goto ErrorExit;
  1681. work++;
  1682. if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
  1683. pl->pUserName = work + 2;
  1684. work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
  1685. if (*work == L':' ) {
  1686. /* parse password */
  1687. work++;
  1688. pl->pPassword = work;
  1689. work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
  1690. if (*work != L'@') {
  1691. /* what we just parsed must be the hostname and port
  1692. * so reset pointers and clear then let it parse */
  1693. pl->szUserName = pl->szPassword = 0;
  1694. work = pl->pUserName - 1;
  1695. pl->pUserName = pl->pPassword = 0;
  1696. }
  1697. } else if (*work == L'@') {
  1698. /* no password */
  1699. pl->szPassword = 0;
  1700. pl->pPassword = 0;
  1701. } else if (!*work || (*work == L'/') || (*work == L'.')) {
  1702. /* what was parsed was hostname, so reset pointers and let it parse */
  1703. pl->szUserName = pl->szPassword = 0;
  1704. work = pl->pUserName - 1;
  1705. pl->pUserName = pl->pPassword = 0;
  1706. } else goto ErrorExit;
  1707. /* now start parsing hostname or hostnumber */
  1708. work++;
  1709. pl->pHostName = work;
  1710. work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
  1711. if (*work == L':') {
  1712. /* parse port */
  1713. work++;
  1714. pl->pPort = work;
  1715. work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
  1716. }
  1717. if (*work == L'/') {
  1718. /* see if query string */
  1719. pl->pQuery = strchrW(work, L'?');
  1720. if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
  1721. }
  1722. TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
  1723. pl->pScheme, pl->szScheme,
  1724. pl->pUserName, pl->szUserName,
  1725. pl->pPassword, pl->szPassword,
  1726. pl->pHostName, pl->szHostName,
  1727. pl->pPort, pl->szPort,
  1728. pl->pQuery, pl->szQuery);
  1729. return S_OK;
  1730. ErrorExit:
  1731. FIXME("failed to parse %s\n", debugstr_w(pszUrl));
  1732. return E_INVALIDARG;
  1733. }
  1734. /*************************************************************************
  1735. * UrlGetPartA [SHLWAPI.@]
  1736. *
  1737. * Retrieve part of a Url.
  1738. *
  1739. * PARAMS
  1740. * pszIn [I] Url to parse
  1741. * pszOut [O] Destination for part of pszIn requested
  1742. * pcchOut [I/O] Length of pszOut/destination for length of pszOut
  1743. * dwPart [I] URL_PART_ enum from "shlwapi.h"
  1744. * dwFlags [I] URL_ flags from "shlwapi.h"
  1745. *
  1746. * RETURNS
  1747. * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
  1748. * Failure: An HRESULT error code describing the error.
  1749. */
  1750. HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
  1751. DWORD dwPart, DWORD dwFlags)
  1752. {
  1753. LPWSTR in, out;
  1754. DWORD ret, len, len2;
  1755. in = HeapAlloc(GetProcessHeap(), 0,
  1756. (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
  1757. out = in + INTERNET_MAX_URL_LENGTH;
  1758. MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
  1759. len = INTERNET_MAX_URL_LENGTH;
  1760. ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
  1761. if (ret != S_OK) {
  1762. HeapFree(GetProcessHeap(), 0, in);
  1763. return ret;
  1764. }
  1765. len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
  1766. if (len2 > *pcchOut) {
  1767. *pcchOut = len2;
  1768. HeapFree(GetProcessHeap(), 0, in);
  1769. return E_POINTER;
  1770. }
  1771. WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
  1772. *pcchOut = len2;
  1773. HeapFree(GetProcessHeap(), 0, in);
  1774. return S_OK;
  1775. }
  1776. /*************************************************************************
  1777. * UrlGetPartW [SHLWAPI.@]
  1778. *
  1779. * See UrlGetPartA.
  1780. */
  1781. HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
  1782. DWORD dwPart, DWORD dwFlags)
  1783. {
  1784. WINE_PARSE_URL pl;
  1785. HRESULT ret;
  1786. DWORD size, schsize;
  1787. LPCWSTR addr, schaddr;
  1788. LPWSTR work;
  1789. TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
  1790. debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
  1791. ret = URL_ParseUrl(pszIn, &pl);
  1792. if (!ret) {
  1793. schaddr = pl.pScheme;
  1794. schsize = pl.szScheme;
  1795. switch (dwPart) {
  1796. case URL_PART_SCHEME:
  1797. if (!pl.szScheme) return E_INVALIDARG;
  1798. addr = pl.pScheme;
  1799. size = pl.szScheme;
  1800. break;
  1801. case URL_PART_HOSTNAME:
  1802. if (!pl.szHostName) return E_INVALIDARG;
  1803. addr = pl.pHostName;
  1804. size = pl.szHostName;
  1805. break;
  1806. case URL_PART_USERNAME:
  1807. if (!pl.szUserName) return E_INVALIDARG;
  1808. addr = pl.pUserName;
  1809. size = pl.szUserName;
  1810. break;
  1811. case URL_PART_PASSWORD:
  1812. if (!pl.szPassword) return E_INVALIDARG;
  1813. addr = pl.pPassword;
  1814. size = pl.szPassword;
  1815. break;
  1816. case URL_PART_PORT:
  1817. if (!pl.szPort) return E_INVALIDARG;
  1818. addr = pl.pPort;
  1819. size = pl.szPort;
  1820. break;
  1821. case URL_PART_QUERY:
  1822. if (!pl.szQuery) return E_INVALIDARG;
  1823. addr = pl.pQuery;
  1824. size = pl.szQuery;
  1825. break;
  1826. default:
  1827. return E_INVALIDARG;
  1828. }
  1829. if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
  1830. if (*pcchOut < size + schsize + 2) {
  1831. *pcchOut = size + schsize + 2;
  1832. return E_POINTER;
  1833. }
  1834. strncpyW(pszOut, schaddr, schsize);
  1835. work = pszOut + schsize;
  1836. *work = L':';
  1837. strncpyW(work+1, addr, size);
  1838. *pcchOut = size + schsize + 1;
  1839. work += (size + 1);
  1840. *work = L'\0';
  1841. }
  1842. else {
  1843. if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
  1844. strncpyW(pszOut, addr, size);
  1845. *pcchOut = size;
  1846. work = pszOut + size;
  1847. *work = L'\0';
  1848. }
  1849. TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
  1850. }
  1851. return ret;
  1852. }
  1853. /*************************************************************************
  1854. * PathIsURLA [SHLWAPI.@]
  1855. *
  1856. * Check if the given path is a Url.
  1857. *
  1858. * PARAMS
  1859. * lpszPath [I] Path to check.
  1860. *
  1861. * RETURNS
  1862. * TRUE if lpszPath is a Url.
  1863. * FALSE if lpszPath is NULL or not a Url.
  1864. */
  1865. BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
  1866. {
  1867. PARSEDURLA base;
  1868. DWORD res1;
  1869. if (!lpstrPath || !*lpstrPath) return FALSE;
  1870. /* get protocol */
  1871. base.cbSize = sizeof(base);
  1872. res1 = ParseURLA(lpstrPath, &base);
  1873. return (base.nScheme != URL_SCHEME_INVALID);
  1874. }
  1875. /*************************************************************************
  1876. * PathIsURLW [SHLWAPI.@]
  1877. *
  1878. * See PathIsURLA.
  1879. */
  1880. BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
  1881. {
  1882. PARSEDURLW base;
  1883. DWORD res1;
  1884. if (!lpstrPath || !*lpstrPath) return FALSE;
  1885. /* get protocol */
  1886. base.cbSize = sizeof(base);
  1887. res1 = ParseURLW(lpstrPath, &base);
  1888. return (base.nScheme != URL_SCHEME_INVALID);
  1889. }
  1890. /*************************************************************************
  1891. * UrlCreateFromPathA [SHLWAPI.@]
  1892. *
  1893. * See UrlCreateFromPathW
  1894. */
  1895. HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
  1896. {
  1897. WCHAR bufW[INTERNET_MAX_URL_LENGTH];
  1898. WCHAR *urlW = bufW;
  1899. UNICODE_STRING pathW;
  1900. HRESULT ret;
  1901. DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
  1902. if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
  1903. return E_INVALIDARG;
  1904. if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
  1905. urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
  1906. ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
  1907. }
  1908. if(ret == S_OK || ret == S_FALSE) {
  1909. RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
  1910. if(*pcchUrl > lenA) {
  1911. RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
  1912. pszUrl[lenA] = 0;
  1913. *pcchUrl = lenA;
  1914. } else {
  1915. *pcchUrl = lenA + 1;
  1916. ret = E_POINTER;
  1917. }
  1918. }
  1919. if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
  1920. RtlFreeUnicodeString(&pathW);
  1921. return ret;
  1922. }
  1923. /*************************************************************************
  1924. * UrlCreateFromPathW [SHLWAPI.@]
  1925. *
  1926. * Create a Url from a file path.
  1927. *
  1928. * PARAMS
  1929. * pszPath [I] Path to convert
  1930. * pszUrl [O] Destination for the converted Url
  1931. * pcchUrl [I/O] Length of pszUrl
  1932. * dwReserved [I] Reserved, must be 0
  1933. *
  1934. * RETURNS
  1935. * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
  1936. * Failure: An HRESULT error code.
  1937. */
  1938. HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
  1939. {
  1940. DWORD needed;
  1941. HRESULT ret;
  1942. WCHAR *pszNewUrl;
  1943. WCHAR file_colonW[] = {'f','i','l','e',':',0};
  1944. WCHAR three_slashesW[] = {'/','/','/',0};
  1945. PARSEDURLW parsed_url;
  1946. TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
  1947. /* Validate arguments */
  1948. if (dwReserved != 0)
  1949. return E_INVALIDARG;
  1950. if (!pszUrl || !pcchUrl)
  1951. return E_INVALIDARG;
  1952. parsed_url.cbSize = sizeof(parsed_url);
  1953. if(ParseURLW(pszPath, &parsed_url) == S_OK) {
  1954. if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
  1955. needed = strlenW(pszPath);
  1956. if (needed >= *pcchUrl) {
  1957. *pcchUrl = needed + 1;
  1958. return E_POINTER;
  1959. } else {
  1960. *pcchUrl = needed;
  1961. strcpyW(pszUrl, pszPath);
  1962. return S_FALSE;
  1963. }
  1964. }
  1965. }
  1966. pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
  1967. strcpyW(pszNewUrl, file_colonW);
  1968. if(isalphaW(pszPath[0]) && pszPath[1] == ':')
  1969. strcatW(pszNewUrl, three_slashesW);
  1970. strcatW(pszNewUrl, pszPath);
  1971. ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
  1972. HeapFree(GetProcessHeap(), 0, pszNewUrl);
  1973. return ret;
  1974. }
  1975. /*************************************************************************
  1976. * SHAutoComplete [SHLWAPI.@]
  1977. *
  1978. * Enable auto-completion for an edit control.
  1979. *
  1980. * PARAMS
  1981. * hwndEdit [I] Handle of control to enable auto-completion for
  1982. * dwFlags [I] SHACF_ flags from "shlwapi.h"
  1983. *
  1984. * RETURNS
  1985. * Success: S_OK. Auto-completion is enabled for the control.
  1986. * Failure: An HRESULT error code indicating the error.
  1987. */
  1988. HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
  1989. {
  1990. FIXME("SHAutoComplete stub\n");
  1991. return S_FALSE;
  1992. }
  1993. /*************************************************************************
  1994. * MLBuildResURLA [SHLWAPI.405]
  1995. *
  1996. * Create a Url pointing to a resource in a module.
  1997. *
  1998. * PARAMS
  1999. * lpszLibName [I] Name of the module containing the resource
  2000. * hMod [I] Callers module handle
  2001. * dwFlags [I] Undocumented flags for loading the module
  2002. * lpszRes [I] Resource name
  2003. * lpszDest [O] Destination for resulting Url
  2004. * dwDestLen [I] Length of lpszDest
  2005. *
  2006. * RETURNS
  2007. * Success: S_OK. lpszDest constains the resource Url.
  2008. * Failure: E_INVALIDARG, if any argument is invalid, or
  2009. * E_FAIL if dwDestLen is too small.
  2010. */
  2011. HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
  2012. LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
  2013. {
  2014. WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
  2015. HRESULT hRet;
  2016. if (lpszLibName)
  2017. MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
  2018. if (lpszRes)
  2019. MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
  2020. if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
  2021. dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
  2022. hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
  2023. lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
  2024. if (SUCCEEDED(hRet) && lpszDest)
  2025. WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
  2026. return hRet;
  2027. }
  2028. /*************************************************************************
  2029. * MLBuildResURLA [SHLWAPI.406]
  2030. *
  2031. * See MLBuildResURLA.
  2032. */
  2033. HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
  2034. LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
  2035. {
  2036. static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
  2037. #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
  2038. HRESULT hRet = E_FAIL;
  2039. TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
  2040. debugstr_w(lpszRes), lpszDest, dwDestLen);
  2041. if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
  2042. !lpszDest || (dwFlags && dwFlags != 2))
  2043. return E_INVALIDARG;
  2044. if (dwDestLen >= szResLen + 1)
  2045. {
  2046. dwDestLen -= (szResLen + 1);
  2047. memcpy(lpszDest, szRes, sizeof(szRes));
  2048. hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
  2049. if (hMod)
  2050. {
  2051. WCHAR szBuff[MAX_PATH];
  2052. DWORD len;
  2053. len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
  2054. if (len && len < sizeof(szBuff)/sizeof(WCHAR))
  2055. {
  2056. DWORD dwPathLen = strlenW(szBuff) + 1;
  2057. if (dwDestLen >= dwPathLen)
  2058. {
  2059. DWORD dwResLen;
  2060. dwDestLen -= dwPathLen;
  2061. memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
  2062. dwResLen = strlenW(lpszRes) + 1;
  2063. if (dwDestLen >= dwResLen + 1)
  2064. {
  2065. lpszDest[szResLen + dwPathLen + dwResLen] = '/';
  2066. memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
  2067. hRet = S_OK;
  2068. }
  2069. }
  2070. }
  2071. MLFreeLibrary(hMod);
  2072. }
  2073. }
  2074. return hRet;
  2075. }