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