comctl32/mru: In EnumMRUListA, ensure that the string is NULL terminated and the...
[wine] / dlls / urlmon / umon.c
1 /*
2  * UrlMon
3  *
4  * Copyright 1999 Ulrich Czekalla for Corel Corporation
5  * Copyright 2002 Huw D M Davies for CodeWeavers
6  * Copyright 2005 Jacek Caban for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "urlmon_main.h"
24
25 #include "winreg.h"
26 #include "shlwapi.h"
27
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
31
32 typedef struct {
33     const IMonikerVtbl *lpIMonikerVtbl;
34
35     LONG ref;
36
37     LPOLESTR URLName; /* URL string identified by this URLmoniker */
38 } URLMoniker;
39
40 #define MONIKER_THIS(iface) DEFINE_THIS(URLMoniker, IMoniker, iface)
41
42 static HRESULT WINAPI URLMoniker_QueryInterface(IMoniker *iface, REFIID riid, void **ppv)
43 {
44     URLMoniker *This = MONIKER_THIS(iface);
45
46     if(!ppv)
47         return E_INVALIDARG;
48
49     if(IsEqualIID(&IID_IUnknown, riid)) {
50         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
51         *ppv = iface;
52     }else if(IsEqualIID(&IID_IPersist, riid)) {
53         TRACE("(%p)->(IID_IPersist %p)\n", This, ppv);
54         *ppv = iface;
55     }else if(IsEqualIID(&IID_IPersistStream,riid)) {
56         TRACE("(%p)->(IID_IPersistStream %p)\n", This, ppv);
57         *ppv = iface;
58     }else if(IsEqualIID(&IID_IMoniker, riid)) {
59         TRACE("(%p)->(IID_IMoniker %p)\n", This, ppv);
60         *ppv = iface;
61     }else if(IsEqualIID(&IID_IAsyncMoniker, riid)) {
62         TRACE("(%p)->(IID_IAsyncMoniker %p)\n", This, ppv);
63         *ppv = iface;
64     }else {
65         WARN("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
66         *ppv = NULL;
67         return E_NOINTERFACE;
68     }
69
70     IMoniker_AddRef((IUnknown*)*ppv);
71     return S_OK;
72 }
73
74 static ULONG WINAPI URLMoniker_AddRef(IMoniker *iface)
75 {
76     URLMoniker *This = MONIKER_THIS(iface);
77     ULONG refCount = InterlockedIncrement(&This->ref);
78
79     TRACE("(%p) ref=%u\n",This, refCount);
80
81     return refCount;
82 }
83
84 static ULONG WINAPI URLMoniker_Release(IMoniker *iface)
85 {
86     URLMoniker *This = MONIKER_THIS(iface);
87     ULONG refCount = InterlockedDecrement(&This->ref);
88
89     TRACE("(%p) ref=%u\n",This, refCount);
90
91     if (!refCount) {
92         heap_free(This->URLName);
93         heap_free(This);
94
95         URLMON_UnlockModule();
96     }
97
98     return refCount;
99 }
100
101 static HRESULT WINAPI URLMoniker_GetClassID(IMoniker *iface, CLSID *pClassID)
102 {
103     URLMoniker *This = MONIKER_THIS(iface);
104
105     TRACE("(%p,%p)\n", This, pClassID);
106
107     if(!pClassID)
108         return E_POINTER;
109
110     /* Windows always returns CLSID_StdURLMoniker */
111     *pClassID = CLSID_StdURLMoniker;
112     return S_OK;
113 }
114
115 static HRESULT WINAPI URLMoniker_IsDirty(IMoniker *iface)
116 {
117     URLMoniker *This = MONIKER_THIS(iface);
118
119     TRACE("(%p)\n",This);
120
121     /* Note that the OLE-provided implementations of the IPersistStream::IsDirty
122        method in the OLE-provided moniker interfaces always return S_FALSE because
123        their internal state never changes. */
124     return S_FALSE;
125 }
126
127 static HRESULT WINAPI URLMoniker_Load(IMoniker* iface,IStream* pStm)
128 {
129     URLMoniker *This = MONIKER_THIS(iface);
130     HRESULT res;
131     ULONG size;
132     ULONG got;
133
134     TRACE("(%p,%p)\n",This,pStm);
135
136     if(!pStm)
137         return E_INVALIDARG;
138
139     /*
140      * NOTE
141      *  Writes a ULONG containing length of unicode string, followed
142      *  by that many unicode characters
143      */
144     res = IStream_Read(pStm, &size, sizeof(ULONG), &got);
145     if(SUCCEEDED(res)) {
146         if(got == sizeof(ULONG)) {
147             heap_free(This->URLName);
148             This->URLName = heap_alloc(size);
149             if(!This->URLName)
150                 res = E_OUTOFMEMORY;
151             else {
152                 res = IStream_Read(pStm, This->URLName, size, NULL);
153                 This->URLName[size/sizeof(WCHAR) - 1] = 0;
154             }
155         }
156         else
157             res = E_FAIL;
158     }
159
160     return res;
161 }
162
163 static HRESULT WINAPI URLMoniker_Save(IMoniker *iface, IStream* pStm, BOOL fClearDirty)
164 {
165     URLMoniker *This = MONIKER_THIS(iface);
166     HRESULT res;
167     ULONG size;
168
169     TRACE("(%p,%p,%d)\n", This, pStm, fClearDirty);
170
171     if(!pStm)
172         return E_INVALIDARG;
173
174     size = (strlenW(This->URLName) + 1)*sizeof(WCHAR);
175     res=IStream_Write(pStm,&size,sizeof(ULONG),NULL);
176     if(SUCCEEDED(res))
177         res=IStream_Write(pStm,This->URLName,size,NULL);
178
179     return res;
180
181 }
182
183 static HRESULT WINAPI URLMoniker_GetSizeMax(IMoniker* iface, ULARGE_INTEGER *pcbSize)
184 {
185     URLMoniker *This = MONIKER_THIS(iface);
186
187     TRACE("(%p,%p)\n",This,pcbSize);
188
189     if(!pcbSize)
190         return E_INVALIDARG;
191
192     pcbSize->QuadPart = sizeof(ULONG) + ((strlenW(This->URLName)+1) * sizeof(WCHAR));
193     return S_OK;
194 }
195
196 static HRESULT WINAPI URLMoniker_BindToObject(IMoniker *iface, IBindCtx* pbc, IMoniker *pmkToLeft,
197         REFIID riid, void **ppv)
198 {
199     URLMoniker *This = MONIKER_THIS(iface);
200     IRunningObjectTable *obj_tbl;
201     HRESULT hres;
202
203     TRACE("(%p)->(%p,%p,%s,%p): stub\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppv);
204
205     hres = IBindCtx_GetRunningObjectTable(pbc, &obj_tbl);
206     if(SUCCEEDED(hres)) {
207         FIXME("use running object table\n");
208         IRunningObjectTable_Release(obj_tbl);
209     }
210
211     return bind_to_object(iface, This->URLName, pbc, riid, ppv);
212 }
213
214 static HRESULT WINAPI URLMoniker_BindToStorage(IMoniker* iface, IBindCtx* pbc,
215         IMoniker* pmkToLeft, REFIID riid, void **ppvObject)
216 {
217     URLMoniker *This = MONIKER_THIS(iface);
218
219     TRACE("(%p)->(%p %p %s %p)\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppvObject);
220
221     if(pmkToLeft)
222         FIXME("Unsupported pmkToLeft\n");
223
224     return bind_to_storage(This->URLName, pbc, riid, ppvObject);
225 }
226
227 static HRESULT WINAPI URLMoniker_Reduce(IMoniker *iface, IBindCtx *pbc,
228         DWORD dwReduceHowFar, IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
229 {
230     URLMoniker *This = MONIKER_THIS(iface);
231     
232     TRACE("(%p,%p,%d,%p,%p)\n", This, pbc, dwReduceHowFar, ppmkToLeft, ppmkReduced);
233
234     if(!ppmkReduced)
235         return E_INVALIDARG;
236
237     IMoniker_AddRef(iface);
238     *ppmkReduced = iface;
239     return MK_S_REDUCED_TO_SELF;
240 }
241
242 static HRESULT WINAPI URLMoniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
243         BOOL fOnlyIfNotGeneric, IMoniker **ppmkComposite)
244 {
245     URLMoniker *This = MONIKER_THIS(iface);
246     FIXME("(%p)->(%p,%d,%p): stub\n",This,pmkRight,fOnlyIfNotGeneric,ppmkComposite);
247     return E_NOTIMPL;
248 }
249
250 static HRESULT WINAPI URLMoniker_Enum(IMoniker *iface, BOOL fForward, IEnumMoniker **ppenumMoniker)
251 {
252     URLMoniker *This = MONIKER_THIS(iface);
253
254     TRACE("(%p,%d,%p)\n", This, fForward, ppenumMoniker);
255
256     if(!ppenumMoniker)
257         return E_INVALIDARG;
258
259     /* Does not support sub-monikers */
260     *ppenumMoniker = NULL;
261     return S_OK;
262 }
263
264 static HRESULT WINAPI URLMoniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
265 {
266     URLMoniker *This = MONIKER_THIS(iface);
267     CLSID clsid;
268     LPOLESTR urlPath;
269     IBindCtx* bind;
270     HRESULT res;
271
272     TRACE("(%p,%p)\n",This, pmkOtherMoniker);
273
274     if(pmkOtherMoniker==NULL)
275         return E_INVALIDARG;
276
277     IMoniker_GetClassID(pmkOtherMoniker,&clsid);
278
279     if(!IsEqualCLSID(&clsid,&CLSID_StdURLMoniker))
280         return S_FALSE;
281
282     res = CreateBindCtx(0,&bind);
283     if(FAILED(res))
284         return res;
285
286     res = S_FALSE;
287     if(SUCCEEDED(IMoniker_GetDisplayName(pmkOtherMoniker,bind,NULL,&urlPath))) {
288         int result = lstrcmpiW(urlPath, This->URLName);
289         CoTaskMemFree(urlPath);
290         if(result == 0)
291             res = S_OK;
292     }
293     IUnknown_Release(bind);
294     return res;
295 }
296
297
298 static HRESULT WINAPI URLMoniker_Hash(IMoniker *iface, DWORD *pdwHash)
299 {
300     URLMoniker *This = MONIKER_THIS(iface);
301     int  h = 0,i,skip,len;
302     int  off = 0;
303     LPOLESTR val;
304
305     TRACE("(%p,%p)\n",This,pdwHash);
306
307     if(!pdwHash)
308         return E_INVALIDARG;
309
310     val = This->URLName;
311     len = lstrlenW(val);
312
313     if(len < 16) {
314         for(i = len ; i > 0; i--) {
315             h = (h * 37) + val[off++];
316         }
317     }else {
318         /* only sample some characters */
319         skip = len / 8;
320         for(i = len; i > 0; i -= skip, off += skip) {
321             h = (h * 39) + val[off];
322         }
323     }
324     *pdwHash = h;
325     return S_OK;
326 }
327
328 static HRESULT WINAPI URLMoniker_IsRunning(IMoniker* iface, IBindCtx* pbc,
329         IMoniker *pmkToLeft, IMoniker *pmkNewlyRunning)
330 {
331     URLMoniker *This = MONIKER_THIS(iface);
332     FIXME("(%p)->(%p,%p,%p): stub\n",This,pbc,pmkToLeft,pmkNewlyRunning);
333     return E_NOTIMPL;
334 }
335
336 static HRESULT WINAPI URLMoniker_GetTimeOfLastChange(IMoniker *iface,
337         IBindCtx *pbc, IMoniker *pmkToLeft, FILETIME *pFileTime)
338 {
339     URLMoniker *This = MONIKER_THIS(iface);
340     FIXME("(%p)->(%p,%p,%p): stub\n", This, pbc, pmkToLeft, pFileTime);
341     return E_NOTIMPL;
342 }
343
344 static HRESULT WINAPI URLMoniker_Inverse(IMoniker *iface, IMoniker **ppmk)
345 {
346     URLMoniker *This = MONIKER_THIS(iface);
347     TRACE("(%p,%p)\n",This,ppmk);
348     return MK_E_NOINVERSE;
349 }
350
351 static HRESULT WINAPI URLMoniker_CommonPrefixWith(IMoniker *iface, IMoniker *pmkOther, IMoniker **ppmkPrefix)
352 {
353     URLMoniker *This = MONIKER_THIS(iface);
354     FIXME("(%p)->(%p,%p): stub\n",This,pmkOther,ppmkPrefix);
355     return E_NOTIMPL;
356 }
357
358 static HRESULT WINAPI URLMoniker_RelativePathTo(IMoniker *iface, IMoniker *pmOther, IMoniker **ppmkRelPath)
359 {
360     URLMoniker *This = MONIKER_THIS(iface);
361     FIXME("(%p)->(%p,%p): stub\n",This,pmOther,ppmkRelPath);
362     return E_NOTIMPL;
363 }
364
365 static HRESULT WINAPI URLMoniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
366         LPOLESTR *ppszDisplayName)
367 {
368     URLMoniker *This = MONIKER_THIS(iface);
369     int len;
370     
371     TRACE("(%p,%p,%p,%p)\n", This, pbc, pmkToLeft, ppszDisplayName);
372     
373     if(!ppszDisplayName)
374         return E_INVALIDARG;
375
376     if(!This->URLName)
377         return E_OUTOFMEMORY;
378
379     /* FIXME: If this is a partial URL, try and get a URL moniker from SZ_URLCONTEXT in the bind context,
380         then look at pmkToLeft to try and complete the URL
381     */
382     len = lstrlenW(This->URLName)+1;
383     *ppszDisplayName = CoTaskMemAlloc(len*sizeof(WCHAR));
384     if(!*ppszDisplayName)
385         return E_OUTOFMEMORY;
386     lstrcpyW(*ppszDisplayName, This->URLName);
387     return S_OK;
388 }
389
390 static HRESULT WINAPI URLMoniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
391         LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
392 {
393     URLMoniker *This = MONIKER_THIS(iface);
394     FIXME("(%p)->(%p,%p,%p,%p,%p): stub\n",This,pbc,pmkToLeft,pszDisplayName,pchEaten,ppmkOut);
395     return E_NOTIMPL;
396 }
397
398 static HRESULT WINAPI URLMoniker_IsSystemMoniker(IMoniker *iface, DWORD *pwdMksys)
399 {
400     URLMoniker *This = MONIKER_THIS(iface);
401
402     TRACE("(%p,%p)\n",This,pwdMksys);
403
404     if(!pwdMksys)
405         return E_INVALIDARG;
406
407     *pwdMksys = MKSYS_URLMONIKER;
408     return S_OK;
409 }
410
411 static const IMonikerVtbl URLMonikerVtbl =
412 {
413     URLMoniker_QueryInterface,
414     URLMoniker_AddRef,
415     URLMoniker_Release,
416     URLMoniker_GetClassID,
417     URLMoniker_IsDirty,
418     URLMoniker_Load,
419     URLMoniker_Save,
420     URLMoniker_GetSizeMax,
421     URLMoniker_BindToObject,
422     URLMoniker_BindToStorage,
423     URLMoniker_Reduce,
424     URLMoniker_ComposeWith,
425     URLMoniker_Enum,
426     URLMoniker_IsEqual,
427     URLMoniker_Hash,
428     URLMoniker_IsRunning,
429     URLMoniker_GetTimeOfLastChange,
430     URLMoniker_Inverse,
431     URLMoniker_CommonPrefixWith,
432     URLMoniker_RelativePathTo,
433     URLMoniker_GetDisplayName,
434     URLMoniker_ParseDisplayName,
435     URLMoniker_IsSystemMoniker
436 };
437
438 static URLMoniker *alloc_moniker(void)
439 {
440     URLMoniker *ret;
441
442     ret = heap_alloc(sizeof(URLMoniker));
443     if(!ret)
444         return NULL;
445
446     ret->lpIMonikerVtbl = &URLMonikerVtbl;
447     ret->ref = 1;
448     ret->URLName = NULL;
449
450     return ret;
451 }
452
453 static HRESULT URLMoniker_Init(URLMoniker *This, LPCOLESTR lpszLeftURLName, LPCOLESTR lpszURLName)
454 {
455     HRESULT hres;
456     DWORD sizeStr = 0;
457
458     TRACE("(%p,%s,%s)\n",This,debugstr_w(lpszLeftURLName),debugstr_w(lpszURLName));
459
460     This->URLName = heap_alloc(INTERNET_MAX_URL_LENGTH*sizeof(WCHAR));
461
462     if(lpszLeftURLName)
463         hres = CoInternetCombineUrl(lpszLeftURLName, lpszURLName, URL_FILE_USE_PATHURL,
464                 This->URLName, INTERNET_MAX_URL_LENGTH, &sizeStr, 0);
465     else
466         hres = CoInternetParseUrl(lpszURLName, PARSE_CANONICALIZE, URL_FILE_USE_PATHURL,
467                 This->URLName, INTERNET_MAX_URL_LENGTH, &sizeStr, 0);
468
469     if(FAILED(hres)) {
470         heap_free(This->URLName);
471         return hres;
472     }
473
474     URLMON_LockModule();
475
476     if(sizeStr != INTERNET_MAX_URL_LENGTH)
477         This->URLName = heap_realloc(This->URLName, (sizeStr+1)*sizeof(WCHAR));
478
479     TRACE("URLName = %s\n", debugstr_w(This->URLName));
480
481     return S_OK;
482 }
483
484 HRESULT StdURLMoniker_Construct(IUnknown *outer, void **ppv)
485 {
486     TRACE("(%p %p)\n", outer, ppv);
487
488     *ppv = alloc_moniker();
489     return *ppv ? S_OK : E_OUTOFMEMORY;
490 }
491
492 /***********************************************************************
493  *           CreateURLMonikerEx (URLMON.@)
494  *
495  * Create a url moniker.
496  *
497  * PARAMS
498  *    pmkContext [I] Context
499  *    szURL      [I] Url to create the moniker for
500  *    ppmk       [O] Destination for created moniker.
501  *    dwFlags    [I] Flags.
502  *
503  * RETURNS
504  *    Success: S_OK. ppmk contains the created IMoniker object.
505  *    Failure: MK_E_SYNTAX if szURL is not a valid url, or
506  *             E_OUTOFMEMORY if memory allocation fails.
507  */
508 HRESULT WINAPI CreateURLMonikerEx(IMoniker *pmkContext, LPCWSTR szURL, IMoniker **ppmk, DWORD dwFlags)
509 {
510     URLMoniker *obj;
511     HRESULT hres;
512     LPOLESTR lefturl = NULL;
513
514     TRACE("(%p, %s, %p, %08x)\n", pmkContext, debugstr_w(szURL), ppmk, dwFlags);
515
516     if (dwFlags & URL_MK_UNIFORM) FIXME("ignoring flag URL_MK_UNIFORM\n");
517
518     if(!(obj = alloc_moniker()))
519         return E_OUTOFMEMORY;
520
521     if(pmkContext) {
522         IBindCtx* bind;
523         DWORD dwMksys = 0;
524         IMoniker_IsSystemMoniker(pmkContext, &dwMksys);
525         if(dwMksys == MKSYS_URLMONIKER && SUCCEEDED(CreateBindCtx(0, &bind))) {
526             IMoniker_GetDisplayName(pmkContext, bind, NULL, &lefturl);
527             TRACE("lefturl = %s\n", debugstr_w(lefturl));
528             IBindCtx_Release(bind);
529         }
530     }
531         
532     hres = URLMoniker_Init(obj, lefturl, szURL);
533     CoTaskMemFree(lefturl);
534     if(SUCCEEDED(hres))
535         hres = URLMoniker_QueryInterface((IMoniker*)obj, &IID_IMoniker, (void**)ppmk);
536     IMoniker_Release((IMoniker*)obj);
537     return hres;
538 }
539
540 /**********************************************************************
541  *           CreateURLMoniker (URLMON.@)
542  *
543  * Create a url moniker.
544  *
545  * PARAMS
546  *    pmkContext [I] Context
547  *    szURL      [I] Url to create the moniker for
548  *    ppmk       [O] Destination for created moniker.
549  *
550  * RETURNS
551  *    Success: S_OK. ppmk contains the created IMoniker object.
552  *    Failure: MK_E_SYNTAX if szURL is not a valid url, or
553  *             E_OUTOFMEMORY if memory allocation fails.
554  */
555 HRESULT WINAPI CreateURLMoniker(IMoniker *pmkContext, LPCWSTR szURL, IMoniker **ppmk)
556 {
557     return CreateURLMonikerEx(pmkContext, szURL, ppmk, URL_MK_LEGACY);
558 }
559
560 /***********************************************************************
561  *           IsAsyncMoniker (URLMON.@)
562  */
563 HRESULT WINAPI IsAsyncMoniker(IMoniker *pmk)
564 {
565     IUnknown *am;
566     
567     TRACE("(%p)\n", pmk);
568     if(!pmk)
569         return E_INVALIDARG;
570     if(SUCCEEDED(IMoniker_QueryInterface(pmk, &IID_IAsyncMoniker, (void**)&am))) {
571         IUnknown_Release(am);
572         return S_OK;
573     }
574     return S_FALSE;
575 }
576
577 /***********************************************************************
578  *           BindAsyncMoniker (URLMON.@)
579  *
580  * Bind a bind status callback to an asynchronous URL Moniker.
581  *
582  * PARAMS
583  *  pmk           [I] Moniker object to bind status callback to
584  *  grfOpt        [I] Options, seems not used
585  *  pbsc          [I] Status callback to bind
586  *  iidResult     [I] Interface to return
587  *  ppvResult     [O] Resulting asynchronous moniker object
588  *
589  * RETURNS
590  *    Success: S_OK.
591  *    Failure: E_INVALIDARG, if any argument is invalid, or
592  *             E_OUTOFMEMORY if memory allocation fails.
593  */
594 HRESULT WINAPI BindAsyncMoniker(IMoniker *pmk, DWORD grfOpt, IBindStatusCallback *pbsc, REFIID iidResult, LPVOID *ppvResult)
595 {
596     LPBC pbc = NULL;
597     HRESULT hr = E_INVALIDARG;
598
599     TRACE("(%p %08x %p %s %p)\n", pmk, grfOpt, pbsc, debugstr_guid(iidResult), ppvResult);
600
601     if (pmk && ppvResult)
602     {
603         *ppvResult = NULL;
604
605         hr = CreateAsyncBindCtx(0, pbsc, NULL, &pbc);
606         if (hr == NOERROR)
607         {
608             hr = IMoniker_BindToObject(pmk, pbc, NULL, iidResult, ppvResult);
609             IBindCtx_Release(pbc);
610         }
611     }
612     return hr;
613 }
614
615 /***********************************************************************
616  *           MkParseDisplayNameEx (URLMON.@)
617  */
618 HRESULT WINAPI MkParseDisplayNameEx(IBindCtx *pbc, LPCWSTR szDisplayName, ULONG *pchEaten, LPMONIKER *ppmk)
619 {
620     TRACE("(%p %s %p %p)\n", pbc, debugstr_w(szDisplayName), pchEaten, ppmk);
621
622     if(is_registered_protocol(szDisplayName)) {
623         HRESULT hres;
624
625         hres = CreateURLMoniker(NULL, szDisplayName, ppmk);
626         if(SUCCEEDED(hres)) {
627             *pchEaten = strlenW(szDisplayName);
628             return hres;
629         }
630     }
631
632     return MkParseDisplayName(pbc, szDisplayName, pchEaten, ppmk);
633 }
634
635
636 /***********************************************************************
637  *           URLDownloadToCacheFileA (URLMON.@)
638  */
639 HRESULT WINAPI URLDownloadToCacheFileA(LPUNKNOWN lpUnkCaller, LPCSTR szURL, LPSTR szFileName,
640         DWORD dwBufLength, DWORD dwReserved, LPBINDSTATUSCALLBACK pBSC)
641 {
642     LPWSTR url = NULL, file_name = NULL;
643     int len;
644     HRESULT hres;
645
646     TRACE("(%p %s %p %d %d %p)\n", lpUnkCaller, debugstr_a(szURL), szFileName,
647             dwBufLength, dwReserved, pBSC);
648
649     if(szURL) {
650         len = MultiByteToWideChar(CP_ACP, 0, szURL, -1, NULL, 0);
651         url = heap_alloc(len*sizeof(WCHAR));
652         MultiByteToWideChar(CP_ACP, 0, szURL, -1, url, len);
653     }
654
655     if(szFileName)
656         file_name = heap_alloc(dwBufLength*sizeof(WCHAR));
657
658     hres = URLDownloadToCacheFileW(lpUnkCaller, url, file_name, dwBufLength*sizeof(WCHAR),
659             dwReserved, pBSC);
660
661     if(SUCCEEDED(hres) && file_name)
662         WideCharToMultiByte(CP_ACP, 0, file_name, -1, szFileName, dwBufLength, NULL, NULL);
663
664     heap_free(url);
665     heap_free(file_name);
666
667     return hres;
668 }
669
670 /***********************************************************************
671  *           URLDownloadToCacheFileW (URLMON.@)
672  */
673 HRESULT WINAPI URLDownloadToCacheFileW(LPUNKNOWN lpUnkCaller, LPCWSTR szURL, LPWSTR szFileName,
674                 DWORD dwBufLength, DWORD dwReserved, LPBINDSTATUSCALLBACK pBSC)
675 {
676     WCHAR cache_path[MAX_PATH + 1];
677     FILETIME expire, modified;
678     HRESULT hr;
679     LPWSTR ext;
680
681     static WCHAR header[] = {
682         'H','T','T','P','/','1','.','0',' ','2','0','0',' ',
683         'O','K','\\','r','\\','n','\\','r','\\','n',0
684     };
685
686     TRACE("(%p, %s, %p, %d, %d, %p)\n", lpUnkCaller, debugstr_w(szURL),
687           szFileName, dwBufLength, dwReserved, pBSC);
688
689     if (!szURL || !szFileName)
690         return E_INVALIDARG;
691
692     ext = PathFindExtensionW(szURL);
693
694     if (!CreateUrlCacheEntryW(szURL, 0, ext, cache_path, 0))
695         return E_FAIL;
696
697     hr = URLDownloadToFileW(lpUnkCaller, szURL, cache_path, 0, pBSC);
698     if (FAILED(hr))
699         return hr;
700
701     expire.dwHighDateTime = 0;
702     expire.dwLowDateTime = 0;
703     modified.dwHighDateTime = 0;
704     modified.dwLowDateTime = 0;
705
706     if (!CommitUrlCacheEntryW(szURL, cache_path, expire, modified, NORMAL_CACHE_ENTRY,
707                               header, sizeof(header), NULL, NULL))
708         return E_FAIL;
709
710     if (strlenW(cache_path) > dwBufLength)
711         return E_OUTOFMEMORY;
712
713     lstrcpyW(szFileName, cache_path);
714
715     return S_OK;
716 }
717
718 /***********************************************************************
719  *           HlinkSimpleNavigateToMoniker (URLMON.@)
720  */
721 HRESULT WINAPI HlinkSimpleNavigateToMoniker(IMoniker *pmkTarget,
722     LPCWSTR szLocation, LPCWSTR szTargetFrameName, IUnknown *pUnk,
723     IBindCtx *pbc, IBindStatusCallback *pbsc, DWORD grfHLNF, DWORD dwReserved)
724 {
725     FIXME("stub\n");
726     return E_NOTIMPL;
727 }
728
729 /***********************************************************************
730  *           HlinkSimpleNavigateToString (URLMON.@)
731  */
732 HRESULT WINAPI HlinkSimpleNavigateToString( LPCWSTR szTarget,
733     LPCWSTR szLocation, LPCWSTR szTargetFrameName, IUnknown *pUnk,
734     IBindCtx *pbc, IBindStatusCallback *pbsc, DWORD grfHLNF, DWORD dwReserved)
735 {
736     FIXME("%s\n", debugstr_w( szTarget ) );
737     return E_NOTIMPL;
738 }
739
740 /***********************************************************************
741  *           HlinkNavigateString (URLMON.@)
742  */
743 HRESULT WINAPI HlinkNavigateString( IUnknown *pUnk, LPCWSTR szTarget )
744 {
745     TRACE("%p %s\n", pUnk, debugstr_w( szTarget ) );
746     return HlinkSimpleNavigateToString( 
747                szTarget, NULL, NULL, pUnk, NULL, NULL, 0, 0 );
748 }
749
750 /***********************************************************************
751  *           GetSoftwareUpdateInfo (URLMON.@)
752  */
753 HRESULT WINAPI GetSoftwareUpdateInfo( LPCWSTR szDistUnit, LPSOFTDISTINFO psdi )
754 {
755     FIXME("%s %p\n", debugstr_w(szDistUnit), psdi );
756     return E_FAIL;
757 }