[URLMON] Sync with Wine Staging 4.18. CORE-16441
[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 #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 }