urlmon: Use ifaces instead of vtbl pointers in FtpProtocol.
[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 static HRESULT find_mime_from_buffer(const BYTE *buf, DWORD size, const WCHAR *proposed_mime, WCHAR **ret_mime)
640 {
641     LPCWSTR ret = NULL;
642     DWORD len, i;
643
644     static const WCHAR text_htmlW[] = {'t','e','x','t','/','h','t','m','l',0};
645     static const WCHAR text_richtextW[] = {'t','e','x','t','/','r','i','c','h','t','e','x','t',0};
646     static const WCHAR audio_basicW[] = {'a','u','d','i','o','/','b','a','s','i','c',0};
647     static const WCHAR audio_wavW[] = {'a','u','d','i','o','/','w','a','v',0};
648     static const WCHAR image_gifW[] = {'i','m','a','g','e','/','g','i','f',0};
649     static const WCHAR image_pjpegW[] = {'i','m','a','g','e','/','p','j','p','e','g',0};
650     static const WCHAR image_tiffW[] = {'i','m','a','g','e','/','t','i','f','f',0};
651     static const WCHAR image_xpngW[] = {'i','m','a','g','e','/','x','-','p','n','g',0};
652     static const WCHAR image_bmpW[] = {'i','m','a','g','e','/','b','m','p',0};
653     static const WCHAR video_aviW[] = {'v','i','d','e','o','/','a','v','i',0};
654     static const WCHAR video_mpegW[] = {'v','i','d','e','o','/','m','p','e','g',0};
655     static const WCHAR app_postscriptW[] =
656         {'a','p','p','l','i','c','a','t','i','o','n','/','p','o','s','t','s','c','r','i','p','t',0};
657     static const WCHAR app_pdfW[] = {'a','p','p','l','i','c','a','t','i','o','n','/','p','d','f',0};
658     static const WCHAR app_xzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
659         'x','-','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
660     static const WCHAR app_xgzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
661         'x','-','g','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
662     static const WCHAR app_javaW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
663         'j','a','v','a',0};
664     static const WCHAR app_xmsdownloadW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
665         'x','-','m','s','d','o','w','n','l','o','a','d',0};
666     static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n','\0'};
667     static const WCHAR app_octetstreamW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
668         'o','c','t','e','t','-','s','t','r','e','a','m','\0'};
669
670     static const struct {
671         LPCWSTR mime;
672         BOOL (*filter)(const BYTE *,DWORD);
673     } mime_filters[] = {
674         {text_htmlW,       text_html_filter},
675         {text_richtextW,   text_richtext_filter},
676      /* {audio_xaiffW,     audio_xaiff_filter}, */
677         {audio_basicW,     audio_basic_filter},
678         {audio_wavW,       audio_wav_filter},
679         {image_gifW,       image_gif_filter},
680         {image_pjpegW,     image_pjpeg_filter},
681         {image_tiffW,      image_tiff_filter},
682         {image_xpngW,      image_xpng_filter},
683      /* {image_xbitmapW,   image_xbitmap_filter}, */
684         {image_bmpW,       image_bmp_filter},
685      /* {image_xjgW,       image_xjg_filter}, */
686      /* {image_xemfW,      image_xemf_filter}, */
687      /* {image_xwmfW,      image_xwmf_filter}, */
688         {video_aviW,       video_avi_filter},
689         {video_mpegW,      video_mpeg_filter},
690         {app_postscriptW,  application_postscript_filter},
691      /* {app_base64W,      application_base64_filter}, */
692      /* {app_macbinhex40W, application_macbinhex40_filter}, */
693         {app_pdfW,         application_pdf_filter},
694      /* {app_zcompressedW, application_xcompressed_filter}, */
695         {app_xzipW,        application_xzip_filter},
696         {app_xgzipW,       application_xgzip_filter},
697         {app_javaW,        application_java_filter},
698         {app_xmsdownloadW, application_xmsdownload},
699         {text_plainW,      text_plain_filter},
700         {app_octetstreamW, application_octet_stream_filter}
701     };
702
703     if(!buf || !size) {
704         if(!proposed_mime)
705             return E_FAIL;
706
707         len = strlenW(proposed_mime)+1;
708         *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
709         if(!*ret_mime)
710             return E_OUTOFMEMORY;
711
712         memcpy(*ret_mime, proposed_mime, len*sizeof(WCHAR));
713         return S_OK;
714     }
715
716     if(proposed_mime && strcmpW(proposed_mime, app_octetstreamW)) {
717         for(i=0; i < sizeof(mime_filters)/sizeof(*mime_filters); i++) {
718             if(!strcmpW(proposed_mime, mime_filters[i].mime))
719                 break;
720         }
721
722         if(i == sizeof(mime_filters)/sizeof(*mime_filters) || mime_filters[i].filter(buf, size)) {
723             len = strlenW(proposed_mime)+1;
724             *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
725             if(!*ret_mime)
726                 return E_OUTOFMEMORY;
727
728             memcpy(*ret_mime, proposed_mime, len*sizeof(WCHAR));
729             return S_OK;
730         }
731     }
732
733     i=0;
734     while(!ret) {
735         if(mime_filters[i].filter(buf, size))
736             ret = mime_filters[i].mime;
737         i++;
738     }
739
740     TRACE("found %s for %s\n", debugstr_w(ret), debugstr_an((const char*)buf, min(32, size)));
741
742     if(proposed_mime) {
743         if(i == sizeof(mime_filters)/sizeof(*mime_filters))
744             ret = proposed_mime;
745
746         /* text/html is a special case */
747         if(!strcmpW(proposed_mime, text_htmlW) && !strcmpW(ret, text_plainW))
748             ret = text_htmlW;
749     }
750
751     len = strlenW(ret)+1;
752     *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
753     if(!*ret_mime)
754         return E_OUTOFMEMORY;
755
756     memcpy(*ret_mime, ret, len*sizeof(WCHAR));
757     return S_OK;
758 }
759
760 /***********************************************************************
761  *           FindMimeFromData (URLMON.@)
762  *
763  * Determines the Multipurpose Internet Mail Extensions (MIME) type from the data provided.
764  */
765 HRESULT WINAPI FindMimeFromData(LPBC pBC, LPCWSTR pwzUrl, LPVOID pBuffer,
766         DWORD cbSize, LPCWSTR pwzMimeProposed, DWORD dwMimeFlags,
767         LPWSTR* ppwzMimeOut, DWORD dwReserved)
768 {
769     TRACE("(%p,%s,%p,%d,%s,0x%x,%p,0x%x)\n", pBC, debugstr_w(pwzUrl), pBuffer, cbSize,
770             debugstr_w(pwzMimeProposed), dwMimeFlags, ppwzMimeOut, dwReserved);
771
772     if(dwMimeFlags)
773         WARN("dwMimeFlags=%08x\n", dwMimeFlags);
774     if(dwReserved)
775         WARN("dwReserved=%d\n", dwReserved);
776
777     /* pBC seams to not be used */
778
779     if(!ppwzMimeOut || (!pwzUrl && !pBuffer))
780         return E_INVALIDARG;
781
782     if(pwzMimeProposed || pBuffer)
783         return find_mime_from_buffer(pBuffer, cbSize, pwzMimeProposed, ppwzMimeOut);
784
785     if(pwzUrl) {
786         HKEY hkey;
787         DWORD res, size;
788         LPCWSTR ptr;
789         WCHAR mime[64];
790
791         static const WCHAR wszContentType[] =
792                 {'C','o','n','t','e','n','t',' ','T','y','p','e','\0'};
793
794         ptr = strrchrW(pwzUrl, '.');
795         if(!ptr)
796             return E_FAIL;
797
798         res = RegOpenKeyW(HKEY_CLASSES_ROOT, ptr, &hkey);
799         if(res != ERROR_SUCCESS)
800             return HRESULT_FROM_WIN32(res);
801
802         size = sizeof(mime);
803         res = RegQueryValueExW(hkey, wszContentType, NULL, NULL, (LPBYTE)mime, &size);
804         RegCloseKey(hkey);
805         if(res != ERROR_SUCCESS)
806             return HRESULT_FROM_WIN32(res);
807
808         *ppwzMimeOut = CoTaskMemAlloc(size);
809         memcpy(*ppwzMimeOut, mime, size);
810         return S_OK;
811     }
812
813     return E_FAIL;
814 }
815
816 /***********************************************************************
817  *           GetClassFileOrMime (URLMON.@)
818  *
819  * Determines the class ID from the bind context, file name or MIME type.
820  */
821 HRESULT WINAPI GetClassFileOrMime(LPBC pBC, LPCWSTR pszFilename,
822         LPVOID pBuffer, DWORD cbBuffer, LPCWSTR pszMimeType, DWORD dwReserved,
823         CLSID *pclsid)
824 {
825     FIXME("(%p, %s, %p, %d, %p, 0x%08x, %p): stub\n", pBC,
826         debugstr_w(pszFilename), pBuffer, cbBuffer, debugstr_w(pszMimeType),
827         dwReserved, pclsid);
828     return E_NOTIMPL;
829 }
830
831 /***********************************************************************
832  * Extract (URLMON.@)
833  */
834 HRESULT WINAPI Extract(void *dest, LPCSTR szCabName)
835 {
836     HRESULT (WINAPI *pExtract)(void *, LPCSTR);
837
838     if (!hCabinet)
839         hCabinet = LoadLibraryA("cabinet.dll");
840
841     if (!hCabinet) return HRESULT_FROM_WIN32(GetLastError());
842     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
843     if (!pExtract) return HRESULT_FROM_WIN32(GetLastError());
844
845     return pExtract(dest, szCabName);
846 }
847
848 /***********************************************************************
849  *           IsLoggingEnabledA (URLMON.@)
850  */
851 BOOL WINAPI IsLoggingEnabledA(LPCSTR url)
852 {
853     FIXME("(%s)\n", debugstr_a(url));
854     return FALSE;
855 }
856
857 /***********************************************************************
858  *           IsLoggingEnabledW (URLMON.@)
859  */
860 BOOL WINAPI IsLoggingEnabledW(LPCWSTR url)
861 {
862     FIXME("(%s)\n", debugstr_w(url));
863     return FALSE;
864 }