urlmon: Added AsyncInstallDistributionUnit implementation.
[wine] / dlls / urlmon / urlmon_main.c
1 /*
2  * UrlMon
3  *
4  * Copyright (c) 2000 Patrik Stridvall
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "urlmon_main.h"
24
25 #include "winreg.h"
26
27 #define NO_SHLWAPI_REG
28 #include "shlwapi.h"
29 #include "advpub.h"
30 #include "initguid.h"
31
32 #include "wine/debug.h"
33
34 #include "urlmon.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
37
38 DEFINE_GUID(CLSID_CUri, 0xDF2FCE13, 0x25EC, 0x45BB, 0x9D,0x4C, 0xCE,0xCD,0x47,0xC2,0x43,0x0C);
39
40 LONG URLMON_refCount = 0;
41 HINSTANCE urlmon_instance;
42
43 static HMODULE hCabinet = NULL;
44 static DWORD urlmon_tls = TLS_OUT_OF_INDEXES;
45
46 static void init_session(void);
47
48 static struct list tls_list = LIST_INIT(tls_list);
49
50 static CRITICAL_SECTION tls_cs;
51 static CRITICAL_SECTION_DEBUG tls_cs_dbg =
52 {
53     0, 0, &tls_cs,
54     { &tls_cs_dbg.ProcessLocksList, &tls_cs_dbg.ProcessLocksList },
55       0, 0, { (DWORD_PTR)(__FILE__ ": tls") }
56 };
57
58 static CRITICAL_SECTION tls_cs = { &tls_cs_dbg, -1, 0, 0, 0, 0 };
59
60 tls_data_t *get_tls_data(void)
61 {
62     tls_data_t *data;
63
64     if(urlmon_tls == TLS_OUT_OF_INDEXES) {
65         DWORD tls = TlsAlloc();
66         if(tls == TLS_OUT_OF_INDEXES)
67             return NULL;
68
69         tls = InterlockedCompareExchange((LONG*)&urlmon_tls, tls, TLS_OUT_OF_INDEXES);
70         if(tls != urlmon_tls)
71             TlsFree(tls);
72     }
73
74     data = TlsGetValue(urlmon_tls);
75     if(!data) {
76         data = heap_alloc_zero(sizeof(tls_data_t));
77         if(!data)
78             return NULL;
79
80         EnterCriticalSection(&tls_cs);
81         list_add_tail(&tls_list, &data->entry);
82         LeaveCriticalSection(&tls_cs);
83
84         TlsSetValue(urlmon_tls, data);
85     }
86
87     return data;
88 }
89
90 static void free_tls_list(void)
91 {
92     tls_data_t *data;
93
94     if(urlmon_tls == TLS_OUT_OF_INDEXES)
95         return;
96
97     while(!list_empty(&tls_list)) {
98         data = LIST_ENTRY(list_head(&tls_list), tls_data_t, entry);
99         list_remove(&data->entry);
100         heap_free(data);
101     }
102
103     TlsFree(urlmon_tls);
104 }
105
106 static void detach_thread(void)
107 {
108     tls_data_t *data;
109
110     if(urlmon_tls == TLS_OUT_OF_INDEXES)
111         return;
112
113     data = TlsGetValue(urlmon_tls);
114     if(!data)
115         return;
116
117     EnterCriticalSection(&tls_cs);
118     list_remove(&data->entry);
119     LeaveCriticalSection(&tls_cs);
120
121     if(data->notif_hwnd) {
122         WARN("notif_hwnd not destroyed\n");
123         DestroyWindow(data->notif_hwnd);
124     }
125
126     heap_free(data);
127 }
128
129 static void process_detach(void)
130 {
131     HINTERNET internet_session;
132
133     internet_session = get_internet_session(NULL);
134     if(internet_session)
135         InternetCloseHandle(internet_session);
136
137     if (hCabinet)
138         FreeLibrary(hCabinet);
139
140     free_session();
141     free_tls_list();
142 }
143
144 /***********************************************************************
145  *              DllMain (URLMON.init)
146  */
147 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
148 {
149     TRACE("%p 0x%x %p\n", hinstDLL, fdwReason, fImpLoad);
150
151     URLMON_DllMain( hinstDLL, fdwReason, fImpLoad );
152
153     switch(fdwReason) {
154     case DLL_PROCESS_ATTACH:
155         urlmon_instance = hinstDLL;
156         init_session();
157         break;
158
159     case DLL_PROCESS_DETACH:
160         process_detach();
161         DeleteCriticalSection(&tls_cs);
162         break;
163
164     case DLL_THREAD_DETACH:
165         detach_thread();
166         break;
167     }
168     return TRUE;
169 }
170
171 const char *debugstr_bindstatus(ULONG status)
172 {
173     switch(status) {
174 #define X(x) case x: return #x
175     X(BINDSTATUS_FINDINGRESOURCE);
176     X(BINDSTATUS_CONNECTING);
177     X(BINDSTATUS_REDIRECTING);
178     X(BINDSTATUS_BEGINDOWNLOADDATA);
179     X(BINDSTATUS_DOWNLOADINGDATA);
180     X(BINDSTATUS_ENDDOWNLOADDATA);
181     X(BINDSTATUS_BEGINDOWNLOADCOMPONENTS);
182     X(BINDSTATUS_INSTALLINGCOMPONENTS);
183     X(BINDSTATUS_ENDDOWNLOADCOMPONENTS);
184     X(BINDSTATUS_USINGCACHEDCOPY);
185     X(BINDSTATUS_SENDINGREQUEST);
186     X(BINDSTATUS_CLASSIDAVAILABLE);
187     X(BINDSTATUS_MIMETYPEAVAILABLE);
188     X(BINDSTATUS_CACHEFILENAMEAVAILABLE);
189     X(BINDSTATUS_BEGINSYNCOPERATION);
190     X(BINDSTATUS_ENDSYNCOPERATION);
191     X(BINDSTATUS_BEGINUPLOADDATA);
192     X(BINDSTATUS_UPLOADINGDATA);
193     X(BINDSTATUS_ENDUPLOADINGDATA);
194     X(BINDSTATUS_PROTOCOLCLASSID);
195     X(BINDSTATUS_ENCODING);
196     X(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE);
197     X(BINDSTATUS_CLASSINSTALLLOCATION);
198     X(BINDSTATUS_DECODING);
199     X(BINDSTATUS_LOADINGMIMEHANDLER);
200     X(BINDSTATUS_CONTENTDISPOSITIONATTACH);
201     X(BINDSTATUS_FILTERREPORTMIMETYPE);
202     X(BINDSTATUS_CLSIDCANINSTANTIATE);
203     X(BINDSTATUS_IUNKNOWNAVAILABLE);
204     X(BINDSTATUS_DIRECTBIND);
205     X(BINDSTATUS_RAWMIMETYPE);
206     X(BINDSTATUS_PROXYDETECTING);
207     X(BINDSTATUS_ACCEPTRANGES);
208     X(BINDSTATUS_COOKIE_SENT);
209     X(BINDSTATUS_COMPACT_POLICY_RECEIVED);
210     X(BINDSTATUS_COOKIE_SUPPRESSED);
211     X(BINDSTATUS_COOKIE_STATE_UNKNOWN);
212     X(BINDSTATUS_COOKIE_STATE_ACCEPT);
213     X(BINDSTATUS_COOKIE_STATE_REJECT);
214     X(BINDSTATUS_COOKIE_STATE_PROMPT);
215     X(BINDSTATUS_COOKIE_STATE_LEASH);
216     X(BINDSTATUS_COOKIE_STATE_DOWNGRADE);
217     X(BINDSTATUS_POLICY_HREF);
218     X(BINDSTATUS_P3P_HEADER);
219     X(BINDSTATUS_SESSION_COOKIE_RECEIVED);
220     X(BINDSTATUS_PERSISTENT_COOKIE_RECEIVED);
221     X(BINDSTATUS_SESSION_COOKIES_ALLOWED);
222     X(BINDSTATUS_CACHECONTROL);
223     X(BINDSTATUS_CONTENTDISPOSITIONFILENAME);
224     X(BINDSTATUS_MIMETEXTPLAINMISMATCH);
225     X(BINDSTATUS_PUBLISHERAVAILABLE);
226     X(BINDSTATUS_DISPLAYNAMEAVAILABLE);
227 #undef X
228     default:
229         return wine_dbg_sprintf("(invalid status %u)", status);
230     }
231 }
232
233 /***********************************************************************
234  *              DllInstall (URLMON.@)
235  */
236 HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
237 {
238   FIXME("(%s, %s): stub\n", bInstall?"TRUE":"FALSE",
239         debugstr_w(cmdline));
240
241   return S_OK;
242 }
243
244 /***********************************************************************
245  *              DllCanUnloadNow (URLMON.@)
246  */
247 HRESULT WINAPI DllCanUnloadNow(void)
248 {
249     return URLMON_refCount != 0 ? S_FALSE : S_OK;
250 }
251
252
253
254 /******************************************************************************
255  * Urlmon ClassFactory
256  */
257 typedef struct {
258     IClassFactory IClassFactory_iface;
259
260     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
261 } ClassFactory;
262
263 static inline ClassFactory *impl_from_IClassFactory(IClassFactory *iface)
264 {
265     return CONTAINING_RECORD(iface, ClassFactory, IClassFactory_iface);
266 }
267
268 static HRESULT WINAPI CF_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppv)
269 {
270     *ppv = NULL;
271
272     if(IsEqualGUID(riid, &IID_IUnknown)) {
273         TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv);
274         *ppv = iface;
275     }else if(IsEqualGUID(riid, &IID_IClassFactory)) {
276         TRACE("(%p)->(IID_IClassFactory %p)\n", iface, ppv);
277         *ppv = iface;
278     }
279
280     if(*ppv) {
281         IUnknown_AddRef((IUnknown*)*ppv);
282         return S_OK;
283     }
284
285     WARN("(%p)->(%s,%p),not found\n", iface, debugstr_guid(riid), ppv);
286     return E_NOINTERFACE;
287 }
288
289 static ULONG WINAPI CF_AddRef(IClassFactory *iface)
290 {
291     URLMON_LockModule();
292     return 2;
293 }
294
295 static ULONG WINAPI CF_Release(IClassFactory *iface)
296 {
297     URLMON_UnlockModule();
298     return 1;
299 }
300
301
302 static HRESULT WINAPI CF_CreateInstance(IClassFactory *iface, IUnknown *pOuter,
303                                         REFIID riid, LPVOID *ppobj)
304 {
305     ClassFactory *This = impl_from_IClassFactory(iface);
306     HRESULT hres;
307     LPUNKNOWN punk;
308     
309     TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
310
311     *ppobj = NULL;
312     if(SUCCEEDED(hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk))) {
313         hres = IUnknown_QueryInterface(punk, riid, ppobj);
314         IUnknown_Release(punk);
315     }
316     return hres;
317 }
318
319 static HRESULT WINAPI CF_LockServer(LPCLASSFACTORY iface,BOOL dolock)
320 {
321     TRACE("(%d)\n", dolock);
322
323     if (dolock)
324            URLMON_LockModule();
325     else
326            URLMON_UnlockModule();
327
328     return S_OK;
329 }
330
331 static const IClassFactoryVtbl ClassFactoryVtbl =
332 {
333     CF_QueryInterface,
334     CF_AddRef,
335     CF_Release,
336     CF_CreateInstance,
337     CF_LockServer
338 };
339
340 static ClassFactory FileProtocolCF =
341     { { &ClassFactoryVtbl }, FileProtocol_Construct};
342 static ClassFactory FtpProtocolCF =
343     { { &ClassFactoryVtbl }, FtpProtocol_Construct};
344 static ClassFactory GopherProtocolCF =
345     { { &ClassFactoryVtbl }, GopherProtocol_Construct};
346 static ClassFactory HttpProtocolCF =
347     { { &ClassFactoryVtbl }, HttpProtocol_Construct};
348 static ClassFactory HttpSProtocolCF =
349     { { &ClassFactoryVtbl }, HttpSProtocol_Construct};
350 static ClassFactory MkProtocolCF =
351     { { &ClassFactoryVtbl }, MkProtocol_Construct};
352 static ClassFactory SecurityManagerCF =
353     { { &ClassFactoryVtbl }, SecManagerImpl_Construct};
354 static ClassFactory ZoneManagerCF =
355     { { &ClassFactoryVtbl }, ZoneMgrImpl_Construct};
356 static ClassFactory StdURLMonikerCF =
357     { { &ClassFactoryVtbl }, StdURLMoniker_Construct};
358 static ClassFactory MimeFilterCF =
359     { { &ClassFactoryVtbl }, MimeFilter_Construct};
360 static ClassFactory CUriCF =
361     { { &ClassFactoryVtbl }, Uri_Construct};
362
363 struct object_creation_info
364 {
365     const CLSID *clsid;
366     IClassFactory *cf;
367     LPCWSTR protocol;
368 };
369
370 static const WCHAR wszFile[] = {'f','i','l','e',0};
371 static const WCHAR wszFtp[]  = {'f','t','p',0};
372 static const WCHAR wszGopher[]  = {'g','o','p','h','e','r',0};
373 static const WCHAR wszHttp[] = {'h','t','t','p',0};
374 static const WCHAR wszHttps[] = {'h','t','t','p','s',0};
375 static const WCHAR wszMk[]   = {'m','k',0};
376
377 static const struct object_creation_info object_creation[] =
378 {
379     { &CLSID_FileProtocol,            &FileProtocolCF.IClassFactory_iface,    wszFile },
380     { &CLSID_FtpProtocol,             &FtpProtocolCF.IClassFactory_iface,     wszFtp  },
381     { &CLSID_GopherProtocol,          &GopherProtocolCF.IClassFactory_iface,  wszGopher },
382     { &CLSID_HttpProtocol,            &HttpProtocolCF.IClassFactory_iface,    wszHttp },
383     { &CLSID_HttpSProtocol,           &HttpSProtocolCF.IClassFactory_iface,   wszHttps },
384     { &CLSID_MkProtocol,              &MkProtocolCF.IClassFactory_iface,      wszMk },
385     { &CLSID_InternetSecurityManager, &SecurityManagerCF.IClassFactory_iface, NULL    },
386     { &CLSID_InternetZoneManager,     &ZoneManagerCF.IClassFactory_iface,     NULL    },
387     { &CLSID_StdURLMoniker,           &StdURLMonikerCF.IClassFactory_iface,   NULL    },
388     { &CLSID_DeCompMimeFilter,        &MimeFilterCF.IClassFactory_iface,      NULL    },
389     { &CLSID_CUri,                    &CUriCF.IClassFactory_iface,            NULL    }
390 };
391
392 static void init_session(void)
393 {
394     unsigned int i;
395
396     for(i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++) {
397         if(object_creation[i].protocol)
398             register_namespace(object_creation[i].cf, object_creation[i].clsid,
399                                       object_creation[i].protocol, TRUE);
400     }
401 }
402
403 /*******************************************************************************
404  * DllGetClassObject [URLMON.@]
405  * Retrieves class object from a DLL object
406  *
407  * NOTES
408  *    Docs say returns STDAPI
409  *
410  * PARAMS
411  *    rclsid [I] CLSID for the class object
412  *    riid   [I] Reference to identifier of interface for class object
413  *    ppv    [O] Address of variable to receive interface pointer for riid
414  *
415  * RETURNS
416  *    Success: S_OK
417  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
418  *             E_UNEXPECTED
419  */
420
421 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
422 {
423     unsigned int i;
424     HRESULT hr;
425     
426     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
427     
428     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
429     {
430         if (IsEqualGUID(object_creation[i].clsid, rclsid))
431             return IClassFactory_QueryInterface(object_creation[i].cf, riid, ppv);
432     }
433
434     hr = URLMON_DllGetClassObject(rclsid, riid, ppv);
435     if(SUCCEEDED(hr))
436         return hr;
437
438     FIXME("%s: no class found.\n", debugstr_guid(rclsid));
439     return CLASS_E_CLASSNOTAVAILABLE;
440 }
441
442 static HRESULT register_inf(BOOL doregister)
443 {
444     HRESULT (WINAPI *pRegInstall)(HMODULE hm, LPCSTR pszSection, const STRTABLEA* pstTable);
445     HMODULE hAdvpack;
446
447     static const WCHAR wszAdvpack[] = {'a','d','v','p','a','c','k','.','d','l','l',0};
448
449     hAdvpack = LoadLibraryW(wszAdvpack);
450     pRegInstall = (void *)GetProcAddress(hAdvpack, "RegInstall");
451
452     return pRegInstall(hProxyDll, doregister ? "RegisterDll" : "UnregisterDll", NULL);
453 }
454
455 /***********************************************************************
456  *              DllRegisterServer (URLMON.@)
457  */
458 HRESULT WINAPI DllRegisterServer(void)
459 {
460     HRESULT hr;
461
462     TRACE("\n");
463
464     hr = URLMON_DllRegisterServer();
465     return SUCCEEDED(hr) ? register_inf(TRUE) : hr;
466 }
467
468 /***********************************************************************
469  *              DllUnregisterServer (URLMON.@)
470  */
471 HRESULT WINAPI DllUnregisterServer(void)
472 {
473     HRESULT hr;
474
475     TRACE("\n");
476
477     hr = URLMON_DllUnregisterServer();
478     return SUCCEEDED(hr) ? register_inf(FALSE) : hr;
479 }
480
481 /***********************************************************************
482  *              DllRegisterServerEx (URLMON.@)
483  */
484 HRESULT WINAPI DllRegisterServerEx(void)
485 {
486     FIXME("(void): stub\n");
487
488     return E_FAIL;
489 }
490
491 /**************************************************************************
492  *                 IsValidURL (URLMON.@)
493  * 
494  * Determines if a specified string is a valid URL.
495  *
496  * PARAMS
497  *  pBC        [I] ignored, should be NULL.
498  *  szURL      [I] string that represents the URL in question.
499  *  dwReserved [I] reserved and must be zero.
500  *
501  * RETURNS
502  *  Success: S_OK.
503  *  Failure: S_FALSE.
504  *  returns E_INVALIDARG if one or more of the args is invalid.
505  *
506  * TODO:
507  *  test functionality against windows to see what a valid URL is.
508  */
509 HRESULT WINAPI IsValidURL(LPBC pBC, LPCWSTR szURL, DWORD dwReserved)
510 {
511     FIXME("(%p, %s, %d): stub\n", pBC, debugstr_w(szURL), dwReserved);
512
513     if (dwReserved || !szURL)
514         return E_INVALIDARG;
515
516     return S_OK;
517 }
518
519 /**************************************************************************
520  *                 FaultInIEFeature (URLMON.@)
521  *
522  *  Undocumented.  Appears to be used by native shdocvw.dll.
523  */
524 HRESULT WINAPI FaultInIEFeature( HWND hwnd, uCLSSPEC * pClassSpec,
525                                  QUERYCONTEXT *pQuery, DWORD flags )
526 {
527     FIXME("%p %p %p %08x\n", hwnd, pClassSpec, pQuery, flags);
528     return E_NOTIMPL;
529 }
530
531 /**************************************************************************
532  *                 CoGetClassObjectFromURL (URLMON.@)
533  */
534 HRESULT WINAPI CoGetClassObjectFromURL( REFCLSID rclsid, LPCWSTR szCodeURL, DWORD dwFileVersionMS,
535                                         DWORD dwFileVersionLS, LPCWSTR szContentType,
536                                         LPBINDCTX pBindCtx, DWORD dwClsContext, LPVOID pvReserved,
537                                         REFIID riid, LPVOID *ppv )
538 {
539     FIXME("(%s %s %d %d %s %p %d %p %s %p) Stub!\n", debugstr_guid(rclsid), debugstr_w(szCodeURL),
540         dwFileVersionMS, dwFileVersionLS, debugstr_w(szContentType), pBindCtx, dwClsContext, pvReserved,
541         debugstr_guid(riid), ppv);
542     return E_NOINTERFACE;
543 }
544
545 /***********************************************************************
546  *           ReleaseBindInfo (URLMON.@)
547  *
548  * Release the resources used by the specified BINDINFO structure.
549  *
550  * PARAMS
551  *  pbindinfo [I] BINDINFO to release.
552  *
553  * RETURNS
554  *  Nothing.
555  */
556 void WINAPI ReleaseBindInfo(BINDINFO* pbindinfo)
557 {
558     DWORD size;
559
560     TRACE("(%p)\n", pbindinfo);
561
562     if(!pbindinfo || !(size = pbindinfo->cbSize))
563         return;
564
565     CoTaskMemFree(pbindinfo->szExtraInfo);
566     ReleaseStgMedium(&pbindinfo->stgmedData);
567
568     if(offsetof(BINDINFO, szExtraInfo) < size)
569         CoTaskMemFree(pbindinfo->szCustomVerb);
570
571     if(pbindinfo->pUnk && offsetof(BINDINFO, pUnk) < size)
572         IUnknown_Release(pbindinfo->pUnk);
573
574     memset(pbindinfo, 0, size);
575     pbindinfo->cbSize = size;
576 }
577
578 /***********************************************************************
579  *           CopyStgMedium (URLMON.@)
580  */
581 HRESULT WINAPI CopyStgMedium(const STGMEDIUM *src, STGMEDIUM *dst)
582 {
583     TRACE("(%p %p)\n", src, dst);
584
585     if(!src || !dst)
586         return E_POINTER;
587
588     *dst = *src;
589
590     switch(dst->tymed) {
591     case TYMED_NULL:
592         break;
593     case TYMED_FILE:
594         if(src->u.lpszFileName && !src->pUnkForRelease) {
595             DWORD size = (strlenW(src->u.lpszFileName)+1)*sizeof(WCHAR);
596             dst->u.lpszFileName = CoTaskMemAlloc(size);
597             if(!dst->u.lpszFileName)
598                 return E_OUTOFMEMORY;
599             memcpy(dst->u.lpszFileName, src->u.lpszFileName, size);
600         }
601         break;
602     case TYMED_ISTREAM:
603         if(dst->u.pstm)
604             IStream_AddRef(dst->u.pstm);
605         break;
606     case TYMED_ISTORAGE:
607         if(dst->u.pstg)
608             IStorage_AddRef(dst->u.pstg);
609         break;
610     case TYMED_HGLOBAL:
611         if(dst->u.hGlobal) {
612             SIZE_T size = GlobalSize(src->u.hGlobal);
613             char *src_ptr, *dst_ptr;
614
615             dst->u.hGlobal = GlobalAlloc(GMEM_FIXED, size);
616             if(!dst->u.hGlobal)
617                 return E_OUTOFMEMORY;
618             dst_ptr = GlobalLock(dst->u.hGlobal);
619             src_ptr = GlobalLock(src->u.hGlobal);
620             memcpy(dst_ptr, src_ptr, size);
621             GlobalUnlock(src_ptr);
622             GlobalUnlock(dst_ptr);
623         }
624         break;
625     default:
626         FIXME("Unimplemented tymed %d\n", src->tymed);
627     }
628
629     if(dst->pUnkForRelease)
630         IUnknown_AddRef(dst->pUnkForRelease);
631
632     return S_OK;
633 }
634
635 /***********************************************************************
636  *           CopyBindInfo (URLMON.@)
637  */
638 HRESULT WINAPI CopyBindInfo(const BINDINFO *pcbiSrc, BINDINFO *pcbiDest)
639 {
640     DWORD size;
641     HRESULT hres;
642
643     TRACE("(%p %p)\n", pcbiSrc, pcbiDest);
644
645     if(!pcbiSrc || !pcbiDest)
646         return E_POINTER;
647     if(!pcbiSrc->cbSize || !pcbiDest->cbSize)
648         return E_INVALIDARG;
649
650     size = pcbiDest->cbSize;
651     if(size > pcbiSrc->cbSize) {
652         memcpy(pcbiDest, pcbiSrc, pcbiSrc->cbSize);
653         memset((char*)pcbiDest+pcbiSrc->cbSize, 0, size-pcbiSrc->cbSize);
654     } else {
655         memcpy(pcbiDest, pcbiSrc, size);
656     }
657     pcbiDest->cbSize = size;
658
659     size = FIELD_OFFSET(BINDINFO, szExtraInfo)+sizeof(void*);
660     if(pcbiSrc->cbSize>=size && pcbiDest->cbSize>=size && pcbiSrc->szExtraInfo) {
661         size = (strlenW(pcbiSrc->szExtraInfo)+1)*sizeof(WCHAR);
662         pcbiDest->szExtraInfo = CoTaskMemAlloc(size);
663         if(!pcbiDest->szExtraInfo)
664             return E_OUTOFMEMORY;
665         memcpy(pcbiDest->szExtraInfo, pcbiSrc->szExtraInfo, size);
666     }
667
668     size = FIELD_OFFSET(BINDINFO, stgmedData)+sizeof(STGMEDIUM);
669     if(pcbiSrc->cbSize>=size && pcbiDest->cbSize>=size) {
670         hres = CopyStgMedium(&pcbiSrc->stgmedData, &pcbiDest->stgmedData);
671         if(FAILED(hres)) {
672             CoTaskMemFree(pcbiDest->szExtraInfo);
673             return hres;
674         }
675     }
676
677     size = FIELD_OFFSET(BINDINFO, szCustomVerb)+sizeof(void*);
678     if(pcbiSrc->cbSize>=size && pcbiDest->cbSize>=size && pcbiSrc->szCustomVerb) {
679         size = (strlenW(pcbiSrc->szCustomVerb)+1)*sizeof(WCHAR);
680         pcbiDest->szCustomVerb = CoTaskMemAlloc(size);
681         if(!pcbiDest->szCustomVerb) {
682             CoTaskMemFree(pcbiDest->szExtraInfo);
683             ReleaseStgMedium(&pcbiDest->stgmedData);
684             return E_OUTOFMEMORY;
685         }
686         memcpy(pcbiDest->szCustomVerb, pcbiSrc->szCustomVerb, size);
687     }
688
689     size = FIELD_OFFSET(BINDINFO, securityAttributes)+sizeof(SECURITY_ATTRIBUTES);
690     if(pcbiDest->cbSize >= size)
691         memset(&pcbiDest->securityAttributes, 0, sizeof(SECURITY_ATTRIBUTES));
692
693     if(pcbiSrc->pUnk)
694         IUnknown_AddRef(pcbiDest->pUnk);
695
696     return S_OK;
697 }
698
699 static BOOL text_richtext_filter(const BYTE *b, DWORD size)
700 {
701     return size > 5 && !memcmp(b, "{\\rtf", 5);
702 }
703
704 static BOOL text_html_filter(const BYTE *b, DWORD size)
705 {
706     if(size < 6)
707         return FALSE;
708
709     if((b[0] == '<'
710                 && (b[1] == 'h' || b[1] == 'H')
711                 && (b[2] == 't' || b[2] == 'T')
712                 && (b[3] == 'm' || b[3] == 'M')
713                 && (b[4] == 'l' || b[4] == 'L'))
714             || (b[0] == '<'
715                 && (b[1] == 'h' || b[1] == 'H')
716                 && (b[2] == 'e' || b[2] == 'E')
717                 && (b[3] == 'a' || b[3] == 'A')
718                 && (b[4] == 'd' || b[4] == 'D'))) return TRUE;
719
720     return FALSE;
721 }
722
723 static BOOL text_xml_filter(const BYTE *b, DWORD size)
724 {
725     if(size < 7)
726         return FALSE;
727
728     if(b[0] == '<' && b[1] == '?'
729             && (b[2] == 'x' || b[2] == 'X')
730             && (b[3] == 'm' || b[3] == 'M')
731             && (b[4] == 'l' || b[4] == 'L')
732             && b[5] == ' ') return TRUE;
733
734     return FALSE;
735 }
736
737 static BOOL audio_basic_filter(const BYTE *b, DWORD size)
738 {
739     return size > 4
740         && b[0] == '.' && b[1] == 's' && b[2] == 'n' && b[3] == 'd';
741 }
742
743 static BOOL audio_wav_filter(const BYTE *b, DWORD size)
744 {
745     return size > 12
746         && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
747         && b[8] == 'W' && b[9] == 'A' && b[10] == 'V' && b[11] == 'E';
748 }
749
750 static BOOL image_gif_filter(const BYTE *b, DWORD size)
751 {
752     return size >= 6
753         && (b[0] == 'G' || b[0] == 'g')
754         && (b[1] == 'I' || b[1] == 'i')
755         && (b[2] == 'F' || b[2] == 'f')
756         &&  b[3] == '8'
757         && (b[4] == '7' || b[4] == '9')
758         && (b[5] == 'A' || b[5] == 'a');
759 }
760
761 static BOOL image_pjpeg_filter(const BYTE *b, DWORD size)
762 {
763     return size > 2 && b[0] == 0xff && b[1] == 0xd8;
764 }
765
766 static BOOL image_tiff_filter(const BYTE *b, DWORD size)
767 {
768     static const BYTE magic1[] = {0x4d,0x4d,0x00,0x2a};
769     static const BYTE magic2[] = {0x49,0x49,0x2a,0xff};
770
771     return size >= 4 && (!memcmp(b, magic1, 4) || !memcmp(b, magic2, 4));
772 }
773
774 static BOOL image_xpng_filter(const BYTE *b, DWORD size)
775 {
776     static const BYTE xpng_header[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a};
777     return size > sizeof(xpng_header) && !memcmp(b, xpng_header, sizeof(xpng_header));
778 }
779
780 static BOOL image_bmp_filter(const BYTE *b, DWORD size)
781 {
782     return size >= 14
783         && b[0] == 0x42 && b[1] == 0x4d
784         && *(const DWORD *)(b+6) == 0;
785 }
786
787 static BOOL video_avi_filter(const BYTE *b, DWORD size)
788 {
789     return size > 12
790         && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
791         && b[8] == 'A' && b[9] == 'V' && b[10] == 'I' && b[11] == 0x20;
792 }
793
794 static BOOL video_mpeg_filter(const BYTE *b, DWORD size)
795 {
796     return size > 4
797         && !b[0] && !b[1] && b[2] == 0x01
798         && (b[3] == 0xb3 || b[3] == 0xba);
799 }
800
801 static BOOL application_postscript_filter(const BYTE *b, DWORD size)
802 {
803     return size > 2 && b[0] == '%' && b[1] == '!';
804 }
805
806 static BOOL application_pdf_filter(const BYTE *b, DWORD size)
807 {
808     return size > 4 && b[0] == 0x25 && b[1] == 0x50 && b[2] == 0x44 && b[3] == 0x46;
809 }
810
811 static BOOL application_xzip_filter(const BYTE *b, DWORD size)
812 {
813     return size > 2 && b[0] == 0x50 && b[1] == 0x4b;
814 }
815
816 static BOOL application_xgzip_filter(const BYTE *b, DWORD size)
817 {
818     return size > 2 && b[0] == 0x1f && b[1] == 0x8b;
819 }
820
821 static BOOL application_java_filter(const BYTE *b, DWORD size)
822 {
823     return size > 4 && b[0] == 0xca && b[1] == 0xfe && b[2] == 0xba && b[3] == 0xbe;
824 }
825
826 static BOOL application_xmsdownload(const BYTE *b, DWORD size)
827 {
828     return size > 2 && b[0] == 'M' && b[1] == 'Z';
829 }
830
831 static inline BOOL is_text_plain_char(BYTE b)
832 {
833     if(b < 0x20 && b != '\n' && b != '\r' && b != '\t')
834         return FALSE;
835     return TRUE;
836 }
837
838 static BOOL text_plain_filter(const BYTE *b, DWORD size)
839 {
840     const BYTE *ptr;
841
842     for(ptr = b; ptr < b+size-1; ptr++) {
843         if(!is_text_plain_char(*ptr))
844             return FALSE;
845     }
846
847     return TRUE;
848 }
849
850 static BOOL application_octet_stream_filter(const BYTE *b, DWORD size)
851 {
852     return TRUE;
853 }
854
855 static HRESULT find_mime_from_buffer(const BYTE *buf, DWORD size, const WCHAR *proposed_mime, WCHAR **ret_mime)
856 {
857     LPCWSTR ret = NULL;
858     int len, i, any_pos_mime = -1;
859
860     static const WCHAR text_htmlW[] = {'t','e','x','t','/','h','t','m','l',0};
861     static const WCHAR text_richtextW[] = {'t','e','x','t','/','r','i','c','h','t','e','x','t',0};
862     static const WCHAR text_xmlW[] = {'t','e','x','t','/','x','m','l',0};
863     static const WCHAR audio_basicW[] = {'a','u','d','i','o','/','b','a','s','i','c',0};
864     static const WCHAR audio_wavW[] = {'a','u','d','i','o','/','w','a','v',0};
865     static const WCHAR image_gifW[] = {'i','m','a','g','e','/','g','i','f',0};
866     static const WCHAR image_pjpegW[] = {'i','m','a','g','e','/','p','j','p','e','g',0};
867     static const WCHAR image_tiffW[] = {'i','m','a','g','e','/','t','i','f','f',0};
868     static const WCHAR image_xpngW[] = {'i','m','a','g','e','/','x','-','p','n','g',0};
869     static const WCHAR image_bmpW[] = {'i','m','a','g','e','/','b','m','p',0};
870     static const WCHAR video_aviW[] = {'v','i','d','e','o','/','a','v','i',0};
871     static const WCHAR video_mpegW[] = {'v','i','d','e','o','/','m','p','e','g',0};
872     static const WCHAR app_postscriptW[] =
873         {'a','p','p','l','i','c','a','t','i','o','n','/','p','o','s','t','s','c','r','i','p','t',0};
874     static const WCHAR app_pdfW[] = {'a','p','p','l','i','c','a','t','i','o','n','/','p','d','f',0};
875     static const WCHAR app_xzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
876         'x','-','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
877     static const WCHAR app_xgzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
878         'x','-','g','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
879     static const WCHAR app_javaW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
880         'j','a','v','a',0};
881     static const WCHAR app_xmsdownloadW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
882         'x','-','m','s','d','o','w','n','l','o','a','d',0};
883     static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n','\0'};
884     static const WCHAR app_octetstreamW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
885         'o','c','t','e','t','-','s','t','r','e','a','m','\0'};
886
887     static const struct {
888         LPCWSTR mime;
889         BOOL (*filter)(const BYTE *,DWORD);
890     } mime_filters_any_pos[] = {
891         {text_htmlW,       text_html_filter},
892         {text_xmlW,        text_xml_filter}
893     }, mime_filters[] = {
894         {text_richtextW,   text_richtext_filter},
895      /* {audio_xaiffW,     audio_xaiff_filter}, */
896         {audio_basicW,     audio_basic_filter},
897         {audio_wavW,       audio_wav_filter},
898         {image_gifW,       image_gif_filter},
899         {image_pjpegW,     image_pjpeg_filter},
900         {image_tiffW,      image_tiff_filter},
901         {image_xpngW,      image_xpng_filter},
902      /* {image_xbitmapW,   image_xbitmap_filter}, */
903         {image_bmpW,       image_bmp_filter},
904      /* {image_xjgW,       image_xjg_filter}, */
905      /* {image_xemfW,      image_xemf_filter}, */
906      /* {image_xwmfW,      image_xwmf_filter}, */
907         {video_aviW,       video_avi_filter},
908         {video_mpegW,      video_mpeg_filter},
909         {app_postscriptW,  application_postscript_filter},
910      /* {app_base64W,      application_base64_filter}, */
911      /* {app_macbinhex40W, application_macbinhex40_filter}, */
912         {app_pdfW,         application_pdf_filter},
913      /* {app_zcompressedW, application_xcompressed_filter}, */
914         {app_xzipW,        application_xzip_filter},
915         {app_xgzipW,       application_xgzip_filter},
916         {app_javaW,        application_java_filter},
917         {app_xmsdownloadW, application_xmsdownload},
918         {text_plainW,      text_plain_filter},
919         {app_octetstreamW, application_octet_stream_filter}
920     };
921
922     if(!buf || !size) {
923         if(!proposed_mime)
924             return E_FAIL;
925
926         len = strlenW(proposed_mime)+1;
927         *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
928         if(!*ret_mime)
929             return E_OUTOFMEMORY;
930
931         memcpy(*ret_mime, proposed_mime, len*sizeof(WCHAR));
932         return S_OK;
933     }
934
935     if(proposed_mime && (!strcmpW(proposed_mime, app_octetstreamW)
936                 || !strcmpW(proposed_mime, text_plainW)))
937         proposed_mime = NULL;
938
939     if(proposed_mime) {
940         ret = proposed_mime;
941
942         for(i=0; i < sizeof(mime_filters_any_pos)/sizeof(*mime_filters_any_pos); i++) {
943             if(!strcmpW(proposed_mime, mime_filters_any_pos[i].mime)) {
944                 any_pos_mime = i;
945                 for(len=size; len>0; len--) {
946                     if(mime_filters_any_pos[i].filter(buf+size-len, len))
947                         break;
948                 }
949                 if(!len)
950                     ret = NULL;
951                 break;
952             }
953         }
954
955         if(i == sizeof(mime_filters_any_pos)/sizeof(*mime_filters_any_pos)) {
956             for(i=0; i < sizeof(mime_filters)/sizeof(*mime_filters); i++) {
957                 if(!strcmpW(proposed_mime, mime_filters[i].mime)) {
958                     if(!mime_filters[i].filter(buf, size))
959                         ret = NULL;
960                     break;
961                 }
962             }
963         }
964     }
965
966     /* Looks like a bug in native implementation, html and xml mimes
967      * are not looked for if none of them was proposed */
968     if(!proposed_mime || any_pos_mime!=-1) {
969         for(len=size; !ret && len>0; len--) {
970             for(i=0; i<sizeof(mime_filters_any_pos)/sizeof(*mime_filters_any_pos); i++) {
971                 if(mime_filters_any_pos[i].filter(buf+size-len, len)) {
972                     ret = mime_filters_any_pos[i].mime;
973                     break;
974                 }
975             }
976         }
977     }
978
979     i=0;
980     while(!ret) {
981         if(mime_filters[i].filter(buf, size))
982             ret = mime_filters[i].mime;
983         i++;
984     }
985
986     if(any_pos_mime!=-1 && ret==text_plainW)
987         ret = mime_filters_any_pos[any_pos_mime].mime;
988     else if(proposed_mime && ret==app_octetstreamW) {
989         for(len=size; ret==app_octetstreamW && len>0; len--) {
990             if(!is_text_plain_char(buf[size-len]))
991                 break;
992             for(i=0; i<sizeof(mime_filters_any_pos)/sizeof(*mime_filters_any_pos); i++) {
993                 if(mime_filters_any_pos[i].filter(buf+size-len, len)) {
994                     ret = text_plainW;
995                     break;
996                 }
997             }
998         }
999
1000         if(ret == app_octetstreamW)
1001             ret = proposed_mime;
1002     }
1003     TRACE("found %s for %s\n", debugstr_w(ret), debugstr_an((const char*)buf, min(32, size)));
1004
1005     len = strlenW(ret)+1;
1006     *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
1007     if(!*ret_mime)
1008         return E_OUTOFMEMORY;
1009
1010     memcpy(*ret_mime, ret, len*sizeof(WCHAR));
1011     return S_OK;
1012 }
1013
1014 /***********************************************************************
1015  *           FindMimeFromData (URLMON.@)
1016  *
1017  * Determines the Multipurpose Internet Mail Extensions (MIME) type from the data provided.
1018  */
1019 HRESULT WINAPI FindMimeFromData(LPBC pBC, LPCWSTR pwzUrl, LPVOID pBuffer,
1020         DWORD cbSize, LPCWSTR pwzMimeProposed, DWORD dwMimeFlags,
1021         LPWSTR* ppwzMimeOut, DWORD dwReserved)
1022 {
1023     TRACE("(%p,%s,%p,%d,%s,0x%x,%p,0x%x)\n", pBC, debugstr_w(pwzUrl), pBuffer, cbSize,
1024             debugstr_w(pwzMimeProposed), dwMimeFlags, ppwzMimeOut, dwReserved);
1025
1026     if(dwMimeFlags)
1027         WARN("dwMimeFlags=%08x\n", dwMimeFlags);
1028     if(dwReserved)
1029         WARN("dwReserved=%d\n", dwReserved);
1030
1031     /* pBC seams to not be used */
1032
1033     if(!ppwzMimeOut || (!pwzUrl && !pBuffer))
1034         return E_INVALIDARG;
1035
1036     if(pwzMimeProposed || pBuffer)
1037         return find_mime_from_buffer(pBuffer, cbSize, pwzMimeProposed, ppwzMimeOut);
1038
1039     if(pwzUrl) {
1040         HKEY hkey;
1041         DWORD res, size;
1042         LPCWSTR ptr;
1043         WCHAR mime[64];
1044
1045         static const WCHAR wszContentType[] =
1046                 {'C','o','n','t','e','n','t',' ','T','y','p','e','\0'};
1047
1048         ptr = strrchrW(pwzUrl, '.');
1049         if(!ptr)
1050             return E_FAIL;
1051
1052         res = RegOpenKeyW(HKEY_CLASSES_ROOT, ptr, &hkey);
1053         if(res != ERROR_SUCCESS)
1054             return HRESULT_FROM_WIN32(res);
1055
1056         size = sizeof(mime);
1057         res = RegQueryValueExW(hkey, wszContentType, NULL, NULL, (LPBYTE)mime, &size);
1058         RegCloseKey(hkey);
1059         if(res != ERROR_SUCCESS)
1060             return HRESULT_FROM_WIN32(res);
1061
1062         *ppwzMimeOut = CoTaskMemAlloc(size);
1063         memcpy(*ppwzMimeOut, mime, size);
1064         return S_OK;
1065     }
1066
1067     return E_FAIL;
1068 }
1069
1070 /***********************************************************************
1071  *           GetClassFileOrMime (URLMON.@)
1072  *
1073  * Determines the class ID from the bind context, file name or MIME type.
1074  */
1075 HRESULT WINAPI GetClassFileOrMime(LPBC pBC, LPCWSTR pszFilename,
1076         LPVOID pBuffer, DWORD cbBuffer, LPCWSTR pszMimeType, DWORD dwReserved,
1077         CLSID *pclsid)
1078 {
1079     FIXME("(%p, %s, %p, %d, %s, 0x%08x, %p): stub\n", pBC, debugstr_w(pszFilename), pBuffer,
1080             cbBuffer, debugstr_w(pszMimeType), dwReserved, pclsid);
1081     return E_NOTIMPL;
1082 }
1083
1084 /***********************************************************************
1085  * Extract (URLMON.@)
1086  */
1087 HRESULT WINAPI Extract(void *dest, LPCSTR szCabName)
1088 {
1089     HRESULT (WINAPI *pExtract)(void *, LPCSTR);
1090
1091     if (!hCabinet)
1092         hCabinet = LoadLibraryA("cabinet.dll");
1093
1094     if (!hCabinet) return HRESULT_FROM_WIN32(GetLastError());
1095     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
1096     if (!pExtract) return HRESULT_FROM_WIN32(GetLastError());
1097
1098     return pExtract(dest, szCabName);
1099 }
1100
1101 /***********************************************************************
1102  *           IsLoggingEnabledA (URLMON.@)
1103  */
1104 BOOL WINAPI IsLoggingEnabledA(LPCSTR url)
1105 {
1106     FIXME("(%s)\n", debugstr_a(url));
1107     return FALSE;
1108 }
1109
1110 /***********************************************************************
1111  *           IsLoggingEnabledW (URLMON.@)
1112  */
1113 BOOL WINAPI IsLoggingEnabledW(LPCWSTR url)
1114 {
1115     FIXME("(%s)\n", debugstr_w(url));
1116     return FALSE;
1117 }
1118
1119 /***********************************************************************
1120  *           IsProtectedModeURL (URLMON.111)
1121  *    Undocumented, added in IE7
1122  */
1123 BOOL WINAPI IsProtectedModeURL(const WCHAR *url)
1124 {
1125     FIXME("stub: %s\n", debugstr_w(url));
1126     return TRUE;
1127 }
1128
1129 /***********************************************************************
1130  *           LogSqmBits (URLMON.410)
1131  *    Undocumented, added in IE8
1132  */
1133 int WINAPI LogSqmBits(DWORD unk1, DWORD unk2)
1134 {
1135     FIXME("stub: %d %d\n", unk1, unk2);
1136     return 0;
1137 }
1138
1139 /***********************************************************************
1140  *           LogSqmUXCommandOffsetInternal (URLMON.423)
1141  *    Undocumented, added in IE8
1142  */
1143 void WINAPI LogSqmUXCommandOffsetInternal(DWORD unk1, DWORD unk2, DWORD unk3, DWORD unk4)
1144 {
1145     FIXME("stub: %d %d %d %d\n", unk1, unk2, unk3, unk4);
1146 }
1147
1148 /***********************************************************************
1149  *           MapUriToBrowserEmulationState (URLMON.444)
1150  *    Undocumented, added in IE8
1151  */
1152 int WINAPI MapUriToBrowserEmulationState(DWORD unk1, DWORD unk2, DWORD unk3)
1153 {
1154     FIXME("stub: %d %d %d\n", unk1, unk2, unk3);
1155     return 0;
1156 }
1157
1158 /***********************************************************************
1159  *           MapBrowserEmulationModeToUserAgent (URLMON.445)
1160  *    Undocumented, added in IE8
1161  */
1162 int WINAPI MapBrowserEmulationModeToUserAgent(DWORD unk1, DWORD unk2)
1163 {
1164     FIXME("stub: %d %d\n", unk1, unk2);
1165     return 0;
1166 }
1167
1168 /***********************************************************************
1169  *            RegisterMediaTypes
1170  *    Added in IE3, registers known MIME-type strings.
1171  */
1172 HRESULT WINAPI RegisterMediaTypes(UINT types, LPCSTR *szTypes, CLIPFORMAT *cfTypes)
1173 {
1174    FIXME("stub: %u %p %p\n", types, szTypes, cfTypes);
1175    return E_INVALIDARG;
1176 }