Leaked source code of windows server 2003
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

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name :
  4. mimemap.cxx
  5. Abstract:
  6. Store and retrieve mime-mapping for file types
  7. Author:
  8. Murali R. Krishnan (MuraliK) 10-Jan-1995
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. UlW3.dll
  13. History:
  14. Anil Ruia (AnilR) 27-Mar-2000 Ported to IIS+
  15. --*/
  16. #include "precomp.hxx"
  17. const WCHAR *g_pszDefaultFileExt = L"*";
  18. const CHAR *g_pszDefaultMimeType = "application/octet-stream";
  19. MIME_MAP *g_pMimeMap = NULL;
  20. HRESULT MIME_MAP::InitMimeMap()
  21. /*++
  22. Synopsis
  23. This function reads the mimemap stored either as a MULTI_SZ or as a
  24. sequence of REG_SZ
  25. Returns:
  26. HRESULT
  27. --*/
  28. {
  29. DBG_ASSERT(!IsValid());
  30. // First read metabase (common types will have priority)
  31. HRESULT hr = InitFromMetabase();
  32. if (SUCCEEDED(hr))
  33. {
  34. m_fValid = TRUE;
  35. }
  36. return hr;
  37. }
  38. static VOID GetFileExtension(IN LPWSTR pszPathName,
  39. OUT LPWSTR *ppszExt)
  40. /*++
  41. Synopsis
  42. Gets The extension portion from a filename.
  43. Arguments
  44. pszPathName: The full path name (containing forward '/' or '\\' 's)
  45. ppszExt: Points to start of extension on return
  46. Return Value
  47. None
  48. --*/
  49. {
  50. LPWSTR pszExt = (LPWSTR)g_pszDefaultFileExt;
  51. DBG_ASSERT(ppszExt != NULL);
  52. if (pszPathName)
  53. {
  54. LPWSTR pszLastDot;
  55. pszLastDot = wcsrchr(pszPathName, L'.');
  56. if (pszLastDot != NULL)
  57. {
  58. LPWSTR pszLastSlash = wcsrchr(pszPathName, L'/');
  59. LPWSTR pszLastBackSlash = wcsrchr(pszPathName, L'\\');
  60. LPWSTR pszLastWhack = max(pszLastSlash, pszLastBackSlash);
  61. if (pszLastDot > pszLastWhack)
  62. {
  63. // if the dot comes only in the last component, then get ext
  64. pszExt = pszLastDot + 1; // +1 to skip last dot.
  65. }
  66. }
  67. }
  68. *ppszExt = pszExt;
  69. }
  70. VOID MIME_MAP::CreateAndAddMimeMapEntry(
  71. IN LPWSTR pszMimeType,
  72. IN LPWSTR pszExtension)
  73. {
  74. MIME_MAP_ENTRY *pEntry = NULL;
  75. //
  76. // File extensions, stored by OLE/shell registration UI have leading
  77. // dot, we need to remove it , as other code won't like it.
  78. //
  79. if (pszExtension[0] == L'.')
  80. {
  81. pszExtension++;
  82. }
  83. //
  84. // First check if this extension is not yet present
  85. //
  86. FindKey(pszExtension, &pEntry);
  87. if (pEntry)
  88. {
  89. pEntry->DereferenceMimeMapEntry();
  90. return;
  91. }
  92. MIME_MAP_ENTRY *pMmeNew;
  93. pMmeNew = new MIME_MAP_ENTRY(pszMimeType, pszExtension);
  94. if (!pMmeNew || !pMmeNew->IsValid())
  95. {
  96. //
  97. // unable to create a new MIME_MAP_ENTRY object.
  98. //
  99. if (pMmeNew)
  100. {
  101. pMmeNew->DereferenceMimeMapEntry();
  102. pMmeNew = NULL;
  103. }
  104. return;
  105. }
  106. InsertRecord(pMmeNew);
  107. pMmeNew->DereferenceMimeMapEntry();
  108. pMmeNew = NULL;
  109. }
  110. static BOOL ReadMimeMapFromMetabase(OUT MULTISZ *pmszMimeMap)
  111. /*++
  112. Synopsis
  113. This function reads the mimemap stored either as a MULTI_SZ or as a
  114. sequence of REG_SZ and returns a double null terminated sequence of mime
  115. types on success. If there is any failure, the failures are ignored and
  116. it returns a NULL.
  117. Arguments:
  118. pmszMimeMap: MULTISZ which will contain the MimeMap on success
  119. Returns:
  120. BOOL
  121. --*/
  122. {
  123. MB mb(g_pW3Server->QueryMDObject());
  124. if (!mb.Open(L"/LM/MimeMap", METADATA_PERMISSION_READ))
  125. {
  126. //
  127. // if this fails, we're hosed.
  128. //
  129. DBGPRINTF((DBG_CONTEXT,"Open MD /LM/MimeMap returns %d\n", GetLastError()));
  130. return FALSE;
  131. }
  132. if (!mb.GetMultisz(L"", MD_MIME_MAP, IIS_MD_UT_FILE, pmszMimeMap))
  133. {
  134. DBGPRINTF((DBG_CONTEXT,"Unable to read mime map from metabase: %d\n",GetLastError() ));
  135. return FALSE;
  136. }
  137. return TRUE;
  138. }
  139. static MIME_MAP_ENTRY *
  140. ReadAndParseMimeMapEntry(IN OUT LPWSTR *ppszValues)
  141. /*++
  142. This function parses the string containing next mime map entry and
  143. related fields and if successful creates a new MIME_MAP_ENTRY
  144. object and returns it.
  145. Otherwise it returns NULL.
  146. In either case, the incoming pointer is updated to point to next entry
  147. in the string ( past terminating NULL), assuming incoming pointer is a
  148. multi-string ( double null terminated).
  149. Arguments:
  150. ppszValues: pointer to MULTISZ containing the MimeEntry values.
  151. Returns:
  152. On successful MIME_ENTRY being parsed, a new MIME_MAP_ENTRY object.
  153. On error returns NULL.
  154. --*/
  155. {
  156. MIME_MAP_ENTRY *pMmeNew = NULL;
  157. DBG_ASSERT( ppszValues != NULL);
  158. LPWSTR pszMimeEntry = *ppszValues;
  159. if ( pszMimeEntry != NULL && *pszMimeEntry != L'\0')
  160. {
  161. LPWSTR pszMimeType;
  162. LPWSTR pszFileExt;
  163. LPWSTR pszComma;
  164. pszFileExt = *ppszValues;
  165. if ((pszComma = wcschr(*ppszValues, L',')) != NULL)
  166. {
  167. //
  168. // update *ppszValues to contain the next field.
  169. //
  170. *ppszValues = pszComma + 1; // goto next field.
  171. *pszComma = L'\0';
  172. pszMimeType = *ppszValues;
  173. *ppszValues += wcslen(*ppszValues) + 1;
  174. }
  175. else
  176. {
  177. *ppszValues += wcslen(*ppszValues) + 1;
  178. pszMimeType = L"\0";
  179. }
  180. if (*pszMimeType == L'\0')
  181. {
  182. DBGPRINTF( ( DBG_CONTEXT,
  183. " ReadAndParseMimeEntry()."
  184. " Invalid Mime String ( %S)."
  185. "MimeType( %08x): %S, FileExt( %08x): %S\n",
  186. pszMimeEntry,
  187. pszMimeType, pszMimeType,
  188. pszFileExt, pszFileExt
  189. ));
  190. DBG_ASSERT( pMmeNew == NULL);
  191. }
  192. else
  193. {
  194. // Strip leading dot.
  195. if (*pszFileExt == '.')
  196. {
  197. pszFileExt++;
  198. }
  199. pMmeNew = new MIME_MAP_ENTRY( pszMimeType, pszFileExt);
  200. if ( pMmeNew != NULL && !pMmeNew->IsValid())
  201. {
  202. //
  203. // unable to create a new MIME_MAP_ENTRY object. Delete it.
  204. //
  205. pMmeNew->DereferenceMimeMapEntry();
  206. pMmeNew = NULL;
  207. }
  208. }
  209. }
  210. return pMmeNew;
  211. }
  212. MIME_MAP::MIME_MAP(LPWSTR pszMimeMappings)
  213. : CTypedHashTable<MIME_MAP, MIME_MAP_ENTRY, LPWSTR>("MimeMapper"),
  214. m_fValid (FALSE)
  215. {
  216. BOOL fOk;
  217. while (*pszMimeMappings != L'\0')
  218. {
  219. MIME_MAP_ENTRY *pMmeNew;
  220. pMmeNew = ReadAndParseMimeMapEntry(&pszMimeMappings);
  221. //
  222. // If New MimeMap entry found, Create a new object and update list
  223. //
  224. if (pMmeNew != NULL)
  225. {
  226. fOk = (InsertRecord(pMmeNew) != LK_ALLOC_FAIL);
  227. pMmeNew->DereferenceMimeMapEntry();
  228. pMmeNew = NULL;
  229. if (!fOk)
  230. {
  231. return;
  232. }
  233. }
  234. } // while
  235. m_fValid = TRUE;
  236. }
  237. HRESULT MIME_MAP::InitFromMetabase()
  238. /*++
  239. Synopsis
  240. This function reads the MIME_MAP entries from metabase and parses
  241. the entry, creates MIME_MAP_ENTRY object and adds the object to list
  242. of MimeMapEntries.
  243. Returns:
  244. HRESULT
  245. Format of Storage in registry:
  246. The entries are stored in NT in tbe metabase with a list of values in
  247. following format: file-extension, mimetype
  248. --*/
  249. {
  250. LPTSTR pszValue;
  251. MULTISZ mszMimeMap;
  252. //
  253. // There is some registry key for Mime Entries. Try open and read.
  254. //
  255. if (!ReadMimeMapFromMetabase(&mszMimeMap))
  256. {
  257. return HRESULT_FROM_WIN32(GetLastError());
  258. }
  259. pszValue = (LPWSTR)mszMimeMap.QueryPtr();
  260. //
  261. // Parse each MimeEntry in the string containing list of mime objects.
  262. //
  263. while (*pszValue != L'\0')
  264. {
  265. MIME_MAP_ENTRY *pMmeNew;
  266. BOOL fOk;
  267. pMmeNew = ReadAndParseMimeMapEntry( &pszValue);
  268. //
  269. // If New MimeMap entry found, Create a new object and update list
  270. //
  271. if (pMmeNew != NULL)
  272. {
  273. fOk = (InsertRecord(pMmeNew) != LK_ALLOC_FAIL);
  274. pMmeNew->DereferenceMimeMapEntry();
  275. pMmeNew = NULL;
  276. if (!fOk)
  277. {
  278. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  279. }
  280. }
  281. } // while
  282. return S_OK;
  283. }
  284. HRESULT InitializeMimeMap()
  285. /*++
  286. Creates a new mime map object and loads the registry entries from
  287. under this entry from \\MimeMap.
  288. --*/
  289. {
  290. HRESULT hr = E_FAIL;
  291. DBG_ASSERT(g_pMimeMap == NULL);
  292. g_pMimeMap = new MIME_MAP();
  293. if (g_pMimeMap == NULL)
  294. {
  295. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  296. }
  297. if (FAILED(hr = g_pMimeMap->InitMimeMap()))
  298. {
  299. DBGPRINTF((DBG_CONTEXT,"InitMimeMap failed with hr %x\n", hr));
  300. delete g_pMimeMap;
  301. g_pMimeMap = NULL;
  302. }
  303. return hr;
  304. } // InitializeMimeMap()
  305. VOID CleanupMimeMap()
  306. {
  307. if ( g_pMimeMap != NULL)
  308. {
  309. delete g_pMimeMap;
  310. g_pMimeMap = NULL;
  311. }
  312. } // CleanupMimeMap()
  313. HRESULT SelectMimeMappingForFileExt(IN WCHAR *pszFilePath,
  314. IN MIME_MAP *pMimeMap,
  315. OUT STRA *pstrMimeType,
  316. OUT BOOL *pfUsedDefault)
  317. /*++
  318. Synopsis
  319. Obtains the mime type for file based on the file extension.
  320. Arguments
  321. pszFilePath pointer to path for the given file
  322. pMimeMap mimemap table to use for lookup
  323. pstrMimeType pointer to string to store the mime type on return
  324. pfUsedDefault (optional) set to TRUE if default mimemapping was chosen
  325. Returns:
  326. HRESULT
  327. --*/
  328. {
  329. HRESULT hr = S_OK;
  330. DBG_ASSERT (pstrMimeType != NULL);
  331. if (pfUsedDefault != NULL)
  332. {
  333. *pfUsedDefault = FALSE;
  334. }
  335. if (pszFilePath != NULL && *pszFilePath)
  336. {
  337. LPWSTR pszExt;
  338. MIME_MAP_ENTRY *pMmeMatch = NULL;
  339. GetFileExtension(pszFilePath, &pszExt);
  340. DBG_ASSERT(pszExt);
  341. //
  342. // Successfully got extension. Search in the table
  343. //
  344. if (pMimeMap)
  345. {
  346. pMimeMap->FindKey(pszExt, &pMmeMatch);
  347. if (!pMmeMatch)
  348. {
  349. pMimeMap->FindKey(L"*", &pMmeMatch);
  350. }
  351. }
  352. //
  353. // If not found, lookup in the global table
  354. //
  355. if (!pMmeMatch)
  356. {
  357. g_pMimeMap->FindKey(pszExt, &pMmeMatch);
  358. }
  359. if (!pMmeMatch)
  360. {
  361. g_pMimeMap->FindKey(L"*", &pMmeMatch);
  362. }
  363. if (pMmeMatch)
  364. {
  365. hr = pstrMimeType->CopyW(pMmeMatch->QueryMimeType());
  366. pMmeMatch->DereferenceMimeMapEntry();
  367. pMmeMatch = NULL;
  368. return hr;
  369. }
  370. //
  371. // If it is not in the table, look in the registry, but only if
  372. // there was an actual extension (GetFileExtension returns '*' if
  373. // there was no real extension
  374. //
  375. if (pszExt[0] != L'*' || pszExt[1] != L'\0')
  376. {
  377. HKEY hkeyExtension;
  378. //
  379. // Go back to the dot
  380. //
  381. pszExt--;
  382. if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
  383. pszExt,
  384. 0,
  385. KEY_READ,
  386. &hkeyExtension) == ERROR_SUCCESS)
  387. {
  388. WCHAR pszMimeType[MAX_PATH];
  389. DWORD dwType;
  390. DWORD cbValue;
  391. //
  392. // Now get content type for this extension if present
  393. //
  394. cbValue = sizeof pszMimeType;
  395. if (RegQueryValueEx(hkeyExtension,
  396. L"Content Type",
  397. NULL,
  398. &dwType,
  399. (LPBYTE)pszMimeType,
  400. &cbValue) == ERROR_SUCCESS &&
  401. dwType == REG_SZ)
  402. {
  403. g_pMimeMap->CreateAndAddMimeMapEntry(pszMimeType, pszExt);
  404. RegCloseKey(hkeyExtension);
  405. return pstrMimeType->CopyW(pszMimeType);
  406. }
  407. RegCloseKey(hkeyExtension);
  408. }
  409. }
  410. }
  411. if (pfUsedDefault != NULL)
  412. {
  413. *pfUsedDefault = TRUE;
  414. }
  415. return pstrMimeType->Copy(g_pszDefaultMimeType);
  416. }