You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
527 lines
13 KiB
527 lines
13 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
mimemap.cxx
|
|
|
|
Abstract:
|
|
Store and retrieve mime-mapping for file types
|
|
|
|
Author:
|
|
Murali R. Krishnan (MuraliK) 10-Jan-1995
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
UlW3.dll
|
|
|
|
History:
|
|
Anil Ruia (AnilR) 27-Mar-2000 Ported to IIS+
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
const WCHAR *g_pszDefaultFileExt = L"*";
|
|
const CHAR *g_pszDefaultMimeType = "application/octet-stream";
|
|
|
|
MIME_MAP *g_pMimeMap = NULL;
|
|
|
|
HRESULT MIME_MAP::InitMimeMap()
|
|
/*++
|
|
Synopsis
|
|
This function reads the mimemap stored either as a MULTI_SZ or as a
|
|
sequence of REG_SZ
|
|
|
|
Returns:
|
|
HRESULT
|
|
--*/
|
|
{
|
|
DBG_ASSERT(!IsValid());
|
|
|
|
// First read metabase (common types will have priority)
|
|
HRESULT hr = InitFromMetabase();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_fValid = TRUE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static VOID GetFileExtension(IN LPWSTR pszPathName,
|
|
OUT LPWSTR *ppszExt)
|
|
/*++
|
|
Synopsis
|
|
Gets The extension portion from a filename.
|
|
|
|
Arguments
|
|
pszPathName: The full path name (containing forward '/' or '\\' 's)
|
|
ppszExt: Points to start of extension on return
|
|
|
|
Return Value
|
|
None
|
|
--*/
|
|
{
|
|
LPWSTR pszExt = (LPWSTR)g_pszDefaultFileExt;
|
|
|
|
DBG_ASSERT(ppszExt != NULL);
|
|
|
|
if (pszPathName)
|
|
{
|
|
LPWSTR pszLastDot;
|
|
|
|
pszLastDot = wcsrchr(pszPathName, L'.');
|
|
|
|
if (pszLastDot != NULL)
|
|
{
|
|
LPWSTR pszLastSlash = wcsrchr(pszPathName, L'/');
|
|
LPWSTR pszLastBackSlash = wcsrchr(pszPathName, L'\\');
|
|
LPWSTR pszLastWhack = max(pszLastSlash, pszLastBackSlash);
|
|
|
|
if (pszLastDot > pszLastWhack)
|
|
{
|
|
// if the dot comes only in the last component, then get ext
|
|
pszExt = pszLastDot + 1; // +1 to skip last dot.
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppszExt = pszExt;
|
|
}
|
|
|
|
VOID MIME_MAP::CreateAndAddMimeMapEntry(
|
|
IN LPWSTR pszMimeType,
|
|
IN LPWSTR pszExtension)
|
|
{
|
|
MIME_MAP_ENTRY *pEntry = NULL;
|
|
|
|
//
|
|
// File extensions, stored by OLE/shell registration UI have leading
|
|
// dot, we need to remove it , as other code won't like it.
|
|
//
|
|
if (pszExtension[0] == L'.')
|
|
{
|
|
pszExtension++;
|
|
}
|
|
|
|
//
|
|
// First check if this extension is not yet present
|
|
//
|
|
FindKey(pszExtension, &pEntry);
|
|
if (pEntry)
|
|
{
|
|
pEntry->DereferenceMimeMapEntry();
|
|
return;
|
|
}
|
|
|
|
MIME_MAP_ENTRY *pMmeNew;
|
|
|
|
pMmeNew = new MIME_MAP_ENTRY(pszMimeType, pszExtension);
|
|
|
|
if (!pMmeNew || !pMmeNew->IsValid())
|
|
{
|
|
//
|
|
// unable to create a new MIME_MAP_ENTRY object.
|
|
//
|
|
if (pMmeNew)
|
|
{
|
|
pMmeNew->DereferenceMimeMapEntry();
|
|
pMmeNew = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
InsertRecord(pMmeNew);
|
|
|
|
pMmeNew->DereferenceMimeMapEntry();
|
|
pMmeNew = NULL;
|
|
}
|
|
|
|
static BOOL ReadMimeMapFromMetabase(OUT MULTISZ *pmszMimeMap)
|
|
/*++
|
|
Synopsis
|
|
This function reads the mimemap stored either as a MULTI_SZ or as a
|
|
sequence of REG_SZ and returns a double null terminated sequence of mime
|
|
types on success. If there is any failure, the failures are ignored and
|
|
it returns a NULL.
|
|
|
|
Arguments:
|
|
pmszMimeMap: MULTISZ which will contain the MimeMap on success
|
|
|
|
Returns:
|
|
BOOL
|
|
--*/
|
|
{
|
|
MB mb(g_pW3Server->QueryMDObject());
|
|
|
|
if (!mb.Open(L"/LM/MimeMap", METADATA_PERMISSION_READ))
|
|
{
|
|
//
|
|
// if this fails, we're hosed.
|
|
//
|
|
DBGPRINTF((DBG_CONTEXT,"Open MD /LM/MimeMap returns %d\n", GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!mb.GetMultisz(L"", MD_MIME_MAP, IIS_MD_UT_FILE, pmszMimeMap))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,"Unable to read mime map from metabase: %d\n",GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static MIME_MAP_ENTRY *
|
|
ReadAndParseMimeMapEntry(IN OUT LPWSTR *ppszValues)
|
|
/*++
|
|
This function parses the string containing next mime map entry and
|
|
related fields and if successful creates a new MIME_MAP_ENTRY
|
|
object and returns it.
|
|
Otherwise it returns NULL.
|
|
In either case, the incoming pointer is updated to point to next entry
|
|
in the string ( past terminating NULL), assuming incoming pointer is a
|
|
multi-string ( double null terminated).
|
|
|
|
Arguments:
|
|
ppszValues: pointer to MULTISZ containing the MimeEntry values.
|
|
|
|
Returns:
|
|
On successful MIME_ENTRY being parsed, a new MIME_MAP_ENTRY object.
|
|
On error returns NULL.
|
|
--*/
|
|
{
|
|
MIME_MAP_ENTRY *pMmeNew = NULL;
|
|
DBG_ASSERT( ppszValues != NULL);
|
|
LPWSTR pszMimeEntry = *ppszValues;
|
|
|
|
if ( pszMimeEntry != NULL && *pszMimeEntry != L'\0')
|
|
{
|
|
LPWSTR pszMimeType;
|
|
LPWSTR pszFileExt;
|
|
LPWSTR pszComma;
|
|
|
|
pszFileExt = *ppszValues;
|
|
if ((pszComma = wcschr(*ppszValues, L',')) != NULL)
|
|
{
|
|
//
|
|
// update *ppszValues to contain the next field.
|
|
//
|
|
*ppszValues = pszComma + 1; // goto next field.
|
|
*pszComma = L'\0';
|
|
|
|
pszMimeType = *ppszValues;
|
|
*ppszValues += wcslen(*ppszValues) + 1;
|
|
}
|
|
else
|
|
{
|
|
*ppszValues += wcslen(*ppszValues) + 1;
|
|
pszMimeType = L"\0";
|
|
}
|
|
|
|
if (*pszMimeType == L'\0')
|
|
{
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" ReadAndParseMimeEntry()."
|
|
" Invalid Mime String ( %S)."
|
|
"MimeType( %08x): %S, FileExt( %08x): %S\n",
|
|
pszMimeEntry,
|
|
pszMimeType, pszMimeType,
|
|
pszFileExt, pszFileExt
|
|
));
|
|
|
|
DBG_ASSERT( pMmeNew == NULL);
|
|
}
|
|
else
|
|
{
|
|
// Strip leading dot.
|
|
|
|
if (*pszFileExt == '.')
|
|
{
|
|
pszFileExt++;
|
|
}
|
|
|
|
pMmeNew = new MIME_MAP_ENTRY( pszMimeType, pszFileExt);
|
|
|
|
if ( pMmeNew != NULL && !pMmeNew->IsValid())
|
|
{
|
|
//
|
|
// unable to create a new MIME_MAP_ENTRY object. Delete it.
|
|
//
|
|
pMmeNew->DereferenceMimeMapEntry();
|
|
pMmeNew = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pMmeNew;
|
|
}
|
|
|
|
|
|
MIME_MAP::MIME_MAP(LPWSTR pszMimeMappings)
|
|
: CTypedHashTable<MIME_MAP, MIME_MAP_ENTRY, LPWSTR>("MimeMapper"),
|
|
m_fValid (FALSE)
|
|
{
|
|
BOOL fOk;
|
|
|
|
while (*pszMimeMappings != L'\0')
|
|
{
|
|
MIME_MAP_ENTRY *pMmeNew;
|
|
|
|
pMmeNew = ReadAndParseMimeMapEntry(&pszMimeMappings);
|
|
|
|
//
|
|
// If New MimeMap entry found, Create a new object and update list
|
|
//
|
|
|
|
if (pMmeNew != NULL)
|
|
{
|
|
fOk = (InsertRecord(pMmeNew) != LK_ALLOC_FAIL);
|
|
|
|
pMmeNew->DereferenceMimeMapEntry();
|
|
pMmeNew = NULL;
|
|
|
|
if (!fOk)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
} // while
|
|
|
|
m_fValid = TRUE;
|
|
}
|
|
|
|
|
|
HRESULT MIME_MAP::InitFromMetabase()
|
|
/*++
|
|
Synopsis
|
|
This function reads the MIME_MAP entries from metabase and parses
|
|
the entry, creates MIME_MAP_ENTRY object and adds the object to list
|
|
of MimeMapEntries.
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
Format of Storage in registry:
|
|
The entries are stored in NT in tbe metabase with a list of values in
|
|
following format: file-extension, mimetype
|
|
|
|
--*/
|
|
{
|
|
LPTSTR pszValue;
|
|
MULTISZ mszMimeMap;
|
|
|
|
//
|
|
// There is some registry key for Mime Entries. Try open and read.
|
|
//
|
|
if (!ReadMimeMapFromMetabase(&mszMimeMap))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
pszValue = (LPWSTR)mszMimeMap.QueryPtr();
|
|
|
|
//
|
|
// Parse each MimeEntry in the string containing list of mime objects.
|
|
//
|
|
while (*pszValue != L'\0')
|
|
{
|
|
MIME_MAP_ENTRY *pMmeNew;
|
|
BOOL fOk;
|
|
|
|
pMmeNew = ReadAndParseMimeMapEntry( &pszValue);
|
|
|
|
//
|
|
// If New MimeMap entry found, Create a new object and update list
|
|
//
|
|
|
|
if (pMmeNew != NULL)
|
|
{
|
|
fOk = (InsertRecord(pMmeNew) != LK_ALLOC_FAIL);
|
|
|
|
pMmeNew->DereferenceMimeMapEntry();
|
|
pMmeNew = NULL;
|
|
|
|
if (!fOk)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
} // while
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT InitializeMimeMap()
|
|
/*++
|
|
|
|
Creates a new mime map object and loads the registry entries from
|
|
under this entry from \\MimeMap.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DBG_ASSERT(g_pMimeMap == NULL);
|
|
|
|
g_pMimeMap = new MIME_MAP();
|
|
|
|
if (g_pMimeMap == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if (FAILED(hr = g_pMimeMap->InitMimeMap()))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,"InitMimeMap failed with hr %x\n", hr));
|
|
|
|
delete g_pMimeMap;
|
|
g_pMimeMap = NULL;
|
|
}
|
|
|
|
return hr;
|
|
} // InitializeMimeMap()
|
|
|
|
|
|
VOID CleanupMimeMap()
|
|
{
|
|
if ( g_pMimeMap != NULL)
|
|
{
|
|
delete g_pMimeMap;
|
|
g_pMimeMap = NULL;
|
|
}
|
|
} // CleanupMimeMap()
|
|
|
|
HRESULT SelectMimeMappingForFileExt(IN WCHAR *pszFilePath,
|
|
IN MIME_MAP *pMimeMap,
|
|
OUT STRA *pstrMimeType,
|
|
OUT BOOL *pfUsedDefault)
|
|
/*++
|
|
Synopsis
|
|
Obtains the mime type for file based on the file extension.
|
|
|
|
Arguments
|
|
pszFilePath pointer to path for the given file
|
|
pMimeMap mimemap table to use for lookup
|
|
pstrMimeType pointer to string to store the mime type on return
|
|
pfUsedDefault (optional) set to TRUE if default mimemapping was chosen
|
|
|
|
Returns:
|
|
HRESULT
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DBG_ASSERT (pstrMimeType != NULL);
|
|
|
|
if (pfUsedDefault != NULL)
|
|
{
|
|
*pfUsedDefault = FALSE;
|
|
}
|
|
|
|
if (pszFilePath != NULL && *pszFilePath)
|
|
{
|
|
LPWSTR pszExt;
|
|
MIME_MAP_ENTRY *pMmeMatch = NULL;
|
|
|
|
GetFileExtension(pszFilePath, &pszExt);
|
|
DBG_ASSERT(pszExt);
|
|
|
|
//
|
|
// Successfully got extension. Search in the table
|
|
//
|
|
if (pMimeMap)
|
|
{
|
|
pMimeMap->FindKey(pszExt, &pMmeMatch);
|
|
|
|
if (!pMmeMatch)
|
|
{
|
|
pMimeMap->FindKey(L"*", &pMmeMatch);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If not found, lookup in the global table
|
|
//
|
|
if (!pMmeMatch)
|
|
{
|
|
g_pMimeMap->FindKey(pszExt, &pMmeMatch);
|
|
}
|
|
|
|
if (!pMmeMatch)
|
|
{
|
|
g_pMimeMap->FindKey(L"*", &pMmeMatch);
|
|
}
|
|
|
|
if (pMmeMatch)
|
|
{
|
|
hr = pstrMimeType->CopyW(pMmeMatch->QueryMimeType());
|
|
|
|
pMmeMatch->DereferenceMimeMapEntry();
|
|
pMmeMatch = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// If it is not in the table, look in the registry, but only if
|
|
// there was an actual extension (GetFileExtension returns '*' if
|
|
// there was no real extension
|
|
//
|
|
if (pszExt[0] != L'*' || pszExt[1] != L'\0')
|
|
{
|
|
HKEY hkeyExtension;
|
|
|
|
//
|
|
// Go back to the dot
|
|
//
|
|
pszExt--;
|
|
|
|
if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
|
pszExt,
|
|
0,
|
|
KEY_READ,
|
|
&hkeyExtension) == ERROR_SUCCESS)
|
|
{
|
|
WCHAR pszMimeType[MAX_PATH];
|
|
DWORD dwType;
|
|
DWORD cbValue;
|
|
|
|
//
|
|
// Now get content type for this extension if present
|
|
//
|
|
cbValue = sizeof pszMimeType;
|
|
|
|
if (RegQueryValueEx(hkeyExtension,
|
|
L"Content Type",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)pszMimeType,
|
|
&cbValue) == ERROR_SUCCESS &&
|
|
dwType == REG_SZ)
|
|
{
|
|
g_pMimeMap->CreateAndAddMimeMapEntry(pszMimeType, pszExt);
|
|
|
|
RegCloseKey(hkeyExtension);
|
|
|
|
return pstrMimeType->CopyW(pszMimeType);
|
|
}
|
|
|
|
RegCloseKey(hkeyExtension);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pfUsedDefault != NULL)
|
|
{
|
|
*pfUsedDefault = TRUE;
|
|
}
|
|
|
|
return pstrMimeType->Copy(g_pszDefaultMimeType);
|
|
}
|
|
|