IInternetSession::RegisterMimeFilter Method
Registers a temporary pluggable MIME filter on the current process.
Syntax
HRESULT RegisterMimeFilter( IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzType );
Parameters
- pCF
- [in] The address of an IClassFactory interface where an IInternetProtocol object can be created.
- rclsid
- [in] A reference to the class identifier (CLSID) of the MIME handler.
- pwzType
- [in] A string value that contains the MIME to register.
Return Value
Returns one of the following values.
S_OK Success. E_FAIL The operation failed. E_NOINTERFACE The class factory cannot be obtained.
Remarks
This method registers a pluggable MIME filter only on the current process. No other processes are affected by this method.
An application can register a pluggable MIME filter for a particular period of time so that it can handle requests for some MIMEs by calling IInternetSession::RegisterMimeFilter. This method can be called multiple times by using the same interface to register the different MIME types it can handle.
Serious problems with RegisterMimeFilter
Problem 1
------------
IInternetSession::RegisterMimeFilter limited to 32 character MIME types
------------------------------------------------------------------------------------------
Internally, IInternetSession::RegisterMimeFilter is handled in COInetSession::RegisterMimeFilter, which calls CProtMgr::Register. CProtMgr::Register adds the passed MIME type being registered to the local atom table as an ANSI string using the AddAtomA function from kernel32.dll. Because the MIME type is passed as a wide character string, before it is passed to AddAtomA it is converted to an ANSI string using the WideCharToMultiByte function.
The problem is that the cbMultiByte parameter passed to WideCharToMultiByte specifies the output buffer as being 32 characters (including NULL terminator). So if the MIME type being registered is more than 31 characters in length, the WideCharToMultiByte call fails and the returned buffer is empty. No check is made of the result of WideCharToMultiByte, and the returned buffer is attempted to be added to the atom table, which of course fails. The RegisterMimeFilter call returns S_OK, giving no indication that the call has failed.
So for examle, a call to IInternetSession::RegisterMimeFilter(<a class ID>, L"application/rss+xml; charset=utf-8") will actually fail to register the MIME type, and of course the specified MIME filter will never be invoked.
This raises a second and third problems....
Problem 2
------------
Method of checking for registered MIME filters is poor
------------------------------------------------------------------
When the time comes for any (successfully) registered MIME filter to be matched against the incoming MIME type from the http header, the lookup is performed using the FindAtomA function to find a matching registered MIME type in the atom table. This is poor coding, because it is prone to missing intended matches. For example, "text/rss; charset=utf-8" will not match with "text/rss;charset=utf-8" (no space before charset).
Problem 3
------------
Debugging the disassembly of urlmon.dll when the registered MIME types are checked against the http Content-Type header data, it is apparent that the whole Content-Type field, including "charset" data is used for the comparison. Why is the MIME type check done using the whole http Content-Type header including the charset setting? When permamanent MIME handlers are installed in the registry, only the Content-Type up to any ';' character is used, like "text/rss". This means that we have to call RegisterMimeFilter for all possible charset values, like "text/rss;charset=utf-8", "text/rss; charset=utf-8", "text/rss;charset=unicode", "text/rss; charset=unicode", "text/rss;charset=ISO-8859-1", "text/rss; charset=ISO-8859-1".
Summary
-----------
The IInternetSession::RegisterMimeFilter method and the associated MIME handling methods and storage need a serious review. In their current state they are practically unusable. They should be re-coded to properly register MIME types of any practical length. MIME comparison should only compare the actual MIME type, not the charset, thus negating the necessity to register possibly dozens of different MIME types to cover all possible charset settings and whitespace variations.
------------
IInternetSession::RegisterMimeFilter limited to 32 character MIME types
------------------------------------------------------------------------------------------
Internally, IInternetSession::RegisterMimeFilter is handled in COInetSession::RegisterMimeFilter, which calls CProtMgr::Register. CProtMgr::Register adds the passed MIME type being registered to the local atom table as an ANSI string using the AddAtomA function from kernel32.dll. Because the MIME type is passed as a wide character string, before it is passed to AddAtomA it is converted to an ANSI string using the WideCharToMultiByte function.
The problem is that the cbMultiByte parameter passed to WideCharToMultiByte specifies the output buffer as being 32 characters (including NULL terminator). So if the MIME type being registered is more than 31 characters in length, the WideCharToMultiByte call fails and the returned buffer is empty. No check is made of the result of WideCharToMultiByte, and the returned buffer is attempted to be added to the atom table, which of course fails. The RegisterMimeFilter call returns S_OK, giving no indication that the call has failed.
So for examle, a call to IInternetSession::RegisterMimeFilter(<a class ID>, L"application/rss+xml; charset=utf-8") will actually fail to register the MIME type, and of course the specified MIME filter will never be invoked.
This raises a second and third problems....
Problem 2
------------
Method of checking for registered MIME filters is poor
------------------------------------------------------------------
When the time comes for any (successfully) registered MIME filter to be matched against the incoming MIME type from the http header, the lookup is performed using the FindAtomA function to find a matching registered MIME type in the atom table. This is poor coding, because it is prone to missing intended matches. For example, "text/rss; charset=utf-8" will not match with "text/rss;charset=utf-8" (no space before charset).
Problem 3
------------
Debugging the disassembly of urlmon.dll when the registered MIME types are checked against the http Content-Type header data, it is apparent that the whole Content-Type field, including "charset" data is used for the comparison. Why is the MIME type check done using the whole http Content-Type header including the charset setting? When permamanent MIME handlers are installed in the registry, only the Content-Type up to any ';' character is used, like "text/rss". This means that we have to call RegisterMimeFilter for all possible charset values, like "text/rss;charset=utf-8", "text/rss; charset=utf-8", "text/rss;charset=unicode", "text/rss; charset=unicode", "text/rss;charset=ISO-8859-1", "text/rss; charset=ISO-8859-1".
Summary
-----------
The IInternetSession::RegisterMimeFilter method and the associated MIME handling methods and storage need a serious review. In their current state they are practically unusable. They should be re-coded to properly register MIME types of any practical length. MIME comparison should only compare the actual MIME type, not the charset, thus negating the necessity to register possibly dozens of different MIME types to cover all possible charset settings and whitespace variations.
- 8/25/2010
- BobM54321