xref: /reactos/dll/win32/urlmon/mimefilter.c (revision 2196a06f)
1 /*
2  * Copyright 2009 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "urlmon_main.h"
20 #include "wine/debug.h"
21 
22 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
23 
24 typedef struct {
25     IInternetProtocol     IInternetProtocol_iface;
26     IInternetProtocolSink IInternetProtocolSink_iface;
27 
28     LONG ref;
29 } MimeFilter;
30 
31 static inline MimeFilter *impl_from_IInternetProtocol(IInternetProtocol *iface)
32 {
33     return CONTAINING_RECORD(iface, MimeFilter, IInternetProtocol_iface);
34 }
35 
36 static HRESULT WINAPI MimeFilterProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
37 {
38     MimeFilter *This = impl_from_IInternetProtocol(iface);
39 
40     *ppv = NULL;
41     if(IsEqualGUID(&IID_IUnknown, riid)) {
42         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
43         *ppv = &This->IInternetProtocol_iface;
44     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
45         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
46         *ppv = &This->IInternetProtocol_iface;
47     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
48         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
49         *ppv = &This->IInternetProtocol_iface;
50     }else if(IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
51         TRACE("(%p)->(IID_IInternetProtocolSink %p)\n", This, ppv);
52         *ppv = &This->IInternetProtocolSink_iface;
53     }
54 
55     if(*ppv) {
56         IInternetProtocol_AddRef(iface);
57         return S_OK;
58     }
59 
60     WARN("not supported interface %s\n", debugstr_guid(riid));
61     return E_NOINTERFACE;
62 }
63 
64 static ULONG WINAPI MimeFilterProtocol_AddRef(IInternetProtocol *iface)
65 {
66     MimeFilter *This = impl_from_IInternetProtocol(iface);
67     LONG ref = InterlockedIncrement(&This->ref);
68     TRACE("(%p) ref=%d\n", This, ref);
69     return ref;
70 }
71 
72 static ULONG WINAPI MimeFilterProtocol_Release(IInternetProtocol *iface)
73 {
74     MimeFilter *This = impl_from_IInternetProtocol(iface);
75     LONG ref = InterlockedDecrement(&This->ref);
76 
77     TRACE("(%p) ref=%d\n", This, ref);
78 
79     if(!ref) {
80         heap_free(This);
81 
82         URLMON_UnlockModule();
83     }
84 
85     return ref;
86 }
87 
88 static HRESULT WINAPI MimeFilterProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
89         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
90         DWORD grfPI, HANDLE_PTR dwReserved)
91 {
92     MimeFilter *This = impl_from_IInternetProtocol(iface);
93     FIXME("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
94           pOIBindInfo, grfPI, dwReserved);
95     return E_NOTIMPL;
96 }
97 
98 static HRESULT WINAPI MimeFilterProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
99 {
100     MimeFilter *This = impl_from_IInternetProtocol(iface);
101     FIXME("(%p)->(%p)\n", This, pProtocolData);
102     return E_NOTIMPL;
103 }
104 
105 static HRESULT WINAPI MimeFilterProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
106         DWORD dwOptions)
107 {
108     MimeFilter *This = impl_from_IInternetProtocol(iface);
109     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
110     return E_NOTIMPL;
111 }
112 
113 static HRESULT WINAPI MimeFilterProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
114 {
115     MimeFilter *This = impl_from_IInternetProtocol(iface);
116     FIXME("(%p)->(%08x)\n", This, dwOptions);
117     return E_NOTIMPL;
118 }
119 
120 static HRESULT WINAPI MimeFilterProtocol_Suspend(IInternetProtocol *iface)
121 {
122     MimeFilter *This = impl_from_IInternetProtocol(iface);
123     FIXME("(%p)\n", This);
124     return E_NOTIMPL;
125 }
126 
127 static HRESULT WINAPI MimeFilterProtocol_Resume(IInternetProtocol *iface)
128 {
129     MimeFilter *This = impl_from_IInternetProtocol(iface);
130     FIXME("(%p)\n", This);
131     return E_NOTIMPL;
132 }
133 
134 static HRESULT WINAPI MimeFilterProtocol_Read(IInternetProtocol *iface, void *pv,
135         ULONG cb, ULONG *pcbRead)
136 {
137     MimeFilter *This = impl_from_IInternetProtocol(iface);
138     FIXME("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
139     return E_NOTIMPL;
140 }
141 
142 static HRESULT WINAPI MimeFilterProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
143         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
144 {
145     MimeFilter *This = impl_from_IInternetProtocol(iface);
146     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
147     return E_NOTIMPL;
148 }
149 
150 static HRESULT WINAPI MimeFilterProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
151 {
152     MimeFilter *This = impl_from_IInternetProtocol(iface);
153     FIXME("(%p)->(%08x)\n", This, dwOptions);
154     return E_NOTIMPL;
155 }
156 
157 static HRESULT WINAPI MimeFilterProtocol_UnlockRequest(IInternetProtocol *iface)
158 {
159     MimeFilter *This = impl_from_IInternetProtocol(iface);
160     FIXME("(%p)\n", This);
161     return E_NOTIMPL;
162 }
163 
164 static const IInternetProtocolVtbl MimeFilterProtocolVtbl = {
165     MimeFilterProtocol_QueryInterface,
166     MimeFilterProtocol_AddRef,
167     MimeFilterProtocol_Release,
168     MimeFilterProtocol_Start,
169     MimeFilterProtocol_Continue,
170     MimeFilterProtocol_Abort,
171     MimeFilterProtocol_Terminate,
172     MimeFilterProtocol_Suspend,
173     MimeFilterProtocol_Resume,
174     MimeFilterProtocol_Read,
175     MimeFilterProtocol_Seek,
176     MimeFilterProtocol_LockRequest,
177     MimeFilterProtocol_UnlockRequest
178 };
179 
180 static inline MimeFilter *impl_from_IInternetProtocolSink(IInternetProtocolSink *iface)
181 {
182     return CONTAINING_RECORD(iface, MimeFilter, IInternetProtocolSink_iface);
183 }
184 
185 static HRESULT WINAPI MimeFilterSink_QueryInterface(IInternetProtocolSink *iface,
186         REFIID riid, void **ppv)
187 {
188     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
189     return IInternetProtocol_QueryInterface(&This->IInternetProtocol_iface, riid, ppv);
190 }
191 
192 static ULONG WINAPI MimeFilterSink_AddRef(IInternetProtocolSink *iface)
193 {
194     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
195     return IInternetProtocol_AddRef(&This->IInternetProtocol_iface);
196 }
197 
198 static ULONG WINAPI MimeFilterSink_Release(IInternetProtocolSink *iface)
199 {
200     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
201     return IInternetProtocol_Release(&This->IInternetProtocol_iface);
202 }
203 
204 static HRESULT WINAPI MimeFilterSink_Switch(IInternetProtocolSink *iface,
205         PROTOCOLDATA *pProtocolData)
206 {
207     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
208     FIXME("(%p)->(%p)\n", This, pProtocolData);
209     return E_NOTIMPL;
210 }
211 
212 static HRESULT WINAPI MimeFilterSink_ReportProgress(IInternetProtocolSink *iface,
213         ULONG ulStatusCode, LPCWSTR szStatusText)
214 {
215     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
216     FIXME("(%p)->(%u %s)\n", This, ulStatusCode, debugstr_w(szStatusText));
217     return E_NOTIMPL;
218 }
219 
220 static HRESULT WINAPI MimeFilterSink_ReportData(IInternetProtocolSink *iface,
221         DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
222 {
223     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
224     FIXME("(%p)->(%d %u %u)\n", This, grfBSCF, ulProgress, ulProgressMax);
225     return E_NOTIMPL;
226 }
227 
228 static HRESULT WINAPI MimeFilterSink_ReportResult(IInternetProtocolSink *iface,
229         HRESULT hrResult, DWORD dwError, LPCWSTR szResult)
230 {
231     MimeFilter *This = impl_from_IInternetProtocolSink(iface);
232     FIXME("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult));
233     return E_NOTIMPL;
234 }
235 
236 static const IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = {
237     MimeFilterSink_QueryInterface,
238     MimeFilterSink_AddRef,
239     MimeFilterSink_Release,
240     MimeFilterSink_Switch,
241     MimeFilterSink_ReportProgress,
242     MimeFilterSink_ReportData,
243     MimeFilterSink_ReportResult
244 };
245 
246 HRESULT MimeFilter_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
247 {
248     MimeFilter *ret;
249 
250     TRACE("(%p %p)\n", pUnkOuter, ppobj);
251 
252     URLMON_LockModule();
253 
254     ret = heap_alloc_zero(sizeof(MimeFilter));
255 
256     ret->IInternetProtocol_iface.lpVtbl = &MimeFilterProtocolVtbl;
257     ret->IInternetProtocolSink_iface.lpVtbl = &InternetProtocolSinkVtbl;
258     ret->ref = 1;
259 
260     *ppobj = &ret->IInternetProtocol_iface;
261     return S_OK;
262 }
263 
264 static BOOL text_richtext_filter(const BYTE *b, DWORD size)
265 {
266     return size > 5 && !memcmp(b, "{\\rtf", 5);
267 }
268 
269 static BOOL text_html_filter(const BYTE *b, DWORD size)
270 {
271     if(size < 6)
272         return FALSE;
273 
274     if((b[0] == '<'
275                 && (b[1] == 'h' || b[1] == 'H')
276                 && (b[2] == 't' || b[2] == 'T')
277                 && (b[3] == 'm' || b[3] == 'M')
278                 && (b[4] == 'l' || b[4] == 'L'))
279             || (b[0] == '<'
280                 && (b[1] == 'h' || b[1] == 'H')
281                 && (b[2] == 'e' || b[2] == 'E')
282                 && (b[3] == 'a' || b[3] == 'A')
283                 && (b[4] == 'd' || b[4] == 'D'))
284             || (b[0] == '<'
285                 && (b[1] == 'b' || b[1] == 'B')
286                 && (b[2] == 'o' || b[2] == 'O')
287                 && (b[3] == 'd' || b[3] == 'D')
288                 && (b[4] == 'y' || b[4] == 'Y'))) return TRUE;
289 
290     return FALSE;
291 }
292 
293 static BOOL text_xml_filter(const BYTE *b, DWORD size)
294 {
295     if(size < 7)
296         return FALSE;
297 
298     if(b[0] == '<' && b[1] == '?'
299             && (b[2] == 'x' || b[2] == 'X')
300             && (b[3] == 'm' || b[3] == 'M')
301             && (b[4] == 'l' || b[4] == 'L')
302             && b[5] == ' ') return TRUE;
303 
304     return FALSE;
305 }
306 
307 static BOOL audio_basic_filter(const BYTE *b, DWORD size)
308 {
309     return size > 4
310         && b[0] == '.' && b[1] == 's' && b[2] == 'n' && b[3] == 'd';
311 }
312 
313 static BOOL audio_wav_filter(const BYTE *b, DWORD size)
314 {
315     return size > 12
316         && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
317         && b[8] == 'W' && b[9] == 'A' && b[10] == 'V' && b[11] == 'E';
318 }
319 
320 static BOOL image_gif_filter(const BYTE *b, DWORD size)
321 {
322     return size >= 6
323         && (b[0] == 'G' || b[0] == 'g')
324         && (b[1] == 'I' || b[1] == 'i')
325         && (b[2] == 'F' || b[2] == 'f')
326         &&  b[3] == '8'
327         && (b[4] == '7' || b[4] == '9')
328         && (b[5] == 'A' || b[5] == 'a');
329 }
330 
331 static BOOL image_pjpeg_filter(const BYTE *b, DWORD size)
332 {
333     return size > 2 && b[0] == 0xff && b[1] == 0xd8;
334 }
335 
336 static BOOL image_tiff_filter(const BYTE *b, DWORD size)
337 {
338     static const BYTE magic1[] = {0x4d,0x4d,0x00,0x2a};
339     static const BYTE magic2[] = {0x49,0x49,0x2a,0xff};
340 
341     return size >= 4 && (!memcmp(b, magic1, 4) || !memcmp(b, magic2, 4));
342 }
343 
344 static BOOL image_xpng_filter(const BYTE *b, DWORD size)
345 {
346     static const BYTE xpng_header[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a};
347     return size > sizeof(xpng_header) && !memcmp(b, xpng_header, sizeof(xpng_header));
348 }
349 
350 static BOOL image_bmp_filter(const BYTE *b, DWORD size)
351 {
352     return size >= 14
353         && b[0] == 0x42 && b[1] == 0x4d
354         && *(const DWORD *)(b+6) == 0;
355 }
356 
357 static BOOL video_avi_filter(const BYTE *b, DWORD size)
358 {
359     return size > 12
360         && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
361         && b[8] == 'A' && b[9] == 'V' && b[10] == 'I' && b[11] == 0x20;
362 }
363 
364 static BOOL video_mpeg_filter(const BYTE *b, DWORD size)
365 {
366     return size > 4
367         && !b[0] && !b[1] && b[2] == 0x01
368         && (b[3] == 0xb3 || b[3] == 0xba);
369 }
370 
371 static BOOL application_postscript_filter(const BYTE *b, DWORD size)
372 {
373     return size > 2 && b[0] == '%' && b[1] == '!';
374 }
375 
376 static BOOL application_pdf_filter(const BYTE *b, DWORD size)
377 {
378     return size > 4 && b[0] == 0x25 && b[1] == 0x50 && b[2] == 0x44 && b[3] == 0x46;
379 }
380 
381 static BOOL application_xzip_filter(const BYTE *b, DWORD size)
382 {
383     return size > 2 && b[0] == 0x50 && b[1] == 0x4b;
384 }
385 
386 static BOOL application_xgzip_filter(const BYTE *b, DWORD size)
387 {
388     return size > 2 && b[0] == 0x1f && b[1] == 0x8b;
389 }
390 
391 static BOOL application_java_filter(const BYTE *b, DWORD size)
392 {
393     return size > 4 && b[0] == 0xca && b[1] == 0xfe && b[2] == 0xba && b[3] == 0xbe;
394 }
395 
396 static BOOL application_xmsdownload(const BYTE *b, DWORD size)
397 {
398     return size > 2 && b[0] == 'M' && b[1] == 'Z';
399 }
400 
401 static inline BOOL is_text_plain_char(BYTE b)
402 {
403     if(b < 0x20 && b != '\n' && b != '\r' && b != '\t')
404         return FALSE;
405     return TRUE;
406 }
407 
408 static BOOL text_plain_filter(const BYTE *b, DWORD size)
409 {
410     const BYTE *ptr;
411 
412     for(ptr = b; ptr < b+size-1; ptr++) {
413         if(!is_text_plain_char(*ptr))
414             return FALSE;
415     }
416 
417     return TRUE;
418 }
419 
420 static BOOL application_octet_stream_filter(const BYTE *b, DWORD size)
421 {
422     return TRUE;
423 }
424 
425 HRESULT find_mime_from_ext(const WCHAR *ext, WCHAR **ret)
426 {
427     DWORD res, size;
428     WCHAR mime[64];
429     HKEY hkey;
430 
431     static const WCHAR content_typeW[] = {'C','o','n','t','e','n','t',' ','T','y','p','e','\0'};
432 
433     res = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey);
434     if(res != ERROR_SUCCESS)
435         return HRESULT_FROM_WIN32(res);
436 
437     size = sizeof(mime);
438     res = RegQueryValueExW(hkey, content_typeW, NULL, NULL, (LPBYTE)mime, &size);
439     RegCloseKey(hkey);
440     if(res != ERROR_SUCCESS)
441         return HRESULT_FROM_WIN32(res);
442 
443     TRACE("found MIME %s\n", debugstr_w(mime));
444 
445     *ret = CoTaskMemAlloc(size);
446     memcpy(*ret, mime, size);
447     return S_OK;
448 }
449 
450 static HRESULT find_mime_from_url(const WCHAR *url, WCHAR **ret)
451 {
452     const WCHAR *ptr, *end_ptr;
453     WCHAR *ext = NULL;
454     HRESULT hres;
455 
456     for(end_ptr = url; *end_ptr; end_ptr++) {
457         if(*end_ptr == '?' || *end_ptr == '#')
458             break;
459     }
460 
461     for(ptr = end_ptr; ptr >= url; ptr--) {
462         if(*ptr == '.')
463             break;
464     }
465 
466     if(ptr < url)
467         return E_FAIL;
468 
469     if(*end_ptr) {
470         unsigned len = end_ptr-ptr;
471 
472         ext = heap_alloc((len+1)*sizeof(WCHAR));
473         if(!ext)
474             return E_OUTOFMEMORY;
475 
476         memcpy(ext, ptr, len*sizeof(WCHAR));
477         ext[len] = 0;
478     }
479 
480     hres = find_mime_from_ext(ext ? ext : ptr, ret);
481     heap_free(ext);
482     return hres;
483 }
484 
485 static const WCHAR text_htmlW[] = {'t','e','x','t','/','h','t','m','l',0};
486 static const WCHAR text_richtextW[] = {'t','e','x','t','/','r','i','c','h','t','e','x','t',0};
487 static const WCHAR text_xmlW[] = {'t','e','x','t','/','x','m','l',0};
488 static const WCHAR audio_basicW[] = {'a','u','d','i','o','/','b','a','s','i','c',0};
489 static const WCHAR audio_wavW[] = {'a','u','d','i','o','/','w','a','v',0};
490 static const WCHAR image_gifW[] = {'i','m','a','g','e','/','g','i','f',0};
491 static const WCHAR image_pjpegW[] = {'i','m','a','g','e','/','p','j','p','e','g',0};
492 static const WCHAR image_tiffW[] = {'i','m','a','g','e','/','t','i','f','f',0};
493 static const WCHAR image_xpngW[] = {'i','m','a','g','e','/','x','-','p','n','g',0};
494 static const WCHAR image_bmpW[] = {'i','m','a','g','e','/','b','m','p',0};
495 static const WCHAR video_aviW[] = {'v','i','d','e','o','/','a','v','i',0};
496 static const WCHAR video_mpegW[] = {'v','i','d','e','o','/','m','p','e','g',0};
497 static const WCHAR app_postscriptW[] =
498         {'a','p','p','l','i','c','a','t','i','o','n','/','p','o','s','t','s','c','r','i','p','t',0};
499 static const WCHAR app_pdfW[] = {'a','p','p','l','i','c','a','t','i','o','n','/','p','d','f',0};
500 static const WCHAR app_xzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
501         'x','-','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
502 static const WCHAR app_xgzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
503         'x','-','g','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0};
504 static const WCHAR app_javaW[] = {'a','p','p','l','i','c','a','t','i','o','n','/','j','a','v','a',0};
505 static const WCHAR app_xmsdownloadW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
506         'x','-','m','s','d','o','w','n','l','o','a','d',0};
507 static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n','\0'};
508 static const WCHAR app_octetstreamW[] = {'a','p','p','l','i','c','a','t','i','o','n','/',
509         'o','c','t','e','t','-','s','t','r','e','a','m','\0'};
510 
511 static const struct {
512     const WCHAR *mime;
513     BOOL (*filter)(const BYTE *,DWORD);
514 } mime_filters_any_pos[] = {
515     {text_htmlW,       text_html_filter},
516     {text_xmlW,        text_xml_filter}
517 }, mime_filters[] = {
518     {text_richtextW,   text_richtext_filter},
519  /* {audio_xaiffW,     audio_xaiff_filter}, */
520     {audio_basicW,     audio_basic_filter},
521     {audio_wavW,       audio_wav_filter},
522     {image_gifW,       image_gif_filter},
523     {image_pjpegW,     image_pjpeg_filter},
524     {image_tiffW,      image_tiff_filter},
525     {image_xpngW,      image_xpng_filter},
526  /* {image_xbitmapW,   image_xbitmap_filter}, */
527     {image_bmpW,       image_bmp_filter},
528  /* {image_xjgW,       image_xjg_filter}, */
529  /* {image_xemfW,      image_xemf_filter}, */
530  /* {image_xwmfW,      image_xwmf_filter}, */
531     {video_aviW,       video_avi_filter},
532     {video_mpegW,      video_mpeg_filter},
533     {app_postscriptW,  application_postscript_filter},
534  /* {app_base64W,      application_base64_filter}, */
535  /* {app_macbinhex40W, application_macbinhex40_filter}, */
536     {app_pdfW,         application_pdf_filter},
537  /* {app_zcompressedW, application_xcompressed_filter}, */
538     {app_xzipW,        application_xzip_filter},
539     {app_xgzipW,       application_xgzip_filter},
540     {app_javaW,        application_java_filter},
541     {app_xmsdownloadW, application_xmsdownload},
542     {text_plainW,      text_plain_filter},
543     {app_octetstreamW, application_octet_stream_filter}
544 };
545 
546 static BOOL is_known_mime_type(const WCHAR *mime)
547 {
548     unsigned i;
549 
550     for(i=0; i < ARRAY_SIZE(mime_filters_any_pos); i++) {
551         if(!wcscmp(mime, mime_filters_any_pos[i].mime))
552             return TRUE;
553     }
554 
555     for(i=0; i < ARRAY_SIZE(mime_filters); i++) {
556         if(!wcscmp(mime, mime_filters[i].mime))
557             return TRUE;
558     }
559 
560     return FALSE;
561 }
562 
563 static HRESULT find_mime_from_buffer(const BYTE *buf, DWORD size, const WCHAR *proposed_mime, const WCHAR *url, WCHAR **ret_mime)
564 {
565     int len, i, any_pos_mime = -1;
566     const WCHAR *ret = NULL;
567 
568     if(!buf || !size) {
569         if(!proposed_mime)
570             return E_FAIL;
571 
572         len = lstrlenW(proposed_mime)+1;
573         *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
574         if(!*ret_mime)
575             return E_OUTOFMEMORY;
576 
577         memcpy(*ret_mime, proposed_mime, len*sizeof(WCHAR));
578         return S_OK;
579     }
580 
581     if(proposed_mime && (!wcscmp(proposed_mime, app_octetstreamW)
582                 || !wcscmp(proposed_mime, text_plainW)))
583         proposed_mime = NULL;
584 
585     if(proposed_mime) {
586         ret = proposed_mime;
587 
588         for(i=0; i < ARRAY_SIZE(mime_filters_any_pos); i++) {
589             if(!wcscmp(proposed_mime, mime_filters_any_pos[i].mime)) {
590                 any_pos_mime = i;
591                 for(len=size; len>0; len--) {
592                     if(mime_filters_any_pos[i].filter(buf+size-len, len))
593                         break;
594                 }
595                 if(!len)
596                     ret = NULL;
597                 break;
598             }
599         }
600 
601         if(i == ARRAY_SIZE(mime_filters_any_pos)) {
602             for(i=0; i < ARRAY_SIZE(mime_filters); i++) {
603                 if(!wcscmp(proposed_mime, mime_filters[i].mime)) {
604                     if(!mime_filters[i].filter(buf, size))
605                         ret = NULL;
606                     break;
607                 }
608             }
609         }
610     }
611 
612     /* Looks like a bug in native implementation, html and xml mimes
613      * are not looked for if none of them was proposed */
614     if(!proposed_mime || any_pos_mime!=-1) {
615         for(len=size; !ret && len>0; len--) {
616             for(i=0; i<ARRAY_SIZE(mime_filters_any_pos); i++) {
617                 if(mime_filters_any_pos[i].filter(buf+size-len, len)) {
618                     ret = mime_filters_any_pos[i].mime;
619                     break;
620                 }
621             }
622         }
623     }
624 
625     i=0;
626     while(!ret) {
627         if(mime_filters[i].filter(buf, size))
628             ret = mime_filters[i].mime;
629         i++;
630     }
631 
632     if(any_pos_mime!=-1 && ret==text_plainW)
633         ret = mime_filters_any_pos[any_pos_mime].mime;
634     else if(proposed_mime && ret==app_octetstreamW) {
635         for(len=size; ret==app_octetstreamW && len>0; len--) {
636             if(!is_text_plain_char(buf[size-len]))
637                 break;
638             for(i=0; i<ARRAY_SIZE(mime_filters_any_pos); i++) {
639                 if(mime_filters_any_pos[i].filter(buf+size-len, len)) {
640                     ret = text_plainW;
641                     break;
642                 }
643             }
644         }
645 
646         if(ret == app_octetstreamW)
647             ret = proposed_mime;
648     }
649 
650     if(url && (ret == app_octetstreamW || ret == text_plainW)) {
651         WCHAR *url_mime;
652         HRESULT hres;
653 
654         hres = find_mime_from_url(url, &url_mime);
655         if(SUCCEEDED(hres)) {
656             if(!is_known_mime_type(url_mime)) {
657                 *ret_mime = url_mime;
658                 return hres;
659             }
660             CoTaskMemFree(url_mime);
661         }
662     }
663 
664     TRACE("found %s for %s\n", debugstr_w(ret), debugstr_an((const char*)buf, min(32, size)));
665 
666     len = lstrlenW(ret)+1;
667     *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR));
668     if(!*ret_mime)
669         return E_OUTOFMEMORY;
670 
671     memcpy(*ret_mime, ret, len*sizeof(WCHAR));
672     return S_OK;
673 }
674 
675 /***********************************************************************
676  *           FindMimeFromData (URLMON.@)
677  *
678  * Determines the Multipurpose Internet Mail Extensions (MIME) type from the data provided.
679  */
680 HRESULT WINAPI FindMimeFromData(LPBC pBC, LPCWSTR pwzUrl, LPVOID pBuffer,
681         DWORD cbSize, LPCWSTR pwzMimeProposed, DWORD dwMimeFlags,
682         LPWSTR* ppwzMimeOut, DWORD dwReserved)
683 {
684     TRACE("(%p,%s,%p,%d,%s,0x%x,%p,0x%x)\n", pBC, debugstr_w(pwzUrl), pBuffer, cbSize,
685             debugstr_w(pwzMimeProposed), dwMimeFlags, ppwzMimeOut, dwReserved);
686 
687     if(dwMimeFlags)
688         WARN("dwMimeFlags=%08x\n", dwMimeFlags);
689     if(dwReserved)
690         WARN("dwReserved=%d\n", dwReserved);
691 
692     /* pBC seems to not be used */
693 
694     if(!ppwzMimeOut || (!pwzUrl && !pBuffer))
695         return E_INVALIDARG;
696 
697     if(pwzMimeProposed || pBuffer)
698         return find_mime_from_buffer(pBuffer, cbSize, pwzMimeProposed, pwzUrl, ppwzMimeOut);
699 
700     return find_mime_from_url(pwzUrl, ppwzMimeOut);
701 }
702