/* * UrlMon * * Copyright (c) 2000 Patrik Stridvall * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include "urlmon_main.h" #include "winreg.h" #define NO_SHLWAPI_REG #include "shlwapi.h" #include "advpub.h" #include "initguid.h" #include "wine/debug.h" #include "urlmon.h" WINE_DEFAULT_DEBUG_CHANNEL(urlmon); DEFINE_GUID(CLSID_CUri, 0xDF2FCE13, 0x25EC, 0x45BB, 0x9D,0x4C, 0xCE,0xCD,0x47,0xC2,0x43,0x0C); LONG URLMON_refCount = 0; HINSTANCE urlmon_instance; static HMODULE hCabinet = NULL; static DWORD urlmon_tls = TLS_OUT_OF_INDEXES; static void init_session(void); static struct list tls_list = LIST_INIT(tls_list); static CRITICAL_SECTION tls_cs; static CRITICAL_SECTION_DEBUG tls_cs_dbg = { 0, 0, &tls_cs, { &tls_cs_dbg.ProcessLocksList, &tls_cs_dbg.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": tls") } }; static CRITICAL_SECTION tls_cs = { &tls_cs_dbg, -1, 0, 0, 0, 0 }; tls_data_t *get_tls_data(void) { tls_data_t *data; if(urlmon_tls == TLS_OUT_OF_INDEXES) { DWORD tls = TlsAlloc(); if(tls == TLS_OUT_OF_INDEXES) return NULL; tls = InterlockedCompareExchange((LONG*)&urlmon_tls, tls, TLS_OUT_OF_INDEXES); if(tls != urlmon_tls) TlsFree(tls); } data = TlsGetValue(urlmon_tls); if(!data) { data = heap_alloc_zero(sizeof(tls_data_t)); if(!data) return NULL; EnterCriticalSection(&tls_cs); list_add_tail(&tls_list, &data->entry); LeaveCriticalSection(&tls_cs); TlsSetValue(urlmon_tls, data); } return data; } static void free_tls_list(void) { tls_data_t *data; if(urlmon_tls == TLS_OUT_OF_INDEXES) return; while(!list_empty(&tls_list)) { data = LIST_ENTRY(list_head(&tls_list), tls_data_t, entry); list_remove(&data->entry); heap_free(data); } TlsFree(urlmon_tls); } static void detach_thread(void) { tls_data_t *data; if(urlmon_tls == TLS_OUT_OF_INDEXES) return; data = TlsGetValue(urlmon_tls); if(!data) return; EnterCriticalSection(&tls_cs); list_remove(&data->entry); LeaveCriticalSection(&tls_cs); if(data->notif_hwnd) { WARN("notif_hwnd not destroyed\n"); DestroyWindow(data->notif_hwnd); } heap_free(data); } static void process_detach(void) { HINTERNET internet_session; internet_session = get_internet_session(NULL); if(internet_session) InternetCloseHandle(internet_session); if (hCabinet) FreeLibrary(hCabinet); free_session(); free_tls_list(); } /*********************************************************************** * DllMain (URLMON.init) */ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) { TRACE("%p 0x%x %p\n", hinstDLL, fdwReason, fImpLoad); URLMON_DllMain( hinstDLL, fdwReason, fImpLoad ); switch(fdwReason) { case DLL_PROCESS_ATTACH: urlmon_instance = hinstDLL; init_session(); break; case DLL_PROCESS_DETACH: process_detach(); DeleteCriticalSection(&tls_cs); break; case DLL_THREAD_DETACH: detach_thread(); break; } return TRUE; } const char *debugstr_bindstatus(ULONG status) { switch(status) { #define X(x) case x: return #x X(BINDSTATUS_FINDINGRESOURCE); X(BINDSTATUS_CONNECTING); X(BINDSTATUS_REDIRECTING); X(BINDSTATUS_BEGINDOWNLOADDATA); X(BINDSTATUS_DOWNLOADINGDATA); X(BINDSTATUS_ENDDOWNLOADDATA); X(BINDSTATUS_BEGINDOWNLOADCOMPONENTS); X(BINDSTATUS_INSTALLINGCOMPONENTS); X(BINDSTATUS_ENDDOWNLOADCOMPONENTS); X(BINDSTATUS_USINGCACHEDCOPY); X(BINDSTATUS_SENDINGREQUEST); X(BINDSTATUS_CLASSIDAVAILABLE); X(BINDSTATUS_MIMETYPEAVAILABLE); X(BINDSTATUS_CACHEFILENAMEAVAILABLE); X(BINDSTATUS_BEGINSYNCOPERATION); X(BINDSTATUS_ENDSYNCOPERATION); X(BINDSTATUS_BEGINUPLOADDATA); X(BINDSTATUS_UPLOADINGDATA); X(BINDSTATUS_ENDUPLOADINGDATA); X(BINDSTATUS_PROTOCOLCLASSID); X(BINDSTATUS_ENCODING); X(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE); X(BINDSTATUS_CLASSINSTALLLOCATION); X(BINDSTATUS_DECODING); X(BINDSTATUS_LOADINGMIMEHANDLER); X(BINDSTATUS_CONTENTDISPOSITIONATTACH); X(BINDSTATUS_FILTERREPORTMIMETYPE); X(BINDSTATUS_CLSIDCANINSTANTIATE); X(BINDSTATUS_IUNKNOWNAVAILABLE); X(BINDSTATUS_DIRECTBIND); X(BINDSTATUS_RAWMIMETYPE); X(BINDSTATUS_PROXYDETECTING); X(BINDSTATUS_ACCEPTRANGES); X(BINDSTATUS_COOKIE_SENT); X(BINDSTATUS_COMPACT_POLICY_RECEIVED); X(BINDSTATUS_COOKIE_SUPPRESSED); X(BINDSTATUS_COOKIE_STATE_UNKNOWN); X(BINDSTATUS_COOKIE_STATE_ACCEPT); X(BINDSTATUS_COOKIE_STATE_REJECT); X(BINDSTATUS_COOKIE_STATE_PROMPT); X(BINDSTATUS_COOKIE_STATE_LEASH); X(BINDSTATUS_COOKIE_STATE_DOWNGRADE); X(BINDSTATUS_POLICY_HREF); X(BINDSTATUS_P3P_HEADER); X(BINDSTATUS_SESSION_COOKIE_RECEIVED); X(BINDSTATUS_PERSISTENT_COOKIE_RECEIVED); X(BINDSTATUS_SESSION_COOKIES_ALLOWED); X(BINDSTATUS_CACHECONTROL); X(BINDSTATUS_CONTENTDISPOSITIONFILENAME); X(BINDSTATUS_MIMETEXTPLAINMISMATCH); X(BINDSTATUS_PUBLISHERAVAILABLE); X(BINDSTATUS_DISPLAYNAMEAVAILABLE); #undef X default: return wine_dbg_sprintf("(invalid status %u)", status); } } /*********************************************************************** * DllInstall (URLMON.@) */ HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline) { FIXME("(%s, %s): stub\n", bInstall?"TRUE":"FALSE", debugstr_w(cmdline)); return S_OK; } /*********************************************************************** * DllCanUnloadNow (URLMON.@) */ HRESULT WINAPI DllCanUnloadNow(void) { return URLMON_refCount != 0 ? S_FALSE : S_OK; } /****************************************************************************** * Urlmon ClassFactory */ typedef struct { IClassFactory IClassFactory_iface; HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj); } ClassFactory; static inline ClassFactory *impl_from_IClassFactory(IClassFactory *iface) { return CONTAINING_RECORD(iface, ClassFactory, IClassFactory_iface); } static HRESULT WINAPI CF_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppv) { *ppv = NULL; if(IsEqualGUID(riid, &IID_IUnknown)) { TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv); *ppv = iface; }else if(IsEqualGUID(riid, &IID_IClassFactory)) { TRACE("(%p)->(IID_IClassFactory %p)\n", iface, ppv); *ppv = iface; } if(*ppv) { IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } WARN("(%p)->(%s,%p),not found\n", iface, debugstr_guid(riid), ppv); return E_NOINTERFACE; } static ULONG WINAPI CF_AddRef(IClassFactory *iface) { URLMON_LockModule(); return 2; } static ULONG WINAPI CF_Release(IClassFactory *iface) { URLMON_UnlockModule(); return 1; } static HRESULT WINAPI CF_CreateInstance(IClassFactory *iface, IUnknown *pOuter, REFIID riid, LPVOID *ppobj) { ClassFactory *This = impl_from_IClassFactory(iface); HRESULT hres; LPUNKNOWN punk; TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj); *ppobj = NULL; if(SUCCEEDED(hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk))) { hres = IUnknown_QueryInterface(punk, riid, ppobj); IUnknown_Release(punk); } return hres; } static HRESULT WINAPI CF_LockServer(LPCLASSFACTORY iface,BOOL dolock) { TRACE("(%d)\n", dolock); if (dolock) URLMON_LockModule(); else URLMON_UnlockModule(); return S_OK; } static const IClassFactoryVtbl ClassFactoryVtbl = { CF_QueryInterface, CF_AddRef, CF_Release, CF_CreateInstance, CF_LockServer }; static ClassFactory FileProtocolCF = { { &ClassFactoryVtbl }, FileProtocol_Construct}; static ClassFactory FtpProtocolCF = { { &ClassFactoryVtbl }, FtpProtocol_Construct}; static ClassFactory GopherProtocolCF = { { &ClassFactoryVtbl }, GopherProtocol_Construct}; static ClassFactory HttpProtocolCF = { { &ClassFactoryVtbl }, HttpProtocol_Construct}; static ClassFactory HttpSProtocolCF = { { &ClassFactoryVtbl }, HttpSProtocol_Construct}; static ClassFactory MkProtocolCF = { { &ClassFactoryVtbl }, MkProtocol_Construct}; static ClassFactory SecurityManagerCF = { { &ClassFactoryVtbl }, SecManagerImpl_Construct}; static ClassFactory ZoneManagerCF = { { &ClassFactoryVtbl }, ZoneMgrImpl_Construct}; static ClassFactory StdURLMonikerCF = { { &ClassFactoryVtbl }, StdURLMoniker_Construct}; static ClassFactory MimeFilterCF = { { &ClassFactoryVtbl }, MimeFilter_Construct}; static ClassFactory CUriCF = { { &ClassFactoryVtbl }, Uri_Construct}; struct object_creation_info { const CLSID *clsid; IClassFactory *cf; LPCWSTR protocol; }; static const WCHAR wszFile[] = {'f','i','l','e',0}; static const WCHAR wszFtp[] = {'f','t','p',0}; static const WCHAR wszGopher[] = {'g','o','p','h','e','r',0}; static const WCHAR wszHttp[] = {'h','t','t','p',0}; static const WCHAR wszHttps[] = {'h','t','t','p','s',0}; static const WCHAR wszMk[] = {'m','k',0}; static const struct object_creation_info object_creation[] = { { &CLSID_FileProtocol, &FileProtocolCF.IClassFactory_iface, wszFile }, { &CLSID_FtpProtocol, &FtpProtocolCF.IClassFactory_iface, wszFtp }, { &CLSID_GopherProtocol, &GopherProtocolCF.IClassFactory_iface, wszGopher }, { &CLSID_HttpProtocol, &HttpProtocolCF.IClassFactory_iface, wszHttp }, { &CLSID_HttpSProtocol, &HttpSProtocolCF.IClassFactory_iface, wszHttps }, { &CLSID_MkProtocol, &MkProtocolCF.IClassFactory_iface, wszMk }, { &CLSID_InternetSecurityManager, &SecurityManagerCF.IClassFactory_iface, NULL }, { &CLSID_InternetZoneManager, &ZoneManagerCF.IClassFactory_iface, NULL }, { &CLSID_StdURLMoniker, &StdURLMonikerCF.IClassFactory_iface, NULL }, { &CLSID_DeCompMimeFilter, &MimeFilterCF.IClassFactory_iface, NULL }, { &CLSID_CUri, &CUriCF.IClassFactory_iface, NULL } }; static void init_session(void) { unsigned int i; for(i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++) { if(object_creation[i].protocol) register_namespace(object_creation[i].cf, object_creation[i].clsid, object_creation[i].protocol, TRUE); } } /******************************************************************************* * DllGetClassObject [URLMON.@] * Retrieves class object from a DLL object * * NOTES * Docs say returns STDAPI * * PARAMS * rclsid [I] CLSID for the class object * riid [I] Reference to identifier of interface for class object * ppv [O] Address of variable to receive interface pointer for riid * * RETURNS * Success: S_OK * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG, * E_UNEXPECTED */ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) { unsigned int i; HRESULT hr; TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++) { if (IsEqualGUID(object_creation[i].clsid, rclsid)) return IClassFactory_QueryInterface(object_creation[i].cf, riid, ppv); } hr = URLMON_DllGetClassObject(rclsid, riid, ppv); if(SUCCEEDED(hr)) return hr; FIXME("%s: no class found.\n", debugstr_guid(rclsid)); return CLASS_E_CLASSNOTAVAILABLE; } static HRESULT register_inf(BOOL doregister) { HRESULT (WINAPI *pRegInstall)(HMODULE hm, LPCSTR pszSection, const STRTABLEA* pstTable); HMODULE hAdvpack; static const WCHAR wszAdvpack[] = {'a','d','v','p','a','c','k','.','d','l','l',0}; hAdvpack = LoadLibraryW(wszAdvpack); pRegInstall = (void *)GetProcAddress(hAdvpack, "RegInstall"); return pRegInstall(hProxyDll, doregister ? "RegisterDll" : "UnregisterDll", NULL); } /*********************************************************************** * DllRegisterServer (URLMON.@) */ HRESULT WINAPI DllRegisterServer(void) { HRESULT hr; TRACE("\n"); hr = URLMON_DllRegisterServer(); return SUCCEEDED(hr) ? register_inf(TRUE) : hr; } /*********************************************************************** * DllUnregisterServer (URLMON.@) */ HRESULT WINAPI DllUnregisterServer(void) { HRESULT hr; TRACE("\n"); hr = URLMON_DllUnregisterServer(); return SUCCEEDED(hr) ? register_inf(FALSE) : hr; } /*********************************************************************** * DllRegisterServerEx (URLMON.@) */ HRESULT WINAPI DllRegisterServerEx(void) { FIXME("(void): stub\n"); return E_FAIL; } /************************************************************************** * IsValidURL (URLMON.@) * * Determines if a specified string is a valid URL. * * PARAMS * pBC [I] ignored, should be NULL. * szURL [I] string that represents the URL in question. * dwReserved [I] reserved and must be zero. * * RETURNS * Success: S_OK. * Failure: S_FALSE. * returns E_INVALIDARG if one or more of the args is invalid. * * TODO: * test functionality against windows to see what a valid URL is. */ HRESULT WINAPI IsValidURL(LPBC pBC, LPCWSTR szURL, DWORD dwReserved) { FIXME("(%p, %s, %d): stub\n", pBC, debugstr_w(szURL), dwReserved); if (dwReserved || !szURL) return E_INVALIDARG; return S_OK; } /************************************************************************** * FaultInIEFeature (URLMON.@) * * Undocumented. Appears to be used by native shdocvw.dll. */ HRESULT WINAPI FaultInIEFeature( HWND hwnd, uCLSSPEC * pClassSpec, QUERYCONTEXT *pQuery, DWORD flags ) { FIXME("%p %p %p %08x\n", hwnd, pClassSpec, pQuery, flags); return E_NOTIMPL; } /************************************************************************** * CoGetClassObjectFromURL (URLMON.@) */ HRESULT WINAPI CoGetClassObjectFromURL( REFCLSID rclsid, LPCWSTR szCodeURL, DWORD dwFileVersionMS, DWORD dwFileVersionLS, LPCWSTR szContentType, LPBINDCTX pBindCtx, DWORD dwClsContext, LPVOID pvReserved, REFIID riid, LPVOID *ppv ) { FIXME("(%s %s %d %d %s %p %d %p %s %p) Stub!\n", debugstr_guid(rclsid), debugstr_w(szCodeURL), dwFileVersionMS, dwFileVersionLS, debugstr_w(szContentType), pBindCtx, dwClsContext, pvReserved, debugstr_guid(riid), ppv); return E_NOINTERFACE; } /*********************************************************************** * ReleaseBindInfo (URLMON.@) * * Release the resources used by the specified BINDINFO structure. * * PARAMS * pbindinfo [I] BINDINFO to release. * * RETURNS * Nothing. */ void WINAPI ReleaseBindInfo(BINDINFO* pbindinfo) { DWORD size; TRACE("(%p)\n", pbindinfo); if(!pbindinfo || !(size = pbindinfo->cbSize)) return; CoTaskMemFree(pbindinfo->szExtraInfo); ReleaseStgMedium(&pbindinfo->stgmedData); if(offsetof(BINDINFO, szExtraInfo) < size) CoTaskMemFree(pbindinfo->szCustomVerb); if(pbindinfo->pUnk && offsetof(BINDINFO, pUnk) < size) IUnknown_Release(pbindinfo->pUnk); memset(pbindinfo, 0, size); pbindinfo->cbSize = size; } /*********************************************************************** * CopyStgMedium (URLMON.@) */ HRESULT WINAPI CopyStgMedium(const STGMEDIUM *src, STGMEDIUM *dst) { TRACE("(%p %p)\n", src, dst); if(!src || !dst) return E_POINTER; *dst = *src; switch(dst->tymed) { case TYMED_NULL: break; case TYMED_FILE: if(src->u.lpszFileName && !src->pUnkForRelease) { DWORD size = (strlenW(src->u.lpszFileName)+1)*sizeof(WCHAR); dst->u.lpszFileName = CoTaskMemAlloc(size); if(!dst->u.lpszFileName) return E_OUTOFMEMORY; memcpy(dst->u.lpszFileName, src->u.lpszFileName, size); } break; case TYMED_ISTREAM: if(dst->u.pstm) IStream_AddRef(dst->u.pstm); break; case TYMED_ISTORAGE: if(dst->u.pstg) IStorage_AddRef(dst->u.pstg); break; case TYMED_HGLOBAL: if(dst->u.hGlobal) { SIZE_T size = GlobalSize(src->u.hGlobal); char *src_ptr, *dst_ptr; dst->u.hGlobal = GlobalAlloc(GMEM_FIXED, size); if(!dst->u.hGlobal) return E_OUTOFMEMORY; dst_ptr = GlobalLock(dst->u.hGlobal); src_ptr = GlobalLock(src->u.hGlobal); memcpy(dst_ptr, src_ptr, size); GlobalUnlock(src_ptr); GlobalUnlock(dst_ptr); } break; default: FIXME("Unimplemented tymed %d\n", src->tymed); } if(dst->pUnkForRelease) IUnknown_AddRef(dst->pUnkForRelease); return S_OK; } /*********************************************************************** * CopyBindInfo (URLMON.@) */ HRESULT WINAPI CopyBindInfo(const BINDINFO *pcbiSrc, BINDINFO *pcbiDest) { DWORD size; HRESULT hres; TRACE("(%p %p)\n", pcbiSrc, pcbiDest); if(!pcbiSrc || !pcbiDest) return E_POINTER; if(!pcbiSrc->cbSize || !pcbiDest->cbSize) return E_INVALIDARG; size = pcbiDest->cbSize; if(size > pcbiSrc->cbSize) { memcpy(pcbiDest, pcbiSrc, pcbiSrc->cbSize); memset((char*)pcbiDest+pcbiSrc->cbSize, 0, size-pcbiSrc->cbSize); } else { memcpy(pcbiDest, pcbiSrc, size); } pcbiDest->cbSize = size; size = FIELD_OFFSET(BINDINFO, szExtraInfo)+sizeof(void*); if(pcbiSrc->cbSize>=size && pcbiDest->cbSize>=size && pcbiSrc->szExtraInfo) { size = (strlenW(pcbiSrc->szExtraInfo)+1)*sizeof(WCHAR); pcbiDest->szExtraInfo = CoTaskMemAlloc(size); if(!pcbiDest->szExtraInfo) return E_OUTOFMEMORY; memcpy(pcbiDest->szExtraInfo, pcbiSrc->szExtraInfo, size); } size = FIELD_OFFSET(BINDINFO, stgmedData)+sizeof(STGMEDIUM); if(pcbiSrc->cbSize>=size && pcbiDest->cbSize>=size) { hres = CopyStgMedium(&pcbiSrc->stgmedData, &pcbiDest->stgmedData); if(FAILED(hres)) { CoTaskMemFree(pcbiDest->szExtraInfo); return hres; } } size = FIELD_OFFSET(BINDINFO, szCustomVerb)+sizeof(void*); if(pcbiSrc->cbSize>=size && pcbiDest->cbSize>=size && pcbiSrc->szCustomVerb) { size = (strlenW(pcbiSrc->szCustomVerb)+1)*sizeof(WCHAR); pcbiDest->szCustomVerb = CoTaskMemAlloc(size); if(!pcbiDest->szCustomVerb) { CoTaskMemFree(pcbiDest->szExtraInfo); ReleaseStgMedium(&pcbiDest->stgmedData); return E_OUTOFMEMORY; } memcpy(pcbiDest->szCustomVerb, pcbiSrc->szCustomVerb, size); } size = FIELD_OFFSET(BINDINFO, securityAttributes)+sizeof(SECURITY_ATTRIBUTES); if(pcbiDest->cbSize >= size) memset(&pcbiDest->securityAttributes, 0, sizeof(SECURITY_ATTRIBUTES)); if(pcbiSrc->pUnk) IUnknown_AddRef(pcbiDest->pUnk); return S_OK; } static BOOL text_richtext_filter(const BYTE *b, DWORD size) { return size > 5 && !memcmp(b, "{\\rtf", 5); } static BOOL text_html_filter(const BYTE *b, DWORD size) { if(size < 6) return FALSE; if((b[0] == '<' && (b[1] == 'h' || b[1] == 'H') && (b[2] == 't' || b[2] == 'T') && (b[3] == 'm' || b[3] == 'M') && (b[4] == 'l' || b[4] == 'L')) || (b[0] == '<' && (b[1] == 'h' || b[1] == 'H') && (b[2] == 'e' || b[2] == 'E') && (b[3] == 'a' || b[3] == 'A') && (b[4] == 'd' || b[4] == 'D'))) return TRUE; return FALSE; } static BOOL text_xml_filter(const BYTE *b, DWORD size) { if(size < 7) return FALSE; if(b[0] == '<' && b[1] == '?' && (b[2] == 'x' || b[2] == 'X') && (b[3] == 'm' || b[3] == 'M') && (b[4] == 'l' || b[4] == 'L') && b[5] == ' ') return TRUE; return FALSE; } static BOOL audio_basic_filter(const BYTE *b, DWORD size) { return size > 4 && b[0] == '.' && b[1] == 's' && b[2] == 'n' && b[3] == 'd'; } static BOOL audio_wav_filter(const BYTE *b, DWORD size) { return size > 12 && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F' && b[8] == 'W' && b[9] == 'A' && b[10] == 'V' && b[11] == 'E'; } static BOOL image_gif_filter(const BYTE *b, DWORD size) { return size >= 6 && (b[0] == 'G' || b[0] == 'g') && (b[1] == 'I' || b[1] == 'i') && (b[2] == 'F' || b[2] == 'f') && b[3] == '8' && (b[4] == '7' || b[4] == '9') && (b[5] == 'A' || b[5] == 'a'); } static BOOL image_pjpeg_filter(const BYTE *b, DWORD size) { return size > 2 && b[0] == 0xff && b[1] == 0xd8; } static BOOL image_tiff_filter(const BYTE *b, DWORD size) { static const BYTE magic1[] = {0x4d,0x4d,0x00,0x2a}; static const BYTE magic2[] = {0x49,0x49,0x2a,0xff}; return size >= 4 && (!memcmp(b, magic1, 4) || !memcmp(b, magic2, 4)); } static BOOL image_xpng_filter(const BYTE *b, DWORD size) { static const BYTE xpng_header[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a}; return size > sizeof(xpng_header) && !memcmp(b, xpng_header, sizeof(xpng_header)); } static BOOL image_bmp_filter(const BYTE *b, DWORD size) { return size >= 14 && b[0] == 0x42 && b[1] == 0x4d && *(const DWORD *)(b+6) == 0; } static BOOL video_avi_filter(const BYTE *b, DWORD size) { return size > 12 && b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F' && b[8] == 'A' && b[9] == 'V' && b[10] == 'I' && b[11] == 0x20; } static BOOL video_mpeg_filter(const BYTE *b, DWORD size) { return size > 4 && !b[0] && !b[1] && b[2] == 0x01 && (b[3] == 0xb3 || b[3] == 0xba); } static BOOL application_postscript_filter(const BYTE *b, DWORD size) { return size > 2 && b[0] == '%' && b[1] == '!'; } static BOOL application_pdf_filter(const BYTE *b, DWORD size) { return size > 4 && b[0] == 0x25 && b[1] == 0x50 && b[2] == 0x44 && b[3] == 0x46; } static BOOL application_xzip_filter(const BYTE *b, DWORD size) { return size > 2 && b[0] == 0x50 && b[1] == 0x4b; } static BOOL application_xgzip_filter(const BYTE *b, DWORD size) { return size > 2 && b[0] == 0x1f && b[1] == 0x8b; } static BOOL application_java_filter(const BYTE *b, DWORD size) { return size > 4 && b[0] == 0xca && b[1] == 0xfe && b[2] == 0xba && b[3] == 0xbe; } static BOOL application_xmsdownload(const BYTE *b, DWORD size) { return size > 2 && b[0] == 'M' && b[1] == 'Z'; } static inline BOOL is_text_plain_char(BYTE b) { if(b < 0x20 && b != '\n' && b != '\r' && b != '\t') return FALSE; return TRUE; } static BOOL text_plain_filter(const BYTE *b, DWORD size) { const BYTE *ptr; for(ptr = b; ptr < b+size-1; ptr++) { if(!is_text_plain_char(*ptr)) return FALSE; } return TRUE; } static BOOL application_octet_stream_filter(const BYTE *b, DWORD size) { return TRUE; } static HRESULT find_mime_from_buffer(const BYTE *buf, DWORD size, const WCHAR *proposed_mime, WCHAR **ret_mime) { LPCWSTR ret = NULL; int len, i, any_pos_mime = -1; static const WCHAR text_htmlW[] = {'t','e','x','t','/','h','t','m','l',0}; static const WCHAR text_richtextW[] = {'t','e','x','t','/','r','i','c','h','t','e','x','t',0}; static const WCHAR text_xmlW[] = {'t','e','x','t','/','x','m','l',0}; static const WCHAR audio_basicW[] = {'a','u','d','i','o','/','b','a','s','i','c',0}; static const WCHAR audio_wavW[] = {'a','u','d','i','o','/','w','a','v',0}; static const WCHAR image_gifW[] = {'i','m','a','g','e','/','g','i','f',0}; static const WCHAR image_pjpegW[] = {'i','m','a','g','e','/','p','j','p','e','g',0}; static const WCHAR image_tiffW[] = {'i','m','a','g','e','/','t','i','f','f',0}; static const WCHAR image_xpngW[] = {'i','m','a','g','e','/','x','-','p','n','g',0}; static const WCHAR image_bmpW[] = {'i','m','a','g','e','/','b','m','p',0}; static const WCHAR video_aviW[] = {'v','i','d','e','o','/','a','v','i',0}; static const WCHAR video_mpegW[] = {'v','i','d','e','o','/','m','p','e','g',0}; static const WCHAR app_postscriptW[] = {'a','p','p','l','i','c','a','t','i','o','n','/','p','o','s','t','s','c','r','i','p','t',0}; static const WCHAR app_pdfW[] = {'a','p','p','l','i','c','a','t','i','o','n','/','p','d','f',0}; static const WCHAR app_xzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/', 'x','-','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0}; static const WCHAR app_xgzipW[] = {'a','p','p','l','i','c','a','t','i','o','n','/', 'x','-','g','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0}; static const WCHAR app_javaW[] = {'a','p','p','l','i','c','a','t','i','o','n','/', 'j','a','v','a',0}; static const WCHAR app_xmsdownloadW[] = {'a','p','p','l','i','c','a','t','i','o','n','/', 'x','-','m','s','d','o','w','n','l','o','a','d',0}; static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n','\0'}; static const WCHAR app_octetstreamW[] = {'a','p','p','l','i','c','a','t','i','o','n','/', 'o','c','t','e','t','-','s','t','r','e','a','m','\0'}; static const struct { LPCWSTR mime; BOOL (*filter)(const BYTE *,DWORD); } mime_filters_any_pos[] = { {text_htmlW, text_html_filter}, {text_xmlW, text_xml_filter} }, mime_filters[] = { {text_richtextW, text_richtext_filter}, /* {audio_xaiffW, audio_xaiff_filter}, */ {audio_basicW, audio_basic_filter}, {audio_wavW, audio_wav_filter}, {image_gifW, image_gif_filter}, {image_pjpegW, image_pjpeg_filter}, {image_tiffW, image_tiff_filter}, {image_xpngW, image_xpng_filter}, /* {image_xbitmapW, image_xbitmap_filter}, */ {image_bmpW, image_bmp_filter}, /* {image_xjgW, image_xjg_filter}, */ /* {image_xemfW, image_xemf_filter}, */ /* {image_xwmfW, image_xwmf_filter}, */ {video_aviW, video_avi_filter}, {video_mpegW, video_mpeg_filter}, {app_postscriptW, application_postscript_filter}, /* {app_base64W, application_base64_filter}, */ /* {app_macbinhex40W, application_macbinhex40_filter}, */ {app_pdfW, application_pdf_filter}, /* {app_zcompressedW, application_xcompressed_filter}, */ {app_xzipW, application_xzip_filter}, {app_xgzipW, application_xgzip_filter}, {app_javaW, application_java_filter}, {app_xmsdownloadW, application_xmsdownload}, {text_plainW, text_plain_filter}, {app_octetstreamW, application_octet_stream_filter} }; if(!buf || !size) { if(!proposed_mime) return E_FAIL; len = strlenW(proposed_mime)+1; *ret_mime = CoTaskMemAlloc(len*sizeof(WCHAR)); if(!*ret_mime) return E_OUTOFMEMORY; memcpy(*ret_mime, proposed_mime, len*sizeof(WCHAR)); return S_OK; } if(proposed_mime && (!strcmpW(proposed_mime, app_octetstreamW) || !strcmpW(proposed_mime, text_plainW))) proposed_mime = NULL; if(proposed_mime) { ret = proposed_mime; for(i=0; i < sizeof(mime_filters_any_pos)/sizeof(*mime_filters_any_pos); i++) { if(!strcmpW(proposed_mime, mime_filters_any_pos[i].mime)) { any_pos_mime = i; for(len=size; len>0; len--) { if(mime_filters_any_pos[i].filter(buf+size-len, len)) break; } if(!len) ret = NULL; break; } } if(i == sizeof(mime_filters_any_pos)/sizeof(*mime_filters_any_pos)) { for(i=0; i < sizeof(mime_filters)/sizeof(*mime_filters); i++) { if(!strcmpW(proposed_mime, mime_filters[i].mime)) { if(!mime_filters[i].filter(buf, size)) ret = NULL; break; } } } } /* Looks like a bug in native implementation, html and xml mimes * are not looked for if none of them was proposed */ if(!proposed_mime || any_pos_mime!=-1) { for(len=size; !ret && len>0; len--) { for(i=0; i0; len--) { if(!is_text_plain_char(buf[size-len])) break; for(i=0; i