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.

468 lines
13 KiB

  1. /*
  2. * E M I T T I N G . C P P
  3. *
  4. * Common response bit emitters
  5. *
  6. * Stolen from the IIS5 project 'iis5\svcs\iisrlt\string.cxx' and
  7. * cleaned up to fit in with the DAV sources.
  8. *
  9. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  10. */
  11. #include "_davprs.h"
  12. #include <dav.rh>
  13. /*
  14. * EmitLocation()
  15. *
  16. * Purpose:
  17. *
  18. * Helper function used to emit the location information
  19. *
  20. * Parameters:
  21. *
  22. * pszHeader [in] name of header to set
  23. * pszURI [in] destination URI
  24. * fCollection [in] is resource a collection...
  25. *
  26. * Note:
  27. * This prefix the relative URI with the local server to get the
  28. * absolute URI. this is OK as now all operations are within one
  29. * vroot.
  30. * Later, if we are able to COPY/MOVE across servers, then this
  31. * function is not enough.
  32. */
  33. void __fastcall
  34. CMethUtil::EmitLocation (
  35. /* [in] */ LPCSTR pszHeader,
  36. /* [in] */ LPCWSTR pwszURI,
  37. /* [in] */ BOOL fCollection)
  38. {
  39. auto_heap_ptr<CHAR> pszEscapedURI;
  40. BOOL fTrailing;
  41. CStackBuffer<WCHAR,MAX_PATH> pwsz;
  42. LPCWSTR pwszPrefix;
  43. LPCWSTR pwszServer;
  44. SCODE sc = S_OK;
  45. UINT cch;
  46. UINT cchURI;
  47. UINT cchServer;
  48. UINT cchPrefix;
  49. Assert (pszHeader);
  50. Assert (pwszURI);
  51. Assert (pwszURI == PwszUrlStrippedOfPrefix(pwszURI));
  52. // Calc the length of the URI once and only once
  53. //
  54. cchURI = static_cast<UINT>(wcslen(pwszURI));
  55. // See if it has a trailing slash
  56. //
  57. fTrailing = !!(L'/' == pwszURI[cchURI - 1]);
  58. // See if it is fully qualified
  59. //
  60. cchPrefix = m_pecb->CchUrlPrefixW (&pwszPrefix);
  61. // Get the server to use: passed in or from ECB
  62. //
  63. cchServer = m_pecb->CchGetServerNameW(&pwszServer);
  64. // We know the size of the prefix, the size of the server
  65. // and the size of the url. All we need to make sure of is
  66. // that there is space for a trailing slash and a terminator
  67. //
  68. cch = cchPrefix + cchServer + cchURI + 1 + 1;
  69. if (!pwsz.resize(cch * sizeof(WCHAR)))
  70. return;
  71. memcpy (pwsz.get(), pwszPrefix, cchPrefix * sizeof(WCHAR));
  72. memcpy (pwsz.get() + cchPrefix, pwszServer, cchServer * sizeof(WCHAR));
  73. memcpy (pwsz.get() + cchPrefix + cchServer, pwszURI, (cchURI + 1) * sizeof(WCHAR));
  74. cchURI += cchPrefix + cchServer;
  75. // Ensure proper termination
  76. //
  77. if (fTrailing != !!fCollection)
  78. {
  79. if (fCollection)
  80. {
  81. pwsz[cchURI] = L'/';
  82. cchURI++;
  83. pwsz[cchURI] = L'\0';
  84. }
  85. else
  86. {
  87. cchURI--;
  88. pwsz[cchURI] = L'\0';
  89. }
  90. }
  91. pwszURI = pwsz.get();
  92. // Make a wire url out of it.
  93. //
  94. sc = ScWireUrlFromWideLocalUrl (cchURI, pwszURI, pszEscapedURI);
  95. if (FAILED(sc))
  96. {
  97. // If we can't make a wire URL for whatever reason
  98. // we just won't emit a Location: header. Oh well.
  99. // It's the best we can do at this point.
  100. //
  101. return;
  102. }
  103. // Add the appropriate header
  104. //
  105. m_presponse->SetHeader(pszHeader, pszEscapedURI.get(), FALSE);
  106. }
  107. /*
  108. * EmitLastModified()
  109. *
  110. * Purpose:
  111. *
  112. * Helper function used to emit the last modified information
  113. *
  114. * Parameters:
  115. *
  116. * pft [in] last mod time
  117. */
  118. void __fastcall
  119. CMethUtil::EmitLastModified (
  120. /* [in] */ FILETIME * pft)
  121. {
  122. SYSTEMTIME st;
  123. WCHAR rgwch[80];
  124. FileTimeToSystemTime (pft, &st);
  125. (VOID) FGetDateRfc1123FromSystime(&st, rgwch, CElems(rgwch));
  126. SetResponseHeader (gc_szLast_Modified, rgwch);
  127. }
  128. /*
  129. * EmitCacheControlAndExpires()
  130. *
  131. * Purpose:
  132. *
  133. * Helper function used to emit the Cache-Control and Expires information
  134. *
  135. * Parameters:
  136. *
  137. * pszURI [in] string representing the URI for the entity to have
  138. * information generated for
  139. *
  140. * Comments: From the HTTP 1.1 specification, draft revision 5.
  141. * 13.4 Response Cachability
  142. * ... If there is neither a cache validator nor an explicit expiration time
  143. * associated with a response, we do not expect it to be cached, but
  144. * certain caches MAY violate this expectation (for example, when little
  145. * or no network connectivity is available). A client can usually detect
  146. * that such a response was taken from a cache by comparing the Date
  147. * header to the current time.
  148. * Note that some HTTP/1.0 caches are known to violate this
  149. * expectation without providing any Warning.
  150. */
  151. VOID __fastcall
  152. CMethUtil::EmitCacheControlAndExpires(
  153. /* [in] */ LPCWSTR pwszURI)
  154. {
  155. //$$BUGBUG: $$CAVEAT: There is an inherent problem here. We get the current
  156. // system time, do some processing, and then eventually the response gets sent
  157. // from IIS, at which time the Date header gets added. However, in the case
  158. // where the expiration time is 0, the Expires header should MATCH the Date
  159. // header EXACTLY. We cannot guarantee this.
  160. //
  161. static const __int64 sc_i64HundredNanoSecUnitsPerSec =
  162. 1 * // second
  163. 1000 * // milliseconds per second
  164. 1000 * // microseconds per millisecond
  165. 10; // 100 nanosecond units per microsecond.
  166. SCODE sc;
  167. FILETIME ft;
  168. FILETIME ftExpire;
  169. SYSTEMTIME stExpire;
  170. __int64 i64ExpirationSeconds = 0;
  171. WCHAR rgwchExpireTime[80] = L"\0";
  172. WCHAR rgwchMetabaseExpireTime[80] = L"\0";
  173. UINT cchMetabaseExpireTime = CElems(rgwchMetabaseExpireTime);
  174. sc = ScGetExpirationTime(pwszURI,
  175. rgwchMetabaseExpireTime,
  176. &cchMetabaseExpireTime);
  177. if (FAILED(sc))
  178. {
  179. // At this point, we cannot emit proper Cache-Control and Expires headers,
  180. // so we do not emit them at all. Please see the comment in this function's
  181. // description above regarding non-emission of these headers.
  182. //
  183. DebugTrace("CMethUtil::EmitCacheControlAndExpires() - ScGetExpirationTime() error getting expiration time %08x\n", sc);
  184. // With a buffer of 80 chars. long we should never have this problem.
  185. // An HTTP date + 3 chars is as long as we should ever have to be.
  186. //
  187. Assert(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != sc);
  188. return;
  189. }
  190. // The metabase expiration string looks like:
  191. // "S, HTTP DATE" --- Expires at a specific date/time.
  192. // "D, 0xHEXNUM" --- Expires after a certain number of seconds.
  193. // "" --- No expiration.
  194. //
  195. switch (rgwchMetabaseExpireTime[0])
  196. {
  197. default:
  198. Assert(L'\0' == rgwchMetabaseExpireTime[0]);
  199. return;
  200. case L'S':
  201. case L's':
  202. if (SUCCEEDED(HrHTTPDateToFileTime(&(rgwchMetabaseExpireTime[3]),
  203. &ftExpire)))
  204. {
  205. // Set our Expires header.
  206. //
  207. SetResponseHeader(gc_szExpires, &(rgwchMetabaseExpireTime[3]));
  208. GetSystemTimeAsFileTime(&ft);
  209. if (CompareFileTime(&ft, &ftExpire) >= 0)
  210. {
  211. // If we already expired, we want cache-control to be no-cache. This
  212. // will do that.
  213. //
  214. i64ExpirationSeconds = 0;
  215. }
  216. else
  217. {
  218. i64ExpirationSeconds = ((FileTimeCastToI64(ftExpire) -
  219. FileTimeCastToI64(ft)) /
  220. sc_i64HundredNanoSecUnitsPerSec);
  221. }
  222. }
  223. else
  224. {
  225. // At this point, we cannot emit proper Cache-Control and Expires headers,
  226. // so we do not emit them at all. Please see the comment in this function's
  227. // description above regarding non-emission of these headers.
  228. //
  229. DebugTrace("EmitCacheControlAndExpires: Failed to convert HTTP date to FILETIME.\n");
  230. return;
  231. }
  232. break;
  233. case L'D':
  234. case L'd':
  235. BOOL fRetTemp;
  236. // Set our Expires header.
  237. //
  238. SetResponseHeader (gc_szExpires, rgwchExpireTime);
  239. i64ExpirationSeconds = wcstoul(&(rgwchMetabaseExpireTime[3]), NULL, 16);
  240. GetSystemTimeAsFileTime(&ft);
  241. FileTimeCastToI64(ft) = (FileTimeCastToI64(ft) +
  242. (i64ExpirationSeconds * sc_i64HundredNanoSecUnitsPerSec));
  243. if (!FileTimeToSystemTime (&ft, &stExpire))
  244. {
  245. // At this point, we cannot emit proper Cache-Control and Expires headers,
  246. // so we do not emit them at all. Please see the comment in this function's
  247. // description above regarding non-emission of these headers.
  248. //
  249. DebugTrace("EmitCacheControlAndExpires: FAILED to convert file time "
  250. "to system time for expiration time.\n");
  251. return;
  252. }
  253. fRetTemp = FGetDateRfc1123FromSystime (&stExpire,
  254. rgwchExpireTime,
  255. CElems(rgwchExpireTime));
  256. Assert(fRetTemp);
  257. break;
  258. }
  259. if (0 == i64ExpirationSeconds)
  260. SetResponseHeader(gc_szCache_Control, gc_szCache_Control_NoCache);
  261. else
  262. SetResponseHeader(gc_szCache_Control, gc_szCache_Control_Private);
  263. }
  264. /*
  265. * ScEmitHeader()
  266. *
  267. * Purpose:
  268. *
  269. * Helper function used to emit the header information for
  270. * GET/HEAD responses.
  271. *
  272. * Parameters:
  273. *
  274. * pszContent [in] string containing content type of resource
  275. * pszURI [optional, in] string containing the URI of the resource
  276. * pftLastModification [optional, in] pointer to a FILETIME structure
  277. * representing the last modification
  278. * time for the resource
  279. *
  280. * Returns:
  281. *
  282. * SCODE. S_OK (0) indicates success.
  283. */
  284. SCODE __fastcall
  285. CMethUtil::ScEmitHeader (
  286. /* [in] */ LPCWSTR pwszContent,
  287. /* [in] */ LPCWSTR pwszURI,
  288. /* [in] */ FILETIME * pftLastModification)
  289. {
  290. SCODE sc = S_OK;
  291. // In the case where we have a last modification time, we also need a URI.
  292. // If we don't have a last modification time, it doesn't matter. We don't
  293. // use the URI anyway in this case.
  294. //
  295. Assert(!pftLastModification || pwszURI);
  296. // See if the content is acceptable to the client, remembering
  297. // that the content type is html for directories. If we are
  298. // in a strict environment and the content is not acceptable,
  299. // then return that as an error code.
  300. //
  301. Assert (pwszContent);
  302. if (FAILED (ScIsAcceptable (this, pwszContent)))
  303. {
  304. DebugTrace ("Dav: client does not want this content type\n");
  305. sc = E_DAV_RESPONSE_TYPE_UNACCEPTED;
  306. goto ret;
  307. }
  308. // Write the common header information, all the calls to
  309. // SetResponseHeader() really cannot fail unless there is
  310. // a memory error (which will throw!)
  311. //
  312. if (*pwszContent)
  313. SetResponseHeader (gc_szContent_Type, pwszContent);
  314. // We support byte ranges for documents but not collections. We also
  315. // only emit Expires and Cache-Control headers for documents but not
  316. // collections.
  317. //
  318. if (pftLastModification != NULL)
  319. {
  320. SetResponseHeader (gc_szAccept_Ranges, gc_szBytes);
  321. // While we are processing documents, get the Etag too
  322. //
  323. EmitETag (pftLastModification);
  324. EmitLastModified (pftLastModification);
  325. EmitCacheControlAndExpires(pwszURI);
  326. }
  327. else
  328. SetResponseHeader (gc_szAccept_Ranges, gc_szNone);
  329. ret:
  330. return sc;
  331. }
  332. // Allow header processing ---------------------------------------------------
  333. //
  334. void
  335. CMethUtil::SetAllowHeader (
  336. /* [in] */ RESOURCE_TYPE rt)
  337. {
  338. // We need to check if we have write permission on the directory. If not, we should
  339. // not allow PUT, DELETE, MKCOL, MOVE, or PROPPATCH.
  340. //
  341. BOOL fHaveWriteAccess = !(E_DAV_NO_IIS_WRITE_ACCESS ==
  342. ScIISCheck(LpwszRequestUrl(),
  343. MD_ACCESS_WRITE));
  344. // The gc_szDavPublic header MUST list all the possible verbs,
  345. // so that's the longest Allow: header we'll ever have.
  346. // NOTE: sizeof includes the trailing NULL!
  347. //
  348. CStackBuffer<CHAR,MAX_PATH> psz(gc_cbszDavPublic);
  349. // Setup the minimal set of methods
  350. //
  351. strcpy (psz.get(), gc_szHttpBase);
  352. // If we have write access, then we can delete.
  353. //
  354. if (fHaveWriteAccess)
  355. strcat (psz.get(), gc_szHttpDelete);
  356. // If the resource is not a directory, PUT will be available...
  357. //
  358. if ((rt != RT_COLLECTION) && fHaveWriteAccess)
  359. strcat (psz.get(), gc_szHttpPut);
  360. // If a scriptmap could apply to this resource, then
  361. // add in the post method
  362. //
  363. if (FInScriptMap (LpwszRequestUrl(), MD_ACCESS_EXECUTE))
  364. strcat (psz.get(), gc_szHttpPost);
  365. // Add in the DAV basic methods
  366. //
  367. if (rt != RT_NULL)
  368. {
  369. strcat (psz.get(), gc_szDavCopy);
  370. if (fHaveWriteAccess) strcat (psz.get(), gc_szDavMove);
  371. strcat (psz.get(), gc_szDavPropfind);
  372. if (fHaveWriteAccess) strcat (psz.get(), gc_szDavProppatch);
  373. strcat (psz.get(), gc_szDavSearch);
  374. strcat (psz.get(), gc_szDavNotif);
  375. if (fHaveWriteAccess) strcat (psz.get(), gc_szDavBatchDelete);
  376. strcat (psz.get(), gc_szDavBatchCopy);
  377. if (fHaveWriteAccess)
  378. {
  379. strcat (psz.get(), gc_szDavBatchMove);
  380. strcat (psz.get(), gc_szDavBatchProppatch);
  381. }
  382. strcat (psz.get(), gc_szDavBatchPropfind);
  383. }
  384. // If the resource is a directory, MKCOL will be available...
  385. //
  386. if ((rt != RT_DOCUMENT) && fHaveWriteAccess)
  387. strcat (psz.get(), gc_szDavMkCol);
  388. // Locks should be available, it doesn't mean it will succeed...
  389. //
  390. strcat (psz.get(), gc_szDavLocks);
  391. // Set the header
  392. //
  393. SetResponseHeader (gc_szAllow, psz.get());
  394. }
  395. // Etags ---------------------------------------------------------------------
  396. //
  397. void __fastcall
  398. CMethUtil::EmitETag (FILETIME * pft)
  399. {
  400. WCHAR pwszEtag[100];
  401. if (FETagFromFiletime (pft, pwszEtag, GetEcb()))
  402. SetResponseHeader (gc_szETag, pwszEtag);
  403. }
  404. void __fastcall
  405. CMethUtil::EmitETag (LPCWSTR pwszPath)
  406. {
  407. FILETIME ft;
  408. // Get and Emit the ETAG
  409. //
  410. if (FGetLastModTime (this, pwszPath, &ft))
  411. EmitETag (&ft);
  412. }