urlmon/tests: Fix CoInternetCompareUrl failure message typos.
[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 "wine/debug.h"
30
31 #include "urlmon.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
34
35 LONG URLMON_refCount = 0;
36
37 static HMODULE hCabinet = NULL;
38 static DWORD urlmon_tls = TLS_OUT_OF_INDEXES;
39
40 static void init_session(BOOL);
41
42 static struct list tls_list = LIST_INIT(tls_list);
43
44 static CRITICAL_SECTION tls_cs;
45 static CRITICAL_SECTION_DEBUG tls_cs_dbg =
46 {
47     0, 0, &tls_cs,
48     { &tls_cs_dbg.ProcessLocksList, &tls_cs_dbg.ProcessLocksList },
49       0, 0, { (DWORD_PTR)(__FILE__ ": tls") }
50 };
51
52 static CRITICAL_SECTION tls_cs = { &tls_cs_dbg, -1, 0, 0, 0, 0 };
53
54 tls_data_t *get_tls_data(void)
55 {
56     tls_data_t *data;
57
58     if(urlmon_tls == TLS_OUT_OF_INDEXES) {
59         DWORD tls = TlsAlloc();
60         if(tls == TLS_OUT_OF_INDEXES)
61             return NULL;
62
63         tls = InterlockedCompareExchange((LONG*)&urlmon_tls, tls, TLS_OUT_OF_INDEXES);
64         if(tls != urlmon_tls)
65             TlsFree(tls);
66     }
67
68     data = TlsGetValue(urlmon_tls);
69     if(!data) {
70         data = heap_alloc_zero(sizeof(tls_data_t));
71         if(!data)
72             return NULL;
73
74         EnterCriticalSection(&tls_cs);
75         list_add_tail(&tls_list, &data->entry);
76         LeaveCriticalSection(&tls_cs);
77
78         TlsSetValue(urlmon_tls, data);
79     }
80
81     return data;
82 }
83
84 static void free_tls_list(void)
85 {
86     tls_data_t *data;
87
88     if(urlmon_tls == TLS_OUT_OF_INDEXES)
89         return;
90
91     while(!list_empty(&tls_list)) {
92         data = LIST_ENTRY(list_head(&tls_list), tls_data_t, entry);
93         list_remove(&data->entry);
94         heap_free(data);
95     }
96
97     TlsFree(urlmon_tls);
98 }
99
100 static void detach_thread(void)
101 {
102     tls_data_t *data;
103
104     if(urlmon_tls == TLS_OUT_OF_INDEXES)
105         return;
106
107     data = TlsGetValue(urlmon_tls);
108     if(!data)
109         return;
110
111     EnterCriticalSection(&tls_cs);
112     list_remove(&data->entry);
113     LeaveCriticalSection(&tls_cs);
114
115     if(data->notif_hwnd) {
116         WARN("notif_hwnd not destroyed\n");
117         DestroyWindow(data->notif_hwnd);
118     }
119
120     heap_free(data);
121 }
122
123 static void process_detach(void)
124 {
125     HINTERNET internet_session;
126
127     internet_session = get_internet_session(NULL);
128     if(internet_session)
129         InternetCloseHandle(internet_session);
130
131     if (hCabinet)
132         FreeLibrary(hCabinet);
133
134     init_session(FALSE);
135     free_session();
136     free_tls_list();
137 }
138
139 /***********************************************************************
140  *              DllMain (URLMON.init)
141  */
142 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
143 {
144     TRACE("%p 0x%x %p\n", hinstDLL, fdwReason, fImpLoad);
145
146     URLMON_DllMain( hinstDLL, fdwReason, fImpLoad );
147
148     switch(fdwReason) {
149     case DLL_PROCESS_ATTACH:
150         init_session(TRUE);
151         break;
152
153     case DLL_PROCESS_DETACH:
154         process_detach();
155         break;
156
157     case DLL_THREAD_DETACH:
158         detach_thread();
159         break;
160     }
161     return TRUE;
162 }
163
164
165 /***********************************************************************
166  *              DllInstall (URLMON.@)
167  */
168 HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
169 {
170   FIXME("(%s, %s): stub\n", bInstall?"TRUE":"FALSE",
171         debugstr_w(cmdline));
172
173   return S_OK;
174 }
175
176 /***********************************************************************
177  *              DllCanUnloadNow (URLMON.@)
178  */
179 HRESULT WINAPI DllCanUnloadNow(void)
180 {
181     return URLMON_refCount != 0 ? S_FALSE : S_OK;
182 }
183
184
185
186 /******************************************************************************
187  * Urlmon ClassFactory
188  */
189 typedef struct {
190     const IClassFactoryVtbl *lpClassFactoryVtbl;
191
192     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
193 } ClassFactory;
194
195 #define CLASSFACTORY(x)  ((IClassFactory*)  &(x)->lpClassFactoryVtbl)
196
197 static HRESULT WINAPI CF_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppv)
198 {
199     *ppv = NULL;
200
201     if(IsEqualGUID(riid, &IID_IUnknown)) {
202         TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv);
203         *ppv = iface;
204     }else if(IsEqualGUID(riid, &IID_IClassFactory)) {
205         TRACE("(%p)->(IID_IClassFactory %p)\n", iface, ppv);
206         *ppv = iface;
207     }
208
209     if(*ppv) {
210         IUnknown_AddRef((IUnknown*)*ppv);
211         return S_OK;
212     }
213
214     WARN("(%p)->(%s,%p),not found\n", iface, debugstr_guid(riid), ppv);
215     return E_NOINTERFACE;
216 }
217
218 static ULONG WINAPI CF_AddRef(IClassFactory *iface)
219 {
220     URLMON_LockModule();
221     return 2;
222 }
223
224 static ULONG WINAPI CF_Release(IClassFactory *iface)
225 {
226     URLMON_UnlockModule();
227     return 1;
228 }
229
230
231 static HRESULT WINAPI CF_CreateInstance(IClassFactory *iface, IUnknown *pOuter,
232                                         REFIID riid, LPVOID *ppobj)
233 {
234     ClassFactory *This = (ClassFactory*)iface;
235     HRESULT hres;
236     LPUNKNOWN punk;
237     
238     TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
239
240     *ppobj = NULL;
241     if(SUCCEEDED(hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk))) {
242         hres = IUnknown_QueryInterface(punk, riid, ppobj);
243         IUnknown_Release(punk);
244     }
245     return hres;
246 }
247
248 static HRESULT WINAPI CF_LockServer(LPCLASSFACTORY iface,BOOL dolock)
249 {
250     TRACE("(%d)\n", dolock);
251
252     if (dolock)
253            URLMON_LockModule();
254     else
255            URLMON_UnlockModule();
256
257     return S_OK;
258 }
259
260 static const IClassFactoryVtbl ClassFactoryVtbl =
261 {
262     CF_QueryInterface,
263     CF_AddRef,
264     CF_Release,
265     CF_CreateInstance,
266     CF_LockServer
267 };
268
269 static const ClassFactory FileProtocolCF =
270     { &ClassFactoryVtbl, FileProtocol_Construct};
271 static const ClassFactory FtpProtocolCF =
272     { &ClassFactoryVtbl, FtpProtocol_Construct};
273 static const ClassFactory GopherProtocolCF =
274     { &ClassFactoryVtbl, GopherProtocol_Construct};
275 static const ClassFactory HttpProtocolCF =
276     { &ClassFactoryVtbl, HttpProtocol_Construct};
277 static const ClassFactory HttpSProtocolCF =
278     { &ClassFactoryVtbl, HttpSProtocol_Construct};
279 static const ClassFactory MkProtocolCF =
280     { &ClassFactoryVtbl, MkProtocol_Construct};
281 static const ClassFactory SecurityManagerCF =
282     { &ClassFactoryVtbl, SecManagerImpl_Construct};
283 static const ClassFactory ZoneManagerCF =
284     { &ClassFactoryVtbl, ZoneMgrImpl_Construct};
285 static const ClassFactory StdURLMonikerCF =
286     { &ClassFactoryVtbl, StdURLMoniker_Construct};
287 static const ClassFactory MimeFilterCF =
288     { &ClassFactoryVtbl, MimeFilter_Construct};
289  
290 struct object_creation_info
291 {
292     const CLSID *clsid;
293     IClassFactory *cf;
294     LPCWSTR protocol;
295 };
296
297 static const WCHAR wszFile[] = {'f','i','l','e',0};
298 static const WCHAR wszFtp[]  = {'f','t','p',0};
299 static const WCHAR wszGopher[]  = {'g','o','p','h','e','r',0};
300 static const WCHAR wszHttp[] = {'h','t','t','p',0};
301 static const WCHAR wszHttps[] = {'h','t','t','p','s',0};
302 static const WCHAR wszMk[]   = {'m','k',0};
303
304 static const struct object_creation_info object_creation[] =
305 {
306     { &CLSID_FileProtocol,            CLASSFACTORY(&FileProtocolCF),    wszFile },
307     { &CLSID_FtpProtocol,             CLASSFACTORY(&FtpProtocolCF),     wszFtp  },
308     { &CLSID_GopherProtocol,          CLASSFACTORY(&GopherProtocolCF),  wszGopher },
309     { &CLSID_HttpProtocol,            CLASSFACTORY(&HttpProtocolCF),    wszHttp },
310     { &CLSID_HttpSProtocol,           CLASSFACTORY(&HttpSProtocolCF),   wszHttps },
311     { &CLSID_MkProtocol,              CLASSFACTORY(&MkProtocolCF),      wszMk },
312     { &CLSID_InternetSecurityManager, CLASSFACTORY(&SecurityManagerCF), NULL    },
313     { &CLSID_InternetZoneManager,     CLASSFACTORY(&ZoneManagerCF),     NULL    },
314     { &CLSID_StdURLMoniker,           CLASSFACTORY(&StdURLMonikerCF),   NULL    },
315     { &CLSID_DeCompMimeFilter,        CLASSFACTORY(&MimeFilterCF),      NULL    }
316 };
317
318 static void init_session(BOOL init)
319 {
320     unsigned int i;
321
322     for(i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++) {
323
324         if(object_creation[i].protocol)
325             register_urlmon_namespace(object_creation[i].cf, object_creation[i].clsid,
326                                       object_creation[i].protocol, init);
327     }
328 }
329
330 /*******************************************************************************
331  * DllGetClassObject [URLMON.@]
332  * Retrieves class object from a DLL object
333  *
334  * NOTES
335  *    Docs say returns STDAPI
336  *
337  * PARAMS
338  *    rclsid [I] CLSID for the class object
339  *    riid   [I] Reference to identifier of interface for class object
340  *    ppv    [O] Address of variable to receive interface pointer for riid
341  *
342  * RETURNS
343  *    Success: S_OK
344  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
345  *             E_UNEXPECTED
346  */
347
348 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
349 {
350     unsigned int i;
351     HRESULT hr;
352     
353     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
354     
355     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
356     {
357         if (IsEqualGUID(object_creation[i].clsid, rclsid))
358             return IClassFactory_QueryInterface(object_creation[i].cf, riid, ppv);
359     }
360
361     hr = URLMON_DllGetClassObject(rclsid, riid, ppv);
362     if(SUCCEEDED(hr))
363         return hr;
364
365     FIXME("%s: no class found.\n", debugstr_guid(rclsid));
366     return CLASS_E_CLASSNOTAVAILABLE;
367 }
368
369
370 /***********************************************************************
371  *              DllRegisterServerEx (URLMON.@)
372  */
373 HRESULT WINAPI DllRegisterServerEx(void)
374 {
375     FIXME("(void): stub\n");
376
377     return E_FAIL;
378 }
379
380 /**************************************************************************
381  *                 IsValidURL (URLMON.@)
382  * 
383  * Determines if a specified string is a valid URL.
384  *
385  * PARAMS
386  *  pBC        [I] ignored, must be NULL.
387  *  szURL      [I] string that represents the URL in question.
388  *  dwReserved [I] reserved and must be zero.
389  *
390  * RETURNS
391  *  Success: S_OK.
392  *  Failure: S_FALSE.
393  *  returns E_INVALIDARG if one or more of the args is invalid.
394  *
395  * TODO:
396  *  test functionality against windows to see what a valid URL is.
397  */
398 HRESULT WINAPI IsValidURL(LPBC pBC, LPCWSTR szURL, DWORD dwReserved)
399 {
400     FIXME("(%p, %s, %d): stub\n", pBC, debugstr_w(szURL), dwReserved);
401
402     if (pBC || dwReserved || !szURL)
403         return E_INVALIDARG;
404
405     return S_OK;
406 }
407
408 /**************************************************************************
409  *                 FaultInIEFeature (URLMON.@)
410  *
411  *  Undocumented.  Appears to be used by native shdocvw.dll.
412  */
413 HRESULT WINAPI FaultInIEFeature( HWND hwnd, uCLSSPEC * pClassSpec,
414                                  QUERYCONTEXT *pQuery, DWORD flags )
415 {
416     FIXME("%p %p %p %08x\n", hwnd, pClassSpec, pQuery, flags);
417     return E_NOTIMPL;
418 }
419
420 /**************************************************************************
421  *                 CoGetClassObjectFromURL (URLMON.@)
422  */
423 HRESULT WINAPI CoGetClassObjectFromURL( REFCLSID rclsid, LPCWSTR szCodeURL, DWORD dwFileVersionMS,
424                                         DWORD dwFileVersionLS, LPCWSTR szContentType,
425                                         LPBINDCTX pBindCtx, DWORD dwClsContext, LPVOID pvReserved,
426                                         REFIID riid, LPVOID *ppv )
427 {
428     FIXME("(%s %s %d %d %s %p %d %p %s %p) Stub!\n", debugstr_guid(rclsid), debugstr_w(szCodeURL),
429         dwFileVersionMS, dwFileVersionLS, debugstr_w(szContentType), pBindCtx, dwClsContext, pvReserved,
430         debugstr_guid(riid), ppv);
431     return E_NOINTERFACE;
432 }
433
434 /***********************************************************************
435  *           ReleaseBindInfo (URLMON.@)
436  *
437  * Release the resources used by the specified BINDINFO structure.
438  *
439  * PARAMS
440  *  pbindinfo [I] BINDINFO to release.
441  *
442  * RETURNS
443  *  Nothing.
444  */
445 void WINAPI ReleaseBindInfo(BINDINFO* pbindinfo)
446 {
447     DWORD size;
448
449     TRACE("(%p)\n", pbindinfo);
450
451     if(!pbindinfo || !(size = pbindinfo->cbSize))
452         return;
453
454     CoTaskMemFree(pbindinfo->szExtraInfo);
455     ReleaseStgMedium(&pbindinfo->stgmedData);
456
457     if(offsetof(BINDINFO, szExtraInfo) < size)
458         CoTaskMemFree(pbindinfo->szCustomVerb);
459
460     if(pbindinfo->pUnk && offsetof(BINDINFO, pUnk) < size)
461         IUnknown_Release(pbindinfo->pUnk);
462
463     memset(pbindinfo, 0, size);
464     pbindinfo->cbSize = size;
465 }
466
467 /***********************************************************************
468  *           CopyStgMedium (URLMON.@)
469  */
470 HRESULT WINAPI CopyStgMedium(const STGMEDIUM *src, STGMEDIUM *dst)
471 {
472     TRACE("(%p %p)\n", src, dst);
473
474     if(!src || !dst)
475         return E_POINTER;
476
477     *dst = *src;
478
479     switch(dst->tymed) {
480     case TYMED_NULL:
481         break;
482     case TYMED_FILE:
483         if(src->u.lpszFileName && !src->pUnkForRelease) {
484             DWORD size = (strlenW(src->u.lpszFileName)+1)*sizeof(WCHAR);
485             dst->u.lpszFileName = CoTaskMemAlloc(size);
486             memcpy(dst->u.lpszFileName, src->u.lpszFileName, size);
487         }
488         break;
489     case TYMED_ISTREAM:
490         if(dst->u.pstm)
491             IStream_AddRef(dst->u.pstm);
492         break;
493     case TYMED_ISTORAGE:
494         if(dst->u.pstg)
495             IStorage_AddRef(dst->u.pstg);
496         break;
497     default:
498         FIXME("Unimplemented tymed %d\n", src->tymed);
499     }
500
501     if(dst->pUnkForRelease)
502         IUnknown_AddRef(dst->pUnkForRelease);
503
504     return S_OK;
505 }
506
507 static BOOL text_richtext_filter(const BYTE *b, DWORD size)
508 {
509     return size > 5 && !memcmp(b, "{\\rtf", 5);
510 }
511
512 static BOOL text_html_filter(const BYTE *b, DWORD size)
513 {
514     DWORD i;
515
516     if(size < 5)
517         return FALSE;
518
519     for(i=0; i < size-5; i++) {
520         if(b[i] == '<'
521            && (b[i+1] == 'h' || b[i+1] == 'H')
522            && (b[i+2] == 't' || b[i+2] == 'T')
523            && (b[i+3] == 'm' || b[i+3] == 'M')
524            && (b[i+4] == 'l' || b[i+4] == 'L'))
525             return TRUE;
526     }
527
528     return FALSE;
529 }
530
531 static BOOL audio_basic_filter(const BYTE *b, DWORD size)
532 {
533     return size > 4
534         && b[0] == '.' && b[1] == 's' && b[2] == 'n' && b[3] == 'd';
535 }
536
537 static BOOL audio_wav_filter(const BYTE *b, DWORD size)
538 {
539     return size > 12
540         && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
541         && b[8] == 'W' && b[9] == 'A' && b[10] == 'V' && b[11] == 'E';
542 }
543
544 static BOOL image_gif_filter(const BYTE *b, DWORD size)
545 {
546     return size >= 6
547         && (b[0] == 'G' || b[0] == 'g')
548         && (b[1] == 'I' || b[1] == 'i')
549         && (b[2] == 'F' || b[2] == 'f')
550         &&  b[3] == '8'
551         && (b[4] == '7' || b[4] == '9')
552         && (b[5] == 'A' || b[5] == 'a');
553 }
554
555 static BOOL image_pjpeg_filter(const BYTE *b, DWORD size)
556 {
557     return size > 2 && b[0] == 0xff && b[1] == 0xd8;
558 }
559
560 static BOOL image_tiff_filter(const BYTE *b, DWORD size)
561 {
562     return size > 2 && b[0] == 0x4d && b[1] == 0x4d;
563 }
564
565 static BOOL image_xpng_filter(const BYTE *b, DWORD size)
566 {
567     static const BYTE xpng_header[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a};
568     return size > sizeof(xpng_header) && !memcmp(b, xpng_header, sizeof(xpng_header));
569 }
570
571 static BOOL image_bmp_filter(const BYTE *b, DWORD size)
572 {
573     return size >= 14
574         && b[0] == 0x42 && b[1] == 0x4d
575         && *(const DWORD *)(b+6) == 0;
576 }
577
578 static BOOL video_avi_filter(const BYTE *b, DWORD size)
579 {
580     return size > 12
581         && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
582         && b[8] == 'A' && b[9] == 'V' && b[10] == 'I' && b[11] == 0x20;
583 }
584
585 static BOOL video_mpeg_filter(const BYTE *b, DWORD size)
586 {
587     return size > 4
588         && !b[0] && !b[1] && b[2] == 0x01
589         && (b[3] == 0xb3 || b[3] == 0xba);
590 }
591
592 static BOOL application_postscript_filter(const BYTE *b, DWORD size)
593 {
594     return size > 2 && b[0] == '%' && b[1] == '!';
595 }
596
597 static BOOL application_pdf_filter(const BYTE *b, DWORD size)
598 {
599     return size > 4 && b[0] == 0x25 && b[1] == 0x50 && b[2] == 0x44 && b[3] == 0x46;
600 }
601
602 static BOOL application_xzip_filter(const BYTE *b, DWORD size)
603 {
604     return size > 2 && b[0] == 0x50 && b[1] == 0x4b;
605 }
606
607 static BOOL application_xgzip_filter(const BYTE *b, DWORD size)
608 {
609     return size > 2 && b[0] == 0x1f && b[1] == 0x8b;
610 }
611
612 static BOOL application_java_filter(const BYTE *b, DWORD size)
613 {
614     return size > 4 && b[0] == 0xca && b[1] == 0xfe && b[2] == 0xba && b[3] == 0xbe;
615 }
616
617 static BOOL application_xmsdownload(const BYTE *b, DWORD size)
618 {
619     return size > 2 && b[0] == 'M' && b[1] == 'Z';
620 }
621
622 static BOOL text_plain_filter(const BYTE *b, DWORD size)
623 {
624     const BYTE *ptr;
625
626     for(ptr = b; ptr < b+size-1; ptr++) {
627         if(*ptr < 0x20 && *ptr != '\n' && *ptr != '\r' && *ptr != '\t')
628             return FALSE;
629     }
630
631     return TRUE;
632 }
633
634 static BOOL application_octet_stream_filter(const BYTE *b, DWORD size)
635 {
636     return TRUE;
637 }
638
639 /***********************************************************************
640  *           FindMimeFromData (URLMON.@)
641  *
642  * Determines the Multipurpose Internet Mail Extensions (MIME) type from the data provided.
643  */
644 HRESULT WINAPI FindMimeFromData(LPBC pBC, LPCWSTR pwzUrl, LPVOID pBuffer,
645         DWORD cbSize, LPCWSTR pwzMimeProposed, DWORD dwMimeFlags,
646         LPWSTR* ppwzMimeOut, DWORD dwReserved)
647 {
648     TRACE("(%p,%s,%p,%d,%s,0x%x,%p,0x%x)\n", pBC, debugstr_w(pwzUrl), pBuffer, cbSize,
649             debugstr_w(pwzMimeProposed), dwMimeFlags, ppwzMimeOut, dwReserved);
650
651     if(dwMimeFlags)
652         WARN("dwMimeFlags=%08x\n", dwMimeFlags);
653     if(dwReserved)
654         WARN("dwReserved=%d\n", dwReserved);
655
656     /* pBC seams to not be used */
657
658     if(!ppwzMimeOut || (!pwzUrl && !pBuffer))
659         return E_INVALIDARG;
660
661     if(pwzMimeProposed && (!pBuffer || (pBuffer && !cbSize))) {
662         DWORD len;
663
664         if(!pwzMimeProposed)
665             return E_FAIL;
666
667         len = strlenW(pwzMimeProposed)+1;
668         *ppwzMimeOut = CoTaskMemAlloc(len*sizeof(WCHAR));
669         memcpy(*ppwzMimeOut, pwzMimeProposed, len*sizeof(WCHAR));
670         return S_OK;
671     }
672
673     if(pBuffer) {
674         const BYTE *buf = pBuffer;
675         DWORD len;
676         LPCWSTR ret = NULL;
677         unsigned int i;
678
679         static const WCHAR wszTextHtml[] = {'t','e','x','t','/','h','t','m','l',0};
680         static const WCHAR wszTextRichtext[] = {'t','e','x','t','/','r','i','c','h','t','e','x','t',0};
681         static const WCHAR wszAudioBasic[] = {'a','u','d','i','o','/','b','a','s','i','c',0};
682         static const WCHAR wszAudioWav[] = {'a','u','d','i','o','/','w','a','v',0};
683         static const WCHAR wszImageGif[] = {'i','m','a','g','e','/','g','i','f',0};
684         static const WCHAR wszImagePjpeg[] = {'i','m','a','g','e','/','p','j','p','e','g',0};
685         static const WCHAR wszImageTiff[] = {'i','m','a','g','e','/','t','i','f','f',0};
686         static const WCHAR wszImageXPng[] = {'i','m','a','g','e','/','x','-','p','n','g',0};
687         static const WCHAR wszImageBmp[] = {'i','m','a','g','e','/','b','m','p',0};
688         static const WCHAR wszVideoAvi[] = {'v','i','d','e','o','/','a','v','i',0};
689         static const WCHAR wszVideoMpeg[] = {'v','i','d','e','o','/','m','p','e','g',0};
690         static const WCHAR wszAppPostscript[] =
691             {'a','p','p','l','i','c','a','t','i','o','n','/','p','o','s','t','s','c','r','i','p','t',0};
692         static const WCHAR wszAppPdf[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
693             'p','d','f',0};
694         static const WCHAR wszAppXZip[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
695             'x','-','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
696         static const WCHAR wszAppXGzip[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
697             'x','-','g','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
698         static const WCHAR wszAppJava[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
699             'j','a','v','a',0};
700         static const WCHAR wszAppXMSDownload[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
701             'x','-','m','s','d','o','w','n','l','o','a','d',0};
702         static const WCHAR wszTextPlain[] = {'t','e','x','t','/','p','l','a','i','n','\0'};
703         static const WCHAR wszAppOctetStream[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
704             'o','c','t','e','t','-','s','t','r','e','a','m','\0'};
705
706         static const struct {
707             LPCWSTR mime;
708             BOOL (*filter)(const BYTE *,DWORD);
709         } mime_filters[] = {
710             {wszTextHtml,       text_html_filter},
711             {wszTextRichtext,   text_richtext_filter},
712          /* {wszAudioXAiff,     audio_xaiff_filter}, */
713             {wszAudioBasic,     audio_basic_filter},
714             {wszAudioWav,       audio_wav_filter},
715             {wszImageGif,       image_gif_filter},
716             {wszImagePjpeg,     image_pjpeg_filter},
717             {wszImageTiff,      image_tiff_filter},
718             {wszImageXPng,      image_xpng_filter},
719          /* {wszImageXBitmap,   image_xbitmap_filter}, */
720             {wszImageBmp,       image_bmp_filter},
721          /* {wszImageXJg,       image_xjg_filter}, */
722          /* {wszImageXEmf,      image_xemf_filter}, */
723          /* {wszImageXWmf,      image_xwmf_filter}, */
724             {wszVideoAvi,       video_avi_filter},
725             {wszVideoMpeg,      video_mpeg_filter},
726             {wszAppPostscript,  application_postscript_filter},
727          /* {wszAppBase64,      application_base64_filter}, */
728          /* {wszAppMacbinhex40, application_macbinhex40_filter}, */
729             {wszAppPdf,         application_pdf_filter},
730          /* {wszAppXCompressed, application_xcompressed_filter}, */
731             {wszAppXZip,        application_xzip_filter},
732             {wszAppXGzip,       application_xgzip_filter},
733             {wszAppJava,        application_java_filter},
734             {wszAppXMSDownload, application_xmsdownload},
735             {wszTextPlain,      text_plain_filter},
736             {wszAppOctetStream, application_octet_stream_filter}
737         };
738
739         if(!cbSize)
740             return E_FAIL;
741
742         if(pwzMimeProposed && strcmpW(pwzMimeProposed, wszAppOctetStream)) {
743             for(i=0; i < sizeof(mime_filters)/sizeof(*mime_filters); i++) {
744                 if(!strcmpW(pwzMimeProposed, mime_filters[i].mime))
745                     break;
746             }
747
748             if(i == sizeof(mime_filters)/sizeof(*mime_filters)
749                     || mime_filters[i].filter(buf, cbSize)) {
750                 len = strlenW(pwzMimeProposed)+1;
751                 *ppwzMimeOut = CoTaskMemAlloc(len*sizeof(WCHAR));
752                 memcpy(*ppwzMimeOut, pwzMimeProposed, len*sizeof(WCHAR));
753                 return S_OK;
754             }
755         }
756
757         i=0;
758         while(!ret) {
759             if(mime_filters[i].filter(buf, cbSize))
760                 ret = mime_filters[i].mime;
761             i++;
762         }
763
764         TRACE("found %s for data\n"
765               "%02x %02x %02x %02x  %02x %02x %02x %02x  %02x %02x %02x %02x  %02x %02x %02x %02x\n",
766               debugstr_w(ret), buf[0],buf[1],buf[2],buf[3], buf[4],buf[5],buf[6],buf[7],
767               buf[8],buf[9],buf[10],buf[11], buf[12],buf[13],buf[14],buf[15]);
768
769         if(pwzMimeProposed) {
770             if(i == sizeof(mime_filters)/sizeof(*mime_filters))
771                 ret = pwzMimeProposed;
772
773             /* text/html is a special case */
774             if(!strcmpW(pwzMimeProposed, wszTextHtml) && !strcmpW(ret, wszTextPlain))
775                 ret = wszTextHtml;
776         }
777
778         len = strlenW(ret)+1;
779         *ppwzMimeOut = CoTaskMemAlloc(len*sizeof(WCHAR));
780         memcpy(*ppwzMimeOut, ret, len*sizeof(WCHAR));
781         return S_OK;
782     }
783
784     if(pwzUrl) {
785         HKEY hkey;
786         DWORD res, size;
787         LPCWSTR ptr;
788         WCHAR mime[64];
789
790         static const WCHAR wszContentType[] =
791                 {'C','o','n','t','e','n','t',' ','T','y','p','e','\0'};
792
793         ptr = strrchrW(pwzUrl, '.');
794         if(!ptr)
795             return E_FAIL;
796
797         res = RegOpenKeyW(HKEY_CLASSES_ROOT, ptr, &hkey);
798         if(res != ERROR_SUCCESS)
799             return HRESULT_FROM_WIN32(res);
800
801         size = sizeof(mime);
802         res = RegQueryValueExW(hkey, wszContentType, NULL, NULL, (LPBYTE)mime, &size);
803         RegCloseKey(hkey);
804         if(res != ERROR_SUCCESS)
805             return HRESULT_FROM_WIN32(res);
806
807         *ppwzMimeOut = CoTaskMemAlloc(size);
808         memcpy(*ppwzMimeOut, mime, size);
809         return S_OK;
810     }
811
812     return E_FAIL;
813 }
814
815 /***********************************************************************
816  *           GetClassFileOrMime (URLMON.@)
817  *
818  * Determines the class ID from the bind context, file name or MIME type.
819  */
820 HRESULT WINAPI GetClassFileOrMime(LPBC pBC, LPCWSTR pszFilename,
821         LPVOID pBuffer, DWORD cbBuffer, LPCWSTR pszMimeType, DWORD dwReserved,
822         CLSID *pclsid)
823 {
824     FIXME("(%p, %s, %p, %d, %p, 0x%08x, %p): stub\n", pBC,
825         debugstr_w(pszFilename), pBuffer, cbBuffer, debugstr_w(pszMimeType),
826         dwReserved, pclsid);
827     return E_NOTIMPL;
828 }
829
830 /***********************************************************************
831  * Extract (URLMON.@)
832  */
833 HRESULT WINAPI Extract(void *dest, LPCSTR szCabName)
834 {
835     HRESULT (WINAPI *pExtract)(void *, LPCSTR);
836
837     if (!hCabinet)
838         hCabinet = LoadLibraryA("cabinet.dll");
839
840     if (!hCabinet) return HRESULT_FROM_WIN32(GetLastError());
841     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
842     if (!pExtract) return HRESULT_FROM_WIN32(GetLastError());
843
844     return pExtract(dest, szCabName);
845 }
846
847 /***********************************************************************
848  *           IsLoggingEnabledA (URLMON.@)
849  */
850 BOOL WINAPI IsLoggingEnabledA(LPCSTR url)
851 {
852     FIXME("(%s)\n", debugstr_a(url));
853     return FALSE;
854 }
855
856 /***********************************************************************
857  *           IsLoggingEnabledW (URLMON.@)
858  */
859 BOOL WINAPI IsLoggingEnabledW(LPCWSTR url)
860 {
861     FIXME("(%s)\n", debugstr_w(url));
862     return FALSE;
863 }