urlmon: Call remove_dot_segments on path opaque for mk: protocol.
[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 #include "hlink.h"
28 #include "shellapi.h"
29
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
33
34 typedef struct {
35     IMoniker      IMoniker_iface;
36     IUriContainer IUriContainer_iface;
37
38     LONG ref;
39
40     LPOLESTR URLName; /* URL string identified by this URLmoniker */
41 } URLMoniker;
42
43 static inline URLMoniker *impl_from_IMoniker(IMoniker *iface)
44 {
45     return CONTAINING_RECORD(iface, URLMoniker, IMoniker_iface);
46 }
47
48 static HRESULT WINAPI URLMoniker_QueryInterface(IMoniker *iface, REFIID riid, void **ppv)
49 {
50     URLMoniker *This = impl_from_IMoniker(iface);
51
52     if(!ppv)
53         return E_INVALIDARG;
54
55     if(IsEqualIID(&IID_IUnknown, riid)) {
56         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
57         *ppv = iface;
58     }else if(IsEqualIID(&IID_IPersist, riid)) {
59         TRACE("(%p)->(IID_IPersist %p)\n", This, ppv);
60         *ppv = iface;
61     }else if(IsEqualIID(&IID_IPersistStream,riid)) {
62         TRACE("(%p)->(IID_IPersistStream %p)\n", This, ppv);
63         *ppv = iface;
64     }else if(IsEqualIID(&IID_IMoniker, riid)) {
65         TRACE("(%p)->(IID_IMoniker %p)\n", This, ppv);
66         *ppv = iface;
67     }else if(IsEqualIID(&IID_IAsyncMoniker, riid)) {
68         TRACE("(%p)->(IID_IAsyncMoniker %p)\n", This, ppv);
69         *ppv = iface;
70     }else if(IsEqualIID(&IID_IUriContainer, riid)) {
71         TRACE("(%p)->(IID_IUriContainer %p)\n", This, ppv);
72         *ppv = &This->IUriContainer_iface;
73     }else {
74         WARN("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
75         *ppv = NULL;
76         return E_NOINTERFACE;
77     }
78
79     IMoniker_AddRef((IUnknown*)*ppv);
80     return S_OK;
81 }
82
83 static ULONG WINAPI URLMoniker_AddRef(IMoniker *iface)
84 {
85     URLMoniker *This = impl_from_IMoniker(iface);
86     ULONG refCount = InterlockedIncrement(&This->ref);
87
88     TRACE("(%p) ref=%u\n",This, refCount);
89
90     return refCount;
91 }
92
93 static ULONG WINAPI URLMoniker_Release(IMoniker *iface)
94 {
95     URLMoniker *This = impl_from_IMoniker(iface);
96     ULONG refCount = InterlockedDecrement(&This->ref);
97
98     TRACE("(%p) ref=%u\n",This, refCount);
99
100     if (!refCount) {
101         heap_free(This->URLName);
102         heap_free(This);
103
104         URLMON_UnlockModule();
105     }
106
107     return refCount;
108 }
109
110 static HRESULT WINAPI URLMoniker_GetClassID(IMoniker *iface, CLSID *pClassID)
111 {
112     URLMoniker *This = impl_from_IMoniker(iface);
113
114     TRACE("(%p,%p)\n", This, pClassID);
115
116     if(!pClassID)
117         return E_POINTER;
118
119     /* Windows always returns CLSID_StdURLMoniker */
120     *pClassID = CLSID_StdURLMoniker;
121     return S_OK;
122 }
123
124 static HRESULT WINAPI URLMoniker_IsDirty(IMoniker *iface)
125 {
126     URLMoniker *This = impl_from_IMoniker(iface);
127
128     TRACE("(%p)\n",This);
129
130     /* Note that the OLE-provided implementations of the IPersistStream::IsDirty
131        method in the OLE-provided moniker interfaces always return S_FALSE because
132        their internal state never changes. */
133     return S_FALSE;
134 }
135
136 static HRESULT WINAPI URLMoniker_Load(IMoniker* iface,IStream* pStm)
137 {
138     URLMoniker *This = impl_from_IMoniker(iface);
139     HRESULT res;
140     ULONG size;
141     ULONG got;
142
143     TRACE("(%p,%p)\n",This,pStm);
144
145     if(!pStm)
146         return E_INVALIDARG;
147
148     /*
149      * NOTE
150      *  Writes a ULONG containing length of unicode string, followed
151      *  by that many unicode characters
152      */
153     res = IStream_Read(pStm, &size, sizeof(ULONG), &got);
154     if(SUCCEEDED(res)) {
155         if(got == sizeof(ULONG)) {
156             heap_free(This->URLName);
157             This->URLName = heap_alloc(size);
158             if(!This->URLName)
159                 res = E_OUTOFMEMORY;
160             else {
161                 res = IStream_Read(pStm, This->URLName, size, NULL);
162                 This->URLName[size/sizeof(WCHAR) - 1] = 0;
163             }
164         }
165         else
166             res = E_FAIL;
167     }
168
169     return res;
170 }
171
172 static HRESULT WINAPI URLMoniker_Save(IMoniker *iface, IStream* pStm, BOOL fClearDirty)
173 {
174     URLMoniker *This = impl_from_IMoniker(iface);
175     HRESULT res;
176     ULONG size;
177
178     TRACE("(%p,%p,%d)\n", This, pStm, fClearDirty);
179
180     if(!pStm)
181         return E_INVALIDARG;
182
183     size = (strlenW(This->URLName) + 1)*sizeof(WCHAR);
184     res=IStream_Write(pStm,&size,sizeof(ULONG),NULL);
185     if(SUCCEEDED(res))
186         res=IStream_Write(pStm,This->URLName,size,NULL);
187
188     return res;
189
190 }
191
192 static HRESULT WINAPI URLMoniker_GetSizeMax(IMoniker* iface, ULARGE_INTEGER *pcbSize)
193 {
194     URLMoniker *This = impl_from_IMoniker(iface);
195
196     TRACE("(%p,%p)\n",This,pcbSize);
197
198     if(!pcbSize)
199         return E_INVALIDARG;
200
201     pcbSize->QuadPart = sizeof(ULONG) + ((strlenW(This->URLName)+1) * sizeof(WCHAR));
202     return S_OK;
203 }
204
205 static HRESULT WINAPI URLMoniker_BindToObject(IMoniker *iface, IBindCtx* pbc, IMoniker *pmkToLeft,
206         REFIID riid, void **ppv)
207 {
208     URLMoniker *This = impl_from_IMoniker(iface);
209     IRunningObjectTable *obj_tbl;
210     IUri *uri;
211     HRESULT hres;
212
213     TRACE("(%p)->(%p,%p,%s,%p): stub\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppv);
214
215     hres = IBindCtx_GetRunningObjectTable(pbc, &obj_tbl);
216     if(SUCCEEDED(hres)) {
217         FIXME("use running object table\n");
218         IRunningObjectTable_Release(obj_tbl);
219     }
220
221     hres = CreateUri(This->URLName, Uri_CREATE_FILE_USE_DOS_PATH, 0, &uri);
222     if(FAILED(hres))
223         return hres;
224
225     hres = bind_to_object(iface, uri, pbc, riid, ppv);
226
227     IUri_Release(uri);
228     return hres;
229 }
230
231 static HRESULT WINAPI URLMoniker_BindToStorage(IMoniker* iface, IBindCtx* pbc,
232         IMoniker* pmkToLeft, REFIID riid, void **ppvObject)
233 {
234     URLMoniker *This = impl_from_IMoniker(iface);
235     IUri *uri;
236     HRESULT hres;
237
238     TRACE("(%p)->(%p %p %s %p)\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppvObject);
239
240     if(pmkToLeft)
241         FIXME("Unsupported pmkToLeft\n");
242
243     hres = CreateUri(This->URLName, Uri_CREATE_FILE_USE_DOS_PATH, 0, &uri);
244     if(FAILED(hres))
245         return hres;
246
247     hres = bind_to_storage(uri, pbc, riid, ppvObject);
248
249     IUri_Release(uri);
250     return hres;
251 }
252
253 static HRESULT WINAPI URLMoniker_Reduce(IMoniker *iface, IBindCtx *pbc,
254         DWORD dwReduceHowFar, IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
255 {
256     URLMoniker *This = impl_from_IMoniker(iface);
257
258     TRACE("(%p,%p,%d,%p,%p)\n", This, pbc, dwReduceHowFar, ppmkToLeft, ppmkReduced);
259
260     if(!ppmkReduced)
261         return E_INVALIDARG;
262
263     IMoniker_AddRef(iface);
264     *ppmkReduced = iface;
265     return MK_S_REDUCED_TO_SELF;
266 }
267
268 static HRESULT WINAPI URLMoniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
269         BOOL fOnlyIfNotGeneric, IMoniker **ppmkComposite)
270 {
271     URLMoniker *This = impl_from_IMoniker(iface);
272     FIXME("(%p)->(%p,%d,%p): stub\n",This,pmkRight,fOnlyIfNotGeneric,ppmkComposite);
273     return E_NOTIMPL;
274 }
275
276 static HRESULT WINAPI URLMoniker_Enum(IMoniker *iface, BOOL fForward, IEnumMoniker **ppenumMoniker)
277 {
278     URLMoniker *This = impl_from_IMoniker(iface);
279
280     TRACE("(%p,%d,%p)\n", This, fForward, ppenumMoniker);
281
282     if(!ppenumMoniker)
283         return E_INVALIDARG;
284
285     /* Does not support sub-monikers */
286     *ppenumMoniker = NULL;
287     return S_OK;
288 }
289
290 static HRESULT WINAPI URLMoniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
291 {
292     URLMoniker *This = impl_from_IMoniker(iface);
293     CLSID clsid;
294     LPOLESTR urlPath;
295     IBindCtx* bind;
296     HRESULT res;
297
298     TRACE("(%p,%p)\n",This, pmkOtherMoniker);
299
300     if(pmkOtherMoniker==NULL)
301         return E_INVALIDARG;
302
303     IMoniker_GetClassID(pmkOtherMoniker,&clsid);
304
305     if(!IsEqualCLSID(&clsid,&CLSID_StdURLMoniker))
306         return S_FALSE;
307
308     res = CreateBindCtx(0,&bind);
309     if(FAILED(res))
310         return res;
311
312     res = S_FALSE;
313     if(SUCCEEDED(IMoniker_GetDisplayName(pmkOtherMoniker,bind,NULL,&urlPath))) {
314         int result = lstrcmpiW(urlPath, This->URLName);
315         CoTaskMemFree(urlPath);
316         if(result == 0)
317             res = S_OK;
318     }
319     IUnknown_Release(bind);
320     return res;
321 }
322
323
324 static HRESULT WINAPI URLMoniker_Hash(IMoniker *iface, DWORD *pdwHash)
325 {
326     URLMoniker *This = impl_from_IMoniker(iface);
327     int  h = 0,i,skip,len;
328     int  off = 0;
329     LPOLESTR val;
330
331     TRACE("(%p,%p)\n",This,pdwHash);
332
333     if(!pdwHash)
334         return E_INVALIDARG;
335
336     val = This->URLName;
337     len = lstrlenW(val);
338
339     if(len < 16) {
340         for(i = len ; i > 0; i--) {
341             h = (h * 37) + val[off++];
342         }
343     }else {
344         /* only sample some characters */
345         skip = len / 8;
346         for(i = len; i > 0; i -= skip, off += skip) {
347             h = (h * 39) + val[off];
348         }
349     }
350     *pdwHash = h;
351     return S_OK;
352 }
353
354 static HRESULT WINAPI URLMoniker_IsRunning(IMoniker* iface, IBindCtx* pbc,
355         IMoniker *pmkToLeft, IMoniker *pmkNewlyRunning)
356 {
357     URLMoniker *This = impl_from_IMoniker(iface);
358     FIXME("(%p)->(%p,%p,%p): stub\n",This,pbc,pmkToLeft,pmkNewlyRunning);
359     return E_NOTIMPL;
360 }
361
362 static HRESULT WINAPI URLMoniker_GetTimeOfLastChange(IMoniker *iface,
363         IBindCtx *pbc, IMoniker *pmkToLeft, FILETIME *pFileTime)
364 {
365     URLMoniker *This = impl_from_IMoniker(iface);
366     FIXME("(%p)->(%p,%p,%p): stub\n", This, pbc, pmkToLeft, pFileTime);
367     return E_NOTIMPL;
368 }
369
370 static HRESULT WINAPI URLMoniker_Inverse(IMoniker *iface, IMoniker **ppmk)
371 {
372     URLMoniker *This = impl_from_IMoniker(iface);
373     TRACE("(%p,%p)\n",This,ppmk);
374     return MK_E_NOINVERSE;
375 }
376
377 static HRESULT WINAPI URLMoniker_CommonPrefixWith(IMoniker *iface, IMoniker *pmkOther, IMoniker **ppmkPrefix)
378 {
379     URLMoniker *This = impl_from_IMoniker(iface);
380     FIXME("(%p)->(%p,%p): stub\n",This,pmkOther,ppmkPrefix);
381     return E_NOTIMPL;
382 }
383
384 static HRESULT WINAPI URLMoniker_RelativePathTo(IMoniker *iface, IMoniker *pmOther, IMoniker **ppmkRelPath)
385 {
386     URLMoniker *This = impl_from_IMoniker(iface);
387     FIXME("(%p)->(%p,%p): stub\n",This,pmOther,ppmkRelPath);
388     return E_NOTIMPL;
389 }
390
391 static HRESULT WINAPI URLMoniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
392         LPOLESTR *ppszDisplayName)
393 {
394     URLMoniker *This = impl_from_IMoniker(iface);
395     int len;
396     
397     TRACE("(%p,%p,%p,%p)\n", This, pbc, pmkToLeft, ppszDisplayName);
398     
399     if(!ppszDisplayName)
400         return E_INVALIDARG;
401
402     if(!This->URLName)
403         return E_OUTOFMEMORY;
404
405     /* FIXME: If this is a partial URL, try and get a URL moniker from SZ_URLCONTEXT in the bind context,
406         then look at pmkToLeft to try and complete the URL
407     */
408     len = lstrlenW(This->URLName)+1;
409     *ppszDisplayName = CoTaskMemAlloc(len*sizeof(WCHAR));
410     if(!*ppszDisplayName)
411         return E_OUTOFMEMORY;
412     lstrcpyW(*ppszDisplayName, This->URLName);
413     return S_OK;
414 }
415
416 static HRESULT WINAPI URLMoniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc, IMoniker *pmkToLeft,
417         LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
418 {
419     URLMoniker *This = impl_from_IMoniker(iface);
420     FIXME("(%p)->(%p,%p,%p,%p,%p): stub\n",This,pbc,pmkToLeft,pszDisplayName,pchEaten,ppmkOut);
421     return E_NOTIMPL;
422 }
423
424 static HRESULT WINAPI URLMoniker_IsSystemMoniker(IMoniker *iface, DWORD *pwdMksys)
425 {
426     URLMoniker *This = impl_from_IMoniker(iface);
427
428     TRACE("(%p,%p)\n",This,pwdMksys);
429
430     if(!pwdMksys)
431         return E_INVALIDARG;
432
433     *pwdMksys = MKSYS_URLMONIKER;
434     return S_OK;
435 }
436
437 static const IMonikerVtbl URLMonikerVtbl =
438 {
439     URLMoniker_QueryInterface,
440     URLMoniker_AddRef,
441     URLMoniker_Release,
442     URLMoniker_GetClassID,
443     URLMoniker_IsDirty,
444     URLMoniker_Load,
445     URLMoniker_Save,
446     URLMoniker_GetSizeMax,
447     URLMoniker_BindToObject,
448     URLMoniker_BindToStorage,
449     URLMoniker_Reduce,
450     URLMoniker_ComposeWith,
451     URLMoniker_Enum,
452     URLMoniker_IsEqual,
453     URLMoniker_Hash,
454     URLMoniker_IsRunning,
455     URLMoniker_GetTimeOfLastChange,
456     URLMoniker_Inverse,
457     URLMoniker_CommonPrefixWith,
458     URLMoniker_RelativePathTo,
459     URLMoniker_GetDisplayName,
460     URLMoniker_ParseDisplayName,
461     URLMoniker_IsSystemMoniker
462 };
463
464 static inline URLMoniker *impl_from_IUriContainer(IUriContainer *iface)
465 {
466     return CONTAINING_RECORD(iface, URLMoniker, IUriContainer_iface);
467 }
468
469 static HRESULT WINAPI UriContainer_QueryInterface(IUriContainer *iface, REFIID riid, void **ppv)
470 {
471     URLMoniker *This = impl_from_IUriContainer(iface);
472     return IMoniker_QueryInterface(&This->IMoniker_iface, riid, ppv);
473 }
474
475 static ULONG WINAPI UriContainer_AddRef(IUriContainer *iface)
476 {
477     URLMoniker *This = impl_from_IUriContainer(iface);
478     return IMoniker_AddRef(&This->IMoniker_iface);
479 }
480
481 static ULONG WINAPI UriContainer_Release(IUriContainer *iface)
482 {
483     URLMoniker *This = impl_from_IUriContainer(iface);
484     return IMoniker_Release(&This->IMoniker_iface);
485 }
486
487 static HRESULT WINAPI UriContainer_GetIUri(IUriContainer *iface, IUri **ppIUri)
488 {
489     URLMoniker *This = impl_from_IUriContainer(iface);
490
491     FIXME("(%p)->(%p)\n", This, ppIUri);
492
493     *ppIUri = NULL;
494     return S_FALSE;
495 }
496
497 static const IUriContainerVtbl UriContainerVtbl = {
498     UriContainer_QueryInterface,
499     UriContainer_AddRef,
500     UriContainer_Release,
501     UriContainer_GetIUri
502 };
503
504 static URLMoniker *alloc_moniker(void)
505 {
506     URLMoniker *ret;
507
508     ret = heap_alloc(sizeof(URLMoniker));
509     if(!ret)
510         return NULL;
511
512     ret->IMoniker_iface.lpVtbl = &URLMonikerVtbl;
513     ret->IUriContainer_iface.lpVtbl = &UriContainerVtbl;
514     ret->ref = 1;
515     ret->URLName = NULL;
516
517     return ret;
518 }
519
520 static HRESULT URLMoniker_Init(URLMoniker *This, LPCOLESTR lpszLeftURLName, LPCOLESTR lpszURLName)
521 {
522     HRESULT hres;
523     DWORD sizeStr = 0;
524
525     TRACE("(%p,%s,%s)\n",This,debugstr_w(lpszLeftURLName),debugstr_w(lpszURLName));
526
527     This->URLName = heap_alloc(INTERNET_MAX_URL_LENGTH*sizeof(WCHAR));
528
529     if(lpszLeftURLName)
530         hres = CoInternetCombineUrl(lpszLeftURLName, lpszURLName, URL_FILE_USE_PATHURL,
531                 This->URLName, INTERNET_MAX_URL_LENGTH, &sizeStr, 0);
532     else
533         hres = CoInternetParseUrl(lpszURLName, PARSE_CANONICALIZE, URL_FILE_USE_PATHURL,
534                 This->URLName, INTERNET_MAX_URL_LENGTH, &sizeStr, 0);
535
536     if(FAILED(hres)) {
537         heap_free(This->URLName);
538         return hres;
539     }
540
541     URLMON_LockModule();
542
543     if(sizeStr != INTERNET_MAX_URL_LENGTH)
544         This->URLName = heap_realloc(This->URLName, (sizeStr+1)*sizeof(WCHAR));
545
546     TRACE("URLName = %s\n", debugstr_w(This->URLName));
547
548     return S_OK;
549 }
550
551 HRESULT StdURLMoniker_Construct(IUnknown *outer, void **ppv)
552 {
553     TRACE("(%p %p)\n", outer, ppv);
554
555     *ppv = alloc_moniker();
556     return *ppv ? S_OK : E_OUTOFMEMORY;
557 }
558
559 /***********************************************************************
560  *           CreateURLMonikerEx (URLMON.@)
561  *
562  * Create a url moniker.
563  *
564  * PARAMS
565  *    pmkContext [I] Context
566  *    szURL      [I] Url to create the moniker for
567  *    ppmk       [O] Destination for created moniker.
568  *    dwFlags    [I] Flags.
569  *
570  * RETURNS
571  *    Success: S_OK. ppmk contains the created IMoniker object.
572  *    Failure: MK_E_SYNTAX if szURL is not a valid url, or
573  *             E_OUTOFMEMORY if memory allocation fails.
574  */
575 HRESULT WINAPI CreateURLMonikerEx(IMoniker *pmkContext, LPCWSTR szURL, IMoniker **ppmk, DWORD dwFlags)
576 {
577     URLMoniker *obj;
578     HRESULT hres;
579     LPOLESTR lefturl = NULL;
580
581     TRACE("(%p, %s, %p, %08x)\n", pmkContext, debugstr_w(szURL), ppmk, dwFlags);
582
583     if (ppmk)
584         *ppmk = NULL;
585
586     if (!szURL || !ppmk)
587         return E_INVALIDARG;
588
589     if (dwFlags & URL_MK_UNIFORM) FIXME("ignoring flag URL_MK_UNIFORM\n");
590
591     if(!(obj = alloc_moniker()))
592         return E_OUTOFMEMORY;
593
594     if(pmkContext) {
595         IBindCtx* bind;
596         DWORD dwMksys = 0;
597         IMoniker_IsSystemMoniker(pmkContext, &dwMksys);
598         if(dwMksys == MKSYS_URLMONIKER && SUCCEEDED(CreateBindCtx(0, &bind))) {
599             IMoniker_GetDisplayName(pmkContext, bind, NULL, &lefturl);
600             TRACE("lefturl = %s\n", debugstr_w(lefturl));
601             IBindCtx_Release(bind);
602         }
603     }
604         
605     hres = URLMoniker_Init(obj, lefturl, szURL);
606     CoTaskMemFree(lefturl);
607     if(SUCCEEDED(hres))
608         hres = URLMoniker_QueryInterface(&obj->IMoniker_iface, &IID_IMoniker, (void**)ppmk);
609     IMoniker_Release(&obj->IMoniker_iface);
610     return hres;
611 }
612
613 /**********************************************************************
614  *           CreateURLMoniker (URLMON.@)
615  *
616  * Create a url moniker.
617  *
618  * PARAMS
619  *    pmkContext [I] Context
620  *    szURL      [I] Url to create the moniker for
621  *    ppmk       [O] Destination for created moniker.
622  *
623  * RETURNS
624  *    Success: S_OK. ppmk contains the created IMoniker object.
625  *    Failure: MK_E_SYNTAX if szURL is not a valid url, or
626  *             E_OUTOFMEMORY if memory allocation fails.
627  */
628 HRESULT WINAPI CreateURLMoniker(IMoniker *pmkContext, LPCWSTR szURL, IMoniker **ppmk)
629 {
630     return CreateURLMonikerEx(pmkContext, szURL, ppmk, URL_MK_LEGACY);
631 }
632
633 /***********************************************************************
634  *           IsAsyncMoniker (URLMON.@)
635  */
636 HRESULT WINAPI IsAsyncMoniker(IMoniker *pmk)
637 {
638     IUnknown *am;
639     
640     TRACE("(%p)\n", pmk);
641     if(!pmk)
642         return E_INVALIDARG;
643     if(SUCCEEDED(IMoniker_QueryInterface(pmk, &IID_IAsyncMoniker, (void**)&am))) {
644         IUnknown_Release(am);
645         return S_OK;
646     }
647     return S_FALSE;
648 }
649
650 /***********************************************************************
651  *           BindAsyncMoniker (URLMON.@)
652  *
653  * Bind a bind status callback to an asynchronous URL Moniker.
654  *
655  * PARAMS
656  *  pmk           [I] Moniker object to bind status callback to
657  *  grfOpt        [I] Options, seems not used
658  *  pbsc          [I] Status callback to bind
659  *  iidResult     [I] Interface to return
660  *  ppvResult     [O] Resulting asynchronous moniker object
661  *
662  * RETURNS
663  *    Success: S_OK.
664  *    Failure: E_INVALIDARG, if any argument is invalid, or
665  *             E_OUTOFMEMORY if memory allocation fails.
666  */
667 HRESULT WINAPI BindAsyncMoniker(IMoniker *pmk, DWORD grfOpt, IBindStatusCallback *pbsc, REFIID iidResult, LPVOID *ppvResult)
668 {
669     LPBC pbc = NULL;
670     HRESULT hr = E_INVALIDARG;
671
672     TRACE("(%p %08x %p %s %p)\n", pmk, grfOpt, pbsc, debugstr_guid(iidResult), ppvResult);
673
674     if (pmk && ppvResult)
675     {
676         *ppvResult = NULL;
677
678         hr = CreateAsyncBindCtx(0, pbsc, NULL, &pbc);
679         if (hr == NOERROR)
680         {
681             hr = IMoniker_BindToObject(pmk, pbc, NULL, iidResult, ppvResult);
682             IBindCtx_Release(pbc);
683         }
684     }
685     return hr;
686 }
687
688 /***********************************************************************
689  *           MkParseDisplayNameEx (URLMON.@)
690  */
691 HRESULT WINAPI MkParseDisplayNameEx(IBindCtx *pbc, LPCWSTR szDisplayName, ULONG *pchEaten, LPMONIKER *ppmk)
692 {
693     TRACE("(%p %s %p %p)\n", pbc, debugstr_w(szDisplayName), pchEaten, ppmk);
694
695     if (!pbc || !szDisplayName || !*szDisplayName || !pchEaten || !ppmk)
696         return E_INVALIDARG;
697
698     if(is_registered_protocol(szDisplayName)) {
699         HRESULT hres;
700
701         hres = CreateURLMoniker(NULL, szDisplayName, ppmk);
702         if(SUCCEEDED(hres)) {
703             *pchEaten = strlenW(szDisplayName);
704             return hres;
705         }
706     }
707
708     return MkParseDisplayName(pbc, szDisplayName, pchEaten, ppmk);
709 }
710
711
712 /***********************************************************************
713  *           URLDownloadToCacheFileA (URLMON.@)
714  */
715 HRESULT WINAPI URLDownloadToCacheFileA(LPUNKNOWN lpUnkCaller, LPCSTR szURL, LPSTR szFileName,
716         DWORD dwBufLength, DWORD dwReserved, LPBINDSTATUSCALLBACK pBSC)
717 {
718     LPWSTR url = NULL, file_name = NULL;
719     int len;
720     HRESULT hres;
721
722     TRACE("(%p %s %p %d %d %p)\n", lpUnkCaller, debugstr_a(szURL), szFileName,
723             dwBufLength, dwReserved, pBSC);
724
725     if(szURL) {
726         len = MultiByteToWideChar(CP_ACP, 0, szURL, -1, NULL, 0);
727         url = heap_alloc(len*sizeof(WCHAR));
728         MultiByteToWideChar(CP_ACP, 0, szURL, -1, url, len);
729     }
730
731     if(szFileName)
732         file_name = heap_alloc(dwBufLength*sizeof(WCHAR));
733
734     hres = URLDownloadToCacheFileW(lpUnkCaller, url, file_name, dwBufLength*sizeof(WCHAR),
735             dwReserved, pBSC);
736
737     if(SUCCEEDED(hres) && file_name)
738         WideCharToMultiByte(CP_ACP, 0, file_name, -1, szFileName, dwBufLength, NULL, NULL);
739
740     heap_free(url);
741     heap_free(file_name);
742
743     return hres;
744 }
745
746 /***********************************************************************
747  *           URLDownloadToCacheFileW (URLMON.@)
748  */
749 HRESULT WINAPI URLDownloadToCacheFileW(LPUNKNOWN lpUnkCaller, LPCWSTR szURL, LPWSTR szFileName,
750                 DWORD dwBufLength, DWORD dwReserved, LPBINDSTATUSCALLBACK pBSC)
751 {
752     WCHAR cache_path[MAX_PATH + 1];
753     FILETIME expire, modified;
754     HRESULT hr;
755     LPWSTR ext;
756
757     static WCHAR header[] = {
758         'H','T','T','P','/','1','.','0',' ','2','0','0',' ',
759         'O','K','\\','r','\\','n','\\','r','\\','n',0
760     };
761
762     TRACE("(%p, %s, %p, %d, %d, %p)\n", lpUnkCaller, debugstr_w(szURL),
763           szFileName, dwBufLength, dwReserved, pBSC);
764
765     if (!szURL || !szFileName)
766         return E_INVALIDARG;
767
768     ext = PathFindExtensionW(szURL);
769
770     if (!CreateUrlCacheEntryW(szURL, 0, ext, cache_path, 0))
771         return E_FAIL;
772
773     hr = URLDownloadToFileW(lpUnkCaller, szURL, cache_path, 0, pBSC);
774     if (FAILED(hr))
775         return hr;
776
777     expire.dwHighDateTime = 0;
778     expire.dwLowDateTime = 0;
779     modified.dwHighDateTime = 0;
780     modified.dwLowDateTime = 0;
781
782     if (!CommitUrlCacheEntryW(szURL, cache_path, expire, modified, NORMAL_CACHE_ENTRY,
783                               header, sizeof(header), NULL, NULL))
784         return E_FAIL;
785
786     if (strlenW(cache_path) > dwBufLength)
787         return E_OUTOFMEMORY;
788
789     lstrcpyW(szFileName, cache_path);
790
791     return S_OK;
792 }
793
794 /***********************************************************************
795  *           HlinkSimpleNavigateToMoniker (URLMON.@)
796  */
797 HRESULT WINAPI HlinkSimpleNavigateToMoniker(IMoniker *pmkTarget,
798     LPCWSTR szLocation, LPCWSTR szTargetFrameName, IUnknown *pUnk,
799     IBindCtx *pbc, IBindStatusCallback *pbsc, DWORD grfHLNF, DWORD dwReserved)
800 {
801     LPWSTR target;
802     HRESULT hres;
803
804     TRACE("\n");
805
806     hres = IMoniker_GetDisplayName(pmkTarget, pbc, 0, &target);
807     if(hres == S_OK)
808         hres = HlinkSimpleNavigateToString( target, szLocation, szTargetFrameName,
809                                             pUnk, pbc, pbsc, grfHLNF, dwReserved );
810     CoTaskMemFree(target);
811
812     return hres;
813 }
814
815 /***********************************************************************
816  *           HlinkSimpleNavigateToString (URLMON.@)
817  */
818 HRESULT WINAPI HlinkSimpleNavigateToString( LPCWSTR szTarget,
819     LPCWSTR szLocation, LPCWSTR szTargetFrameName, IUnknown *pUnk,
820     IBindCtx *pbc, IBindStatusCallback *pbsc, DWORD grfHLNF, DWORD dwReserved)
821 {
822     FIXME("%s %s %s %p %p %p %u %u partial stub\n", debugstr_w( szTarget ), debugstr_w( szLocation ),
823           debugstr_w( szTargetFrameName ), pUnk, pbc, pbsc, grfHLNF, dwReserved);
824
825     /* undocumented: 0 means HLNF_OPENINNEWWINDOW*/
826     if (!grfHLNF) grfHLNF = HLNF_OPENINNEWWINDOW;
827
828     if (grfHLNF == HLNF_OPENINNEWWINDOW)
829     {
830         SHELLEXECUTEINFOW sei;
831         static const WCHAR openW[] = { 'o', 'p', 'e', 'n', 0 };
832
833         memset(&sei, 0, sizeof(sei));
834         sei.cbSize = sizeof(sei);
835         sei.lpVerb = openW;
836         sei.nShow = SW_SHOWNORMAL;
837         sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NO_CONSOLE;
838         sei.lpFile = szTarget;
839
840         if (ShellExecuteExW(&sei)) return S_OK;
841     }
842
843     return E_NOTIMPL;
844 }
845
846 /***********************************************************************
847  *           HlinkNavigateString (URLMON.@)
848  */
849 HRESULT WINAPI HlinkNavigateString( IUnknown *pUnk, LPCWSTR szTarget )
850 {
851     TRACE("%p %s\n", pUnk, debugstr_w( szTarget ) );
852     return HlinkSimpleNavigateToString( 
853                szTarget, NULL, NULL, pUnk, NULL, NULL, 0, 0 );
854 }
855
856 /***********************************************************************
857  *           GetSoftwareUpdateInfo (URLMON.@)
858  */
859 HRESULT WINAPI GetSoftwareUpdateInfo( LPCWSTR szDistUnit, LPSOFTDISTINFO psdi )
860 {
861     FIXME("%s %p\n", debugstr_w(szDistUnit), psdi );
862     return E_FAIL;
863 }
864
865 /***********************************************************************
866  *           AsyncInstallDistributionUnit (URLMON.@)
867  */
868 HRESULT WINAPI AsyncInstallDistributionUnit( LPCWSTR szDistUnit, LPCWSTR szTYPE,
869                             LPCWSTR szExt, DWORD dwFileVersionMS, DWORD dwFileVersionLS,
870                             LPCWSTR szURL, IBindCtx *pbc, LPVOID pvReserved, DWORD flags )
871 {
872     FIXME(": stub\n");
873     return E_NOTIMPL;
874 }