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.

507 lines
12 KiB

  1. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. //
  3. // CUSTERR.CPP
  4. //
  5. // Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  6. //
  7. #include "_davprs.h"
  8. #include "custerr.h"
  9. #include "content.h"
  10. // ========================================================================
  11. //
  12. // CLASS IError
  13. //
  14. // Interface class for error response handler classes. An error response
  15. // handler class implements one virtual method, DoResponse(), which
  16. // handles the error response.
  17. //
  18. class IError : public CMTRefCounted
  19. {
  20. // NOT IMPLEMENTED
  21. //
  22. IError& operator=(const IError&);
  23. IError( const IError& );
  24. protected:
  25. IError() {}
  26. public:
  27. // CREATORS
  28. //
  29. virtual ~IError() = 0;
  30. // ACCESSORS
  31. //
  32. virtual void DoResponse( IResponse& response , const IEcb& ecb ) const = 0;
  33. };
  34. // ------------------------------------------------------------------------
  35. //
  36. // IError::~IError()
  37. //
  38. // Out of line virtual destructor necessary for proper deletion
  39. // of objects of derived classes via this class
  40. //
  41. IError::~IError() {}
  42. // ------------------------------------------------------------------------
  43. //
  44. // BOOL AddResponseBodyFromFile( IResponse& response, LPCSTR lpszFilePath )
  45. //
  46. // Utility function to add the file's contents to the response's body
  47. //
  48. static BOOL AddResponseBodyFromFile( IResponse& response, LPCWSTR pwszFilePath )
  49. {
  50. BOOL fReturn = FALSE;
  51. auto_ref_handle hf;
  52. //
  53. // Add the file to the response body
  54. //
  55. if ( hf.FCreate(
  56. CreateFileW( pwszFilePath,
  57. GENERIC_READ,
  58. FILE_SHARE_READ | FILE_SHARE_WRITE,
  59. NULL,
  60. OPEN_EXISTING,
  61. FILE_ATTRIBUTE_NORMAL |
  62. FILE_FLAG_SEQUENTIAL_SCAN |
  63. FILE_FLAG_OVERLAPPED,
  64. NULL )) )
  65. {
  66. response.AddBodyFile(hf);
  67. // Set the response content type to an appropriate value based
  68. // on the file's extension.
  69. //
  70. UINT cchContentType = 60;
  71. CStackBuffer<WCHAR> pwszContentType(cchContentType * sizeof(WCHAR));
  72. if (!pwszContentType.get())
  73. return FALSE;
  74. if ( !FGetContentTypeFromPath( *response.GetEcb(),
  75. pwszFilePath,
  76. pwszContentType.get(),
  77. &cchContentType))
  78. {
  79. if (!pwszContentType.resize(cchContentType * sizeof(WCHAR)))
  80. return FALSE;
  81. if ( !FGetContentTypeFromPath( *response.GetEcb(),
  82. pwszFilePath,
  83. pwszContentType.get(),
  84. &cchContentType))
  85. {
  86. //
  87. // If we can't get a reasonable value from the mime map
  88. // then use a reasonable default: application/octet-stream
  89. //
  90. Assert (pwszContentType.celems() >
  91. CchConstString(gc_wszAppl_Octet_Stream));
  92. wcscpy (pwszContentType.get(), gc_wszAppl_Octet_Stream);
  93. }
  94. }
  95. response.SetHeader( gc_szContent_Type, pwszContentType.get() );
  96. fReturn = TRUE;
  97. }
  98. return fReturn;
  99. }
  100. // ========================================================================
  101. //
  102. // CLASS CURLError
  103. //
  104. // URL error response handler class. Handles an error response by
  105. // forwarding to another URL.
  106. //
  107. class CURLError : public IError
  108. {
  109. //
  110. // The URL
  111. //
  112. LPCWSTR m_pwszURL;
  113. // NOT IMPLEMENTED
  114. //
  115. CURLError& operator=(const CURLError&);
  116. CURLError(const CURLError&);
  117. public:
  118. // CREATORS
  119. //
  120. CURLError( LPCWSTR pwszURL ) : m_pwszURL(pwszURL) {}
  121. // ACCESSORS
  122. //
  123. void DoResponse( IResponse& response, const IEcb& ecb ) const;
  124. };
  125. // ------------------------------------------------------------------------
  126. //
  127. // CURLError::DoResponse()
  128. //
  129. // Handle the error response by forwarding to the configured URL.
  130. //
  131. void
  132. CURLError::DoResponse( IResponse& response, const IEcb& ecb ) const
  133. {
  134. SCODE sc = S_OK;
  135. // The first boolean flag is for keeping the query string
  136. // and the second flag indicates that we are doing CustomError
  137. // processing.
  138. //
  139. sc = response.ScForward( m_pwszURL, TRUE , TRUE );
  140. if (FAILED(sc))
  141. {
  142. // The child execute failed - one reason is that the URL is a simple
  143. // file URL. Try mapping the URL to file..
  144. //
  145. if ( HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) == sc )
  146. {
  147. HSE_UNICODE_URL_MAPEX_INFO mi;
  148. // Obtain the file path and send the file in the body
  149. //
  150. sc = ecb.ScReqMapUrlToPathEx(m_pwszURL, &mi);
  151. if (FAILED(sc))
  152. {
  153. // We ran into a case where the CE URL resource itself is not
  154. // found. When we are ready to send response, this will mean
  155. // an empty body. Appropriate body will be default generated there.
  156. //
  157. DebugTrace("CURLError::DoResponse() - IEcb::ScSSFReqMapUrlToPathEx() failed 0x%08lX\n", sc);
  158. }
  159. else
  160. {
  161. AddResponseBodyFromFile( response, mi.lpszPath );
  162. }
  163. }
  164. }
  165. return;
  166. }
  167. // ========================================================================
  168. //
  169. // CLASS CFileError
  170. //
  171. // File error response handler class. Handles an error response by
  172. // adding a file containing response body content to the response body.
  173. //
  174. class CFileError : public IError
  175. {
  176. //
  177. // The filename
  178. //
  179. LPCWSTR m_pwszFileName;
  180. // NOT IMPLEMENTED
  181. //
  182. CFileError& operator=(const CFileError&);
  183. CFileError(const CFileError&);
  184. public:
  185. // CREATORS
  186. //
  187. CFileError( LPCWSTR pwszFileName ) : m_pwszFileName(pwszFileName) {}
  188. // ACCESSORS
  189. //
  190. void DoResponse( IResponse& response, const IEcb& ) const;
  191. };
  192. // ------------------------------------------------------------------------
  193. //
  194. // CFileError::DoResponse()
  195. //
  196. // Handle the error response by setting the response body content
  197. // to the contents of the configured file.
  198. //
  199. void
  200. CFileError::DoResponse( IResponse& response, const IEcb& ) const
  201. {
  202. AddResponseBodyFromFile( response, m_pwszFileName );
  203. }
  204. // ========================================================================
  205. // class CEKey
  206. // Key class for custom error keys that can be compared with ==.
  207. //
  208. #pragma warning(disable:4201) // Nameless struct/union
  209. class CEKey
  210. {
  211. private:
  212. union
  213. {
  214. DWORD m_dw;
  215. struct
  216. {
  217. USHORT m_iStatusCode;
  218. USHORT m_iSubError;
  219. };
  220. };
  221. public:
  222. CEKey (USHORT iStatusCode,
  223. USHORT iSubError) :
  224. m_iStatusCode(iStatusCode),
  225. m_iSubError(iSubError)
  226. {
  227. }
  228. DWORD Dw() const
  229. {
  230. return m_dw;
  231. }
  232. int CEKey::hash (const int rhs) const
  233. {
  234. return (m_dw % rhs);
  235. }
  236. bool CEKey::isequal (const CEKey& rhs) const
  237. {
  238. return (rhs.m_dw == m_dw);
  239. }
  240. };
  241. #pragma warning(default:4201) // Nameless struct/union
  242. // ========================================================================
  243. //
  244. // CLASS CCustomErrorMap
  245. //
  246. // Custom error list class. Each instance of this class encapsulates a
  247. // set of custom error mappings. Each mapping maps from a status code
  248. // and "suberror" (as defined by IIS) to an error response handling object.
  249. //
  250. // The list is configured, via FInit(), from a set of null-terminated
  251. // string of the following form:
  252. //
  253. // "<error>,<suberror|*>,<"FILE"|"URL">,<filename|URL>"
  254. //
  255. // For example, the string "404,*,FILE,C:\WINNT\help\common\404b.htm" would
  256. // translate to a mapping from "404,*" to a CFileError(C:\WINNT\htlp\common\404b.htm)
  257. // object.
  258. //
  259. class CCustomErrorMap : public ICustomErrorMap
  260. {
  261. //
  262. // A cache of status-code-plus-sub-error strings to
  263. // error object mappings
  264. //
  265. CCache<CEKey, auto_ref_ptr<IError> > m_cache;
  266. // NOT IMPLEMENTED
  267. //
  268. CCustomErrorMap& operator=(const CCustomErrorMap&);
  269. CCustomErrorMap(const CCustomErrorMap&);
  270. public:
  271. // CREATORS
  272. //
  273. CCustomErrorMap()
  274. {
  275. // If this fails, our allocators will throw for us.
  276. (void)m_cache.FInit();
  277. //
  278. //$COM refcounting
  279. //
  280. m_cRef = 1;
  281. }
  282. // MANIPULATORS
  283. //
  284. BOOL FInit( LPWSTR pwszCustomErrorMappings );
  285. // ACCESSORS
  286. //
  287. BOOL FDoResponse( IResponse& response, const IEcb& ecb ) const;
  288. };
  289. // ------------------------------------------------------------------------
  290. //
  291. // CCustomErrorMap::FInit()
  292. //
  293. // Initialize a custom error map from a sequence of comma-delimited mapping
  294. // strings.
  295. //
  296. // Disable warnings about conversion from INT to USHORT losing data for
  297. // this function only. The conversion is for the status code and suberror
  298. // which we Assert() are in the range of a USHORT.
  299. //
  300. BOOL
  301. CCustomErrorMap::FInit( LPWSTR pwszCustomErrorMappings )
  302. {
  303. Assert( pwszCustomErrorMappings != NULL );
  304. //
  305. // Parse through the error list and build up the cache.
  306. // (Code mostly copied from IIS' W3_METADATA::BuildCustomErrorTable())
  307. //
  308. // Each mapping is a string of the form:
  309. //
  310. // "<error>,<suberror|*>,<"FILE"|"URL">,<filename|URL>"
  311. //
  312. // Note that if any of the mappings is invalid we fail the whole call.
  313. // This is consistent with IIS' behavior.
  314. //
  315. for ( LPWSTR pwszMapping = pwszCustomErrorMappings; *pwszMapping; )
  316. {
  317. enum {
  318. ISZ_CE_STATCODE = 0,
  319. ISZ_CE_SUBERROR,
  320. ISZ_CE_TYPE,
  321. ISZ_CE_PATH,
  322. ISZ_CE_URL = ISZ_CE_PATH, // alias
  323. CSZ_CE_FIELDS
  324. };
  325. LPWSTR rgpwsz[CSZ_CE_FIELDS];
  326. INT iStatusCode;
  327. INT iSubError = 0;
  328. auto_ref_ptr<IError> pError;
  329. UINT cchMapping;
  330. Assert( !IsBadWritePtr(pwszMapping, wcslen(pwszMapping) * sizeof(WCHAR)) );
  331. //
  332. // Digest the metadata
  333. //
  334. if ( !FParseMDData( pwszMapping,
  335. rgpwsz,
  336. CSZ_CE_FIELDS,
  337. &cchMapping ) )
  338. return FALSE;
  339. //
  340. // Verify that the first field is a valid status code
  341. //
  342. iStatusCode = _wtoi(rgpwsz[ISZ_CE_STATCODE]);
  343. if ( iStatusCode < 400 || iStatusCode > 599 )
  344. return FALSE;
  345. //
  346. // Verify that the second field is a valid suberror. A valid
  347. // suberror is either a "*" or an integer. Note: IIS'
  348. // BuildCustomErrorTable() only checks whether the first
  349. // character is a '*' so we do the same here.
  350. //
  351. if ( *rgpwsz[ISZ_CE_SUBERROR] != L'*' )
  352. {
  353. iSubError = _wtoi(rgpwsz[ISZ_CE_SUBERROR]);
  354. if ( iSubError < 0 || iSubError > _UI16_MAX )
  355. return FALSE;
  356. }
  357. //
  358. // Verify that the third field is a valid type and
  359. // create the appropriate (file or URL) error object.
  360. //
  361. if ( !_wcsicmp(rgpwsz[ISZ_CE_TYPE], L"FILE") )
  362. {
  363. pError = new CFileError(rgpwsz[ISZ_CE_PATH]);
  364. }
  365. else if ( !_wcsicmp(rgpwsz[ISZ_CE_TYPE], L"URL") )
  366. {
  367. pError = new CURLError(rgpwsz[ISZ_CE_URL]);
  368. }
  369. else
  370. {
  371. return FALSE;
  372. }
  373. //
  374. // Add the error object to the cache, keyed by the error/suberror.
  375. //
  376. (void)m_cache.FSet( CEKey(static_cast<USHORT>(iStatusCode),
  377. static_cast<USHORT>(iSubError)),
  378. pError );
  379. //
  380. // Get the next mapping
  381. //
  382. pwszMapping += cchMapping;
  383. }
  384. return TRUE;
  385. }
  386. // ------------------------------------------------------------------------
  387. //
  388. // CCustomErrorMap::FDoResponse()
  389. //
  390. // Look for a custom error response mapping for the particular response
  391. // error status and, if one exists, apply it to the response.
  392. //
  393. // Returns TRUE if an error mapping exists, FALSE if not.
  394. //
  395. BOOL
  396. CCustomErrorMap::FDoResponse( IResponse& response, const IEcb& ecb ) const
  397. {
  398. auto_ref_ptr<IError> pError;
  399. Assert( response.DwStatusCode() <= _UI16_MAX );
  400. Assert( response.DwSubError() <= _UI16_MAX );
  401. //
  402. // Lookup the error/suberror pair in the cache
  403. //
  404. if ( m_cache.FFetch( CEKey(static_cast<USHORT>(response.DwStatusCode()),
  405. static_cast<USHORT>(response.DwSubError())),
  406. &pError ) )
  407. {
  408. pError->DoResponse( response, ecb );
  409. return TRUE;
  410. }
  411. return FALSE;
  412. }
  413. // ========================================================================
  414. //
  415. // FREE FUNCTIONS
  416. //
  417. // ------------------------------------------------------------------------
  418. //
  419. // FSetCustomErrorResponse()
  420. //
  421. BOOL
  422. FSetCustomErrorResponse( const IEcb& ecb,
  423. IResponse& response )
  424. {
  425. const ICustomErrorMap * pCustomErrorMap;
  426. pCustomErrorMap = ecb.MetaData().GetCustomErrorMap();
  427. return pCustomErrorMap && pCustomErrorMap->FDoResponse(response, ecb);
  428. }
  429. // ------------------------------------------------------------------------
  430. //
  431. // NewCustomErrorMap()
  432. //
  433. ICustomErrorMap *
  434. NewCustomErrorMap( LPWSTR pwszCustomErrorMappings )
  435. {
  436. auto_ref_ptr<CCustomErrorMap> pCustomErrorMap;
  437. pCustomErrorMap.take_ownership(new CCustomErrorMap());
  438. if ( pCustomErrorMap->FInit(pwszCustomErrorMappings) )
  439. return pCustomErrorMap.relinquish();
  440. return NULL;
  441. }