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.

1123 lines
28 KiB

  1. /*
  2. * C N V T . C P P
  3. *
  4. * Data conversion routines
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_xmllib.h"
  9. #include <string.h>
  10. #include <stdio.h>
  11. // Month names ---------------------------------------------------------------
  12. //
  13. DEC_CONST LPCWSTR c_rgwszMonthNames[] =
  14. {
  15. L"Jan",
  16. L"Feb",
  17. L"Mar",
  18. L"Apr",
  19. L"May",
  20. L"Jun",
  21. L"Jul",
  22. L"Aug",
  23. L"Sep",
  24. L"Oct",
  25. L"Nov",
  26. L"Dec",
  27. };
  28. DEC_CONST ULONG c_cMonthNames = CElems(c_rgwszMonthNames);
  29. DEC_CONST ULONG c_cchMonthName = 3;
  30. DEC_CONST LPCWSTR c_rgwszDayNames[] =
  31. {
  32. L"Sun",
  33. L"Mon",
  34. L"Tue",
  35. L"Wed",
  36. L"Thu",
  37. L"Fri",
  38. L"Sat",
  39. };
  40. DEC_CONST UINT c_cDayNames = CElems(c_rgwszDayNames);
  41. DEC_CONST UINT c_cchDayName = 3;
  42. // Date formats --------------------------------------------------------------
  43. //
  44. DEC_CONST WCHAR gc_wszIso8601_min[] = L"yyyy-mm-ddThh:mm:ssZ";
  45. DEC_CONST UINT gc_cchIso8601_min = CchConstString(gc_wszIso8601_min);
  46. DEC_CONST WCHAR gc_wszIso8601_scanfmt[] = L"%04hu-%02hu-%02huT%02hu:%02hu:%02hu";
  47. DEC_CONST WCHAR gc_wszIso8601_tz_scanfmt[] = L"%02hu:%02hu";
  48. DEC_CONST WCHAR gc_wszIso8601_fmt[] = L"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ";
  49. DEC_CONST WCHAR gc_wszRfc1123_min[] = L"www, dd mmm yyyy hh:mm:ss GMT";
  50. DEC_CONST UINT gc_cchRfc1123_min = CchConstString (gc_wszRfc1123_min);
  51. DEC_CONST WCHAR gc_wszRfc1123_fmt[] = L"%ls, %02d %ls %04d %02d:%02d:%02d GMT";
  52. enum {
  53. tf_year,
  54. tf_month,
  55. tf_day,
  56. tf_hour,
  57. tf_minute,
  58. tf_second,
  59. cTimeFields,
  60. tz_hour = 0,
  61. tz_minute,
  62. cTzDeltaFields,
  63. RADIX_BASE = 10,
  64. };
  65. // Conversion functions ------------------------------------------------------
  66. //
  67. /*
  68. * CchFindChar
  69. *
  70. * Look for the given char, obeying the cbMax limit.
  71. * If the char is not found, return INVALID_INDEX.
  72. */
  73. UINT __fastcall
  74. CchFindChar(WCHAR wch, LPCWSTR pwszData, UINT cchMax)
  75. {
  76. UINT cchParsed = 0;
  77. while (cchParsed < cchMax &&
  78. wch != *pwszData)
  79. {
  80. cchParsed++;
  81. pwszData++;
  82. }
  83. if (cchParsed == cchMax)
  84. cchParsed = INVALID_INDEX;
  85. return cchParsed;
  86. }
  87. /*
  88. * CchSkipWhitespace
  89. *
  90. * Skips whitespace, obeying the cbMax limit.
  91. * Returns the number of bytes parsed.
  92. */
  93. UINT __fastcall
  94. CchSkipWhitespace(LPCWSTR pwszData, UINT cchMax)
  95. {
  96. UINT cchParsed = 0;
  97. while (cchParsed < cchMax &&
  98. (L' ' == *pwszData ||
  99. L'\t' == *pwszData ||
  100. L'\n' == *pwszData ||
  101. L'\r' == *pwszData))
  102. {
  103. cchParsed++;
  104. pwszData++;
  105. }
  106. return cchParsed;
  107. }
  108. LONG __fastcall
  109. LNumberFromParam(LPCWSTR pwszData, UINT cchMax)
  110. {
  111. LONG lReturn = 0;
  112. UINT cchCurrent = 0;
  113. BOOL fNegative = FALSE;
  114. if (0 < cchMax)
  115. {
  116. // Get any sign char.
  117. //
  118. if (L'-' == *pwszData)
  119. {
  120. // Set the negative flag to true.
  121. //
  122. fNegative = TRUE;
  123. // Skip this valid character.
  124. //
  125. cchCurrent++;
  126. // Skip any whitespace.
  127. //
  128. cchCurrent += CchSkipWhitespace(&pwszData[1], cchMax - 1);
  129. }
  130. else if (L'+' == *pwszData)
  131. {
  132. // Skip any whitespace.
  133. //
  134. cchCurrent += CchSkipWhitespace(&pwszData[1], cchMax - 1);
  135. }
  136. }
  137. // From here, any non-number chars are invalid & mean we
  138. // should stop parsing.
  139. // Get the magnitude of the number.
  140. //
  141. while (cchCurrent < cchMax)
  142. {
  143. if (L'0' <= static_cast<USHORT>(pwszData[cchCurrent]) &&
  144. L'9' >= static_cast<USHORT>(pwszData[cchCurrent]))
  145. {
  146. lReturn *= 10;
  147. lReturn += (pwszData[cchCurrent] - L'0');
  148. }
  149. else
  150. {
  151. // Not a number char. Time to quit parsing.
  152. //
  153. break;
  154. }
  155. // Move to the next char.
  156. //
  157. cchCurrent++;
  158. }
  159. // Apply the negative sign, if any.
  160. //
  161. if (fNegative)
  162. lReturn = (0 - lReturn);
  163. return lReturn;
  164. }
  165. HRESULT __fastcall
  166. HrHTTPDateToFileTime(LPCWSTR pwszDate,
  167. FILETIME * pft)
  168. {
  169. HRESULT hr;
  170. SYSTEMTIME systime;
  171. UINT cchDate;
  172. // Make sure we were passed something as a date string.
  173. //
  174. Assert(pwszDate);
  175. Assert(pft);
  176. // Zero out the structure.
  177. //
  178. memset(&systime, 0, sizeof(SYSTEMTIME));
  179. // Get the length of the date string.
  180. //
  181. cchDate = static_cast<UINT>(wcslen(pwszDate));
  182. // Get the date and time pieces. If either fails, return its
  183. // error code. Otherwise, convert to a file time at the end,
  184. // return E_FAIL if the conversion fails, S_OK otherwise.
  185. //
  186. hr = GetFileDateFromParam(pwszDate,
  187. cchDate,
  188. &systime);
  189. if (FAILED(hr))
  190. return hr;
  191. hr = GetFileTimeFromParam(pwszDate,
  192. cchDate,
  193. &systime);
  194. if (FAILED(hr))
  195. return hr;
  196. if (!SystemTimeToFileTime(&systime, pft))
  197. return E_FAIL;
  198. return S_OK;
  199. }
  200. HRESULT __fastcall
  201. GetFileDateFromParam (LPCWSTR pwszData,
  202. UINT cchTotal,
  203. SYSTEMTIME * psystime)
  204. {
  205. LPCWSTR pwszCurrent;
  206. UINT cchLeft;
  207. UINT cchTemp;
  208. Assert(pwszData);
  209. Assert(psystime);
  210. // Skip leading whitespace.
  211. //
  212. cchTemp = CchSkipWhitespace(pwszData, cchTotal);
  213. pwszCurrent = pwszData + cchTemp;
  214. cchLeft = cchTotal - cchTemp;
  215. // If we've hit the end of our buffer already, this was an invalid date
  216. // string.
  217. //
  218. if (0 == cchLeft)
  219. return E_FAIL;
  220. // If the first char's of the date are ddd, then the day of the
  221. // week is a part of the date, and we really do not care.
  222. //
  223. if (L'9' < static_cast<USHORT>(*pwszCurrent))
  224. {
  225. // Find the day
  226. //
  227. UINT uiDay;
  228. for (uiDay = 0; uiDay < c_cDayNames; uiDay++)
  229. {
  230. // Compare the month names.
  231. //
  232. if (*pwszCurrent == *(c_rgwszDayNames[uiDay]) &&
  233. (c_cchDayName <= cchLeft) &&
  234. !_wcsnicmp(pwszCurrent, c_rgwszDayNames[uiDay], c_cchDayName))
  235. {
  236. // Found the right month. This index tells us the month number.
  237. //
  238. psystime->wDayOfWeek = static_cast<WORD>(uiDay); // Sunday is 0
  239. break;
  240. }
  241. }
  242. if (uiDay == c_cDayNames)
  243. return E_FAIL;
  244. // Look for our space delimiter.
  245. //
  246. cchTemp = CchFindChar(L' ', pwszCurrent, cchLeft);
  247. if (INVALID_INDEX == cchTemp)
  248. {
  249. // Invalid format to this data. Fail here.
  250. //
  251. return E_FAIL;
  252. }
  253. pwszCurrent += cchTemp;
  254. cchLeft -= cchTemp;
  255. // CchFindChar will return INVALID_INDEX if we hit the end of the
  256. // string, so we can assert that we have more space in the string.
  257. //
  258. Assert(0 < cchLeft);
  259. // Again, skip whitespace.
  260. //
  261. cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
  262. pwszCurrent += cchTemp;
  263. cchLeft -= cchTemp;
  264. // If we've hit the end of our buffer already, this was an invalid
  265. // date string.
  266. //
  267. if (0 == cchLeft)
  268. return E_FAIL;
  269. }
  270. // The date format is dd month yyyy. Anything else is invalid.
  271. // Get the day-of-the-month number.
  272. //
  273. psystime->wDay = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
  274. // Look for our space delimiter.
  275. //
  276. cchTemp = CchFindChar(L' ', pwszCurrent, cchLeft);
  277. if (INVALID_INDEX == cchTemp)
  278. {
  279. // Invalid format to this data. Fail here.
  280. //
  281. return E_FAIL;
  282. }
  283. pwszCurrent += cchTemp;
  284. cchLeft -= cchTemp;
  285. // CchFindChar will return INVALID_INDEX if we hit the end of the
  286. // string, so we can assert that we have more space in the string.
  287. //
  288. Assert(0 < cchLeft);
  289. // Again, skip whitespace.
  290. //
  291. cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
  292. pwszCurrent += cchTemp;
  293. cchLeft -= cchTemp;
  294. // If we've hit the end of our buffer already, this was an invalid
  295. // date string.
  296. //
  297. if (0 == cchLeft)
  298. return E_FAIL;
  299. // Find the month number.
  300. //
  301. for (UINT uiMonth = 0; uiMonth < c_cMonthNames; uiMonth++)
  302. {
  303. // Compare the month names.
  304. //
  305. if (*pwszCurrent == *(c_rgwszMonthNames[uiMonth]) &&
  306. (c_cchMonthName <= cchLeft) &&
  307. !_wcsnicmp(pwszCurrent, c_rgwszMonthNames[uiMonth], c_cchMonthName))
  308. {
  309. // Found the right month. This index tells us the month number.
  310. //
  311. psystime->wMonth = static_cast<WORD>(uiMonth + 1); // January is 1.
  312. break;
  313. }
  314. }
  315. // Look for our space delimiter.
  316. //
  317. cchTemp = CchFindChar(L' ', pwszCurrent, cchLeft);
  318. if (INVALID_INDEX == cchTemp)
  319. {
  320. // Invalid format to this data. Fail here.
  321. //
  322. return E_FAIL;
  323. }
  324. pwszCurrent += cchTemp;
  325. cchLeft -= cchTemp;
  326. // CchFindChar will return INVALID_INDEX if we hit the end of the
  327. // string, so we can assert that we have more space in the string.
  328. //
  329. Assert(0 < cchLeft);
  330. // Again, skip whitespace.
  331. //
  332. cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
  333. pwszCurrent += cchTemp;
  334. cchLeft -= cchTemp;
  335. // If we've hit the end of our buffer already, this was an invalid
  336. // date string.
  337. //
  338. if (0 == cchLeft)
  339. return E_FAIL;
  340. // Now get the year.
  341. //
  342. psystime->wYear = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
  343. return S_OK;
  344. }
  345. HRESULT __fastcall
  346. GetFileTimeFromParam (LPCWSTR pwszData,
  347. UINT cchTotal,
  348. SYSTEMTIME * psystime)
  349. {
  350. LPCWSTR pwszCurrent;
  351. UINT cchLeft;
  352. UINT cchTemp;
  353. Assert(pwszData);
  354. Assert(psystime);
  355. // Skip leading whitespace.
  356. //
  357. cchTemp = CchSkipWhitespace(pwszData, cchTotal);
  358. pwszCurrent = pwszData + cchTemp;
  359. cchLeft = cchTotal - cchTemp;
  360. // If we've hit the end of our buffer already, this was an invalid
  361. // date string.
  362. //
  363. if (0 == cchLeft)
  364. return E_FAIL;
  365. // Skip any date information. This could get called for date-time params!
  366. // Look for the first colon delimiter. Yes, we assume no colons in date info!
  367. //
  368. cchTemp = CchFindChar(L':', pwszCurrent, cchLeft);
  369. if (INVALID_INDEX == cchTemp)
  370. {
  371. // No time info available. Fail here.
  372. //
  373. return E_FAIL;
  374. }
  375. // Make sure we've got room to back up
  376. //
  377. if (2 > cchTemp)
  378. {
  379. return E_FAIL;
  380. }
  381. cchTemp--; // Back up to get the hours digits.
  382. cchTemp--;
  383. pwszCurrent += cchTemp;
  384. cchLeft -= cchTemp;
  385. // CchFindChar will return INVALID_INDEX if we hit the end of the
  386. // string, so we can assert that we have at least two digits plus a
  387. // ':' still in the string.
  388. //
  389. Assert(2 < cchLeft);
  390. // Skip whitespace (in case the parm is h:mm:ss).
  391. //
  392. cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
  393. pwszCurrent += cchTemp;
  394. cchLeft -= cchTemp;
  395. // If we've hit the end of our buffer already, this was an invalid
  396. // date string.
  397. //
  398. if (0 == cchLeft)
  399. return E_FAIL;
  400. // Time format is hh:mm:ss UT, GMT, +- hh:mm, anything else is invalid.
  401. // (Actually, we allow [h]h:mm[:ss], and whitespace around the colons.)
  402. // Get the hours.
  403. //
  404. psystime->wHour = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
  405. // Look for our colon delimiter.
  406. //
  407. cchTemp = CchFindChar(L':', pwszCurrent, cchLeft);
  408. if (INVALID_INDEX == cchTemp)
  409. {
  410. // No minutes specified. This is not allowed. Fail here.
  411. //
  412. return E_FAIL;
  413. }
  414. cchTemp++; // Skip the found character also.
  415. pwszCurrent += cchTemp;
  416. cchLeft -= cchTemp;
  417. // If we've hit the end of our buffer already, this was an invalid
  418. // date string.
  419. //
  420. if (0 == cchLeft)
  421. return E_FAIL;
  422. // Again, skip whitespace.
  423. //
  424. cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
  425. pwszCurrent += cchTemp;
  426. cchLeft -= cchTemp;
  427. // If we've hit the end of our buffer already, this was an invalid
  428. // date string.
  429. //
  430. if (0 == cchLeft)
  431. return E_FAIL;
  432. // Get the minutes.
  433. //
  434. psystime->wMinute = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
  435. // NOTE: The seconds are optional. Don't fail here!
  436. // Look for our colon delimiter.
  437. //
  438. cchTemp = CchFindChar(L':', pwszCurrent, cchLeft);
  439. if (INVALID_INDEX == cchTemp)
  440. {
  441. // No seconds specified. This is allowed. Return success.
  442. //
  443. return S_OK;
  444. }
  445. cchTemp++; // Skip the found character also.
  446. pwszCurrent += cchTemp;
  447. cchLeft -= cchTemp;
  448. // If we've hit the end of our buffer already, this was an invalid
  449. // date string.
  450. //
  451. if (0 == cchLeft)
  452. return E_FAIL;
  453. // Again, skip whitespace.
  454. //
  455. cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
  456. pwszCurrent += cchTemp;
  457. cchLeft -= cchTemp;
  458. // If we've hit the end of our buffer already, this was an invalid
  459. // date string.
  460. //
  461. if (0 == cchLeft)
  462. return E_FAIL;
  463. // Get the seconds, if any.
  464. //
  465. psystime->wSecond = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
  466. // LATER: Get the timezone spec from the line and shift this data into our timezone...
  467. return S_OK;
  468. }
  469. BOOL __fastcall
  470. FGetSystimeFromDateIso8601(LPCWSTR pwszDate, SYSTEMTIME * psystime)
  471. {
  472. UINT i;
  473. // Iso8601 is a fixed digit format: "yyyy-mm-ddThh:mm:ssZ"
  474. // we require the date strings has at least the required
  475. // chars (we allow for the ommission of the fractional
  476. // seconds, and the time delta), otherwise it is an error.
  477. //
  478. if (gc_cchIso8601_min > static_cast<UINT>(wcslen(pwszDate)))
  479. {
  480. DebugTrace ("Dav: date length < than minimal\n");
  481. return FALSE;
  482. }
  483. // Scan the first bit of date information up to the
  484. // optional bits
  485. //
  486. psystime->wMilliseconds = 0;
  487. if (cTimeFields != swscanf (pwszDate,
  488. gc_wszIso8601_scanfmt,
  489. &psystime->wYear,
  490. &psystime->wMonth,
  491. &psystime->wDay,
  492. &psystime->wHour,
  493. &psystime->wMinute,
  494. &psystime->wSecond))
  495. {
  496. DebugTrace ("Dav: minimal scan failed\n");
  497. return FALSE;
  498. }
  499. // Take a look at what is next and process accordingly.
  500. //
  501. // ('Z'), ('.'), ('+') and ('-').
  502. //
  503. // The ('Z') element signifies ZULU time and completes
  504. // the time string. The ('.') element signifies that a
  505. // fractional second value follows. And either a ('+')
  506. // or ('-') element indicates that a timezone delta will
  507. // follow.
  508. //
  509. i = gc_cchIso8601_min - 1;
  510. if (pwszDate[i] == L'Z')
  511. goto ret;
  512. else if (pwszDate[i] == L'.')
  513. goto frac_sec;
  514. else if ((pwszDate[i] == L'+') || (pwszDate[i] == L'+'))
  515. goto tz_delta;
  516. DebugTrace ("Dav: minimal date not terminated properly\n");
  517. return FALSE;
  518. frac_sec:
  519. Assert (pwszDate[i] == L'.');
  520. {
  521. UINT iFrac;
  522. for (iFrac = ++i; pwszDate[i]; i++)
  523. {
  524. // Any non-digit terminates the fractional seconds time
  525. //
  526. if ((pwszDate[i] > L'9') || (pwszDate[i] < L'0'))
  527. {
  528. // At this point, we are expecting ('Z') or a timezone
  529. // delta ('+') or ('-')
  530. //
  531. if (pwszDate[i] == L'Z')
  532. goto ret;
  533. else if ((pwszDate[i] == L'+') || (pwszDate[i] == L'-'))
  534. goto tz_delta;
  535. break;
  536. }
  537. // It turns out, our granularity is only milliseconds, so
  538. // we cannot keep any better precision than that. However,
  539. // we can round the last digit, so at best we will process
  540. // the next four digits
  541. //
  542. if (i - iFrac < 3)
  543. {
  544. // As many digits remain, comprise the fractional
  545. //
  546. psystime->wMilliseconds = static_cast<WORD>(
  547. psystime->wMilliseconds * RADIX_BASE + (pwszDate[i]-L'0'));
  548. }
  549. else if (i - iFrac < 4)
  550. {
  551. // Our granularity is only milliseconds, so we cannot keep
  552. // any better precision than that. However, we can round this
  553. // digit.
  554. //
  555. psystime->wMilliseconds = static_cast<WORD>(
  556. psystime->wMilliseconds + (((pwszDate[i]-L'0')>4)?1:0));
  557. }
  558. }
  559. // We ran out of string before the time was terminated
  560. //
  561. return FALSE;
  562. }
  563. tz_delta:
  564. Assert ((pwszDate[i] == L'+') || (pwszDate[i] == L'-'));
  565. {
  566. WORD wHr;
  567. WORD wMin;
  568. __int64 tm;
  569. __int64 tzDelta;
  570. static const __int64 sc_i64Min = 600000000;
  571. static const __int64 sc_i64Hr = 36000000000;
  572. FILETIME ft;
  573. // Find the time delta in terms of FILETIME units
  574. //
  575. if (cTzDeltaFields != swscanf (pwszDate + i + 1,
  576. gc_wszIso8601_tz_scanfmt,
  577. &wHr,
  578. &wMin))
  579. {
  580. DebugTrace ("Dav: tz delta scan failed\n");
  581. return FALSE;
  582. }
  583. tzDelta = (sc_i64Hr * wHr) + (sc_i64Min * wMin);
  584. // Convert the time into a FILETIME, and stuff it into
  585. // a 64bit integer
  586. //
  587. if (!SystemTimeToFileTime (psystime, &ft))
  588. {
  589. DebugTrace ("Dav: invalid time specified\n");
  590. return FALSE;
  591. }
  592. tm = FileTimeCastToI64(ft);
  593. // Apply the delta
  594. //
  595. if (pwszDate[i] == L'+')
  596. tm = tm + tzDelta;
  597. else
  598. {
  599. Assert (pwszDate[i] == L'-');
  600. tm = tm - tzDelta;
  601. }
  602. // Return the value converted back into a SYSTEMTIME
  603. //
  604. ft = I64CastToFileTime(tm);
  605. if (!FileTimeToSystemTime (&ft, psystime))
  606. {
  607. DebugTrace ("Dav: delta invalidated time\n");
  608. return FALSE;
  609. }
  610. }
  611. ret:
  612. return TRUE;
  613. }
  614. BOOL __fastcall
  615. FGetDateIso8601FromSystime(SYSTEMTIME * psystime, LPWSTR pwszDate, UINT cchSize)
  616. {
  617. // If there is not enough space...
  618. //
  619. if (gc_cchIso8601_min >= cchSize)
  620. return FALSE;
  621. // Format it and return...
  622. //
  623. return (!!wsprintfW (pwszDate,
  624. gc_wszIso8601_fmt,
  625. psystime->wYear,
  626. psystime->wMonth,
  627. psystime->wDay,
  628. psystime->wHour,
  629. psystime->wMinute,
  630. psystime->wSecond,
  631. psystime->wMilliseconds));
  632. }
  633. BOOL __fastcall
  634. FGetDateRfc1123FromSystime (SYSTEMTIME * psystime, LPWSTR pwszDate, UINT cchSize)
  635. {
  636. // If there is not enough space...
  637. //
  638. if (gc_cchRfc1123_min >= cchSize)
  639. return FALSE;
  640. // If wDayOfWeek (Sun-Sat: 0-7) or wMonth (Jan-Dec: 1-12) is out of range,
  641. // we'll fail here (to protect our const array lookups below).
  642. // Note: psystime->wMonth is unsigned, so if there's an invalid value
  643. // of 0, then we'll still catch it.
  644. //
  645. if (c_cDayNames <= psystime->wDayOfWeek)
  646. return FALSE;
  647. if (c_cMonthNames <= (psystime->wMonth - 1))
  648. return FALSE;
  649. // Format it and return...
  650. //
  651. return (!!wsprintfW (pwszDate,
  652. gc_wszRfc1123_fmt,
  653. c_rgwszDayNames[psystime->wDayOfWeek],
  654. psystime->wDay,
  655. c_rgwszMonthNames[psystime->wMonth - 1],
  656. psystime->wYear,
  657. psystime->wHour,
  658. psystime->wMinute,
  659. psystime->wSecond));
  660. }
  661. // BCharToHalfByte -----------------------------------------------------------
  662. //
  663. // Switches a wide char to a half-byte hex value. The incoming char
  664. // MUST be in the "ASCII-encoded hex digit" range: 0-9, A-F, a-f.
  665. //
  666. DEC_CONST BYTE gc_mpbchCharToHalfByte[] = {
  667. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  668. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  669. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  670. 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7, 0x8,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
  671. 0x0,0xa,0xb,0xc,0xd,0xe,0xf,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // Caps here.
  672. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  673. 0x0,0xa,0xb,0xc,0xd,0xe,0xf,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // Lowercase here.
  674. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  675. };
  676. inline BYTE BCharToHalfByte(WCHAR ch)
  677. {
  678. // gc_mpbchCharToHalfByte - map a ASCII-encoded char representing a single hex
  679. // digit to a half-byte value. Used to convert hex represented strings into a
  680. // binary representation.
  681. //
  682. // Reference values:
  683. //
  684. // '0' = 49, 0x31;
  685. // 'A' = 65, 0x41;
  686. // 'a' = 97, 0x61;
  687. //
  688. AssertSz (!(ch & 0xFF00), "BCharToHalfByte: char upper bits non-zero");
  689. AssertSz (iswxdigit(ch), "Char out of hex digit range.");
  690. return gc_mpbchCharToHalfByte[ch];
  691. }
  692. // ------------------------------------------------------------------------
  693. // c_mpwchbStringize - map a half-byte (low nibble) value to
  694. // the correspoding ASCII-encoded wide char.
  695. // Used to convert binary data into Unicode URL strings.
  696. //
  697. DEC_CONST WCHAR c_mpwchhbStringize[] =
  698. {
  699. L'0', L'1', L'2', L'3',
  700. L'4', L'5', L'6', L'7',
  701. L'8', L'9', L'a', L'b',
  702. L'c', L'd', L'e', L'f',
  703. };
  704. // ------------------------------------------------------------------------
  705. // WchHalfByteToWideChar
  706. // Switches a half-byte to an ACSII-encoded wide char.
  707. // NOTE: The caller must mask out the "other half" of the byte!
  708. //
  709. inline WCHAR WchHalfByteToWideChar(BYTE b)
  710. {
  711. AssertSz(!(b & 0xF0), "Garbage in upper nibble.");
  712. return c_mpwchhbStringize[b];
  713. };
  714. // ==========================================================================
  715. //
  716. // UTILITY FUNCTIONS
  717. // Used in building some props -- like getetag, resourcetag and flat url.
  718. // This code has been moved from calcprops.cpp to exprops.cpp and now to
  719. // cnvt.cpp. The Flat URL code lives in this file because it is needed
  720. // by _storext, exdav and davex. _props is the other component which is
  721. // shared by all of them. But _cnvt seemed like a better place to put
  722. // it. The other utility functions are needed by the flat url generation
  723. // code and by davex in processing parameterized URLs.
  724. //
  725. // ==========================================================================
  726. // ------------------------------------------------------------------------
  727. // Un-stringiz-ing support functions
  728. // (Stringize = dump a binary blob to a string.
  729. // Unstringize = make it a binary blob again.)
  730. //
  731. inline
  732. void
  733. AssertCharInHexRange (char ch)
  734. {
  735. Assert ((ch >= '0' && ch <= '9') ||
  736. (ch >= 'A' && ch <= 'F') ||
  737. (ch >= 'a' && ch <= 'f'));
  738. }
  739. inline
  740. BYTE
  741. NibbleFromChar (char ch)
  742. {
  743. // Assumes data is already in range....
  744. //
  745. return static_cast<BYTE>((ch <= '9')
  746. ? ch - '0'
  747. : ((ch >= 'a')
  748. ? ch - 'W' // 'W' = 'a' - 0xa
  749. : ch - '7')); // '7' = 'A' - 0xa
  750. }
  751. inline
  752. BYTE
  753. ByteFromTwoChars (char chLow, char chHigh)
  754. {
  755. BYTE nibbleLow;
  756. BYTE nibbleHigh;
  757. nibbleLow = NibbleFromChar(chLow);
  758. nibbleHigh = NibbleFromChar(chHigh);
  759. return static_cast<BYTE>(nibbleLow | (nibbleHigh << 4));
  760. }
  761. //$REVIEW: The following two functions really does not belong to any common libraries
  762. //$REVIEW: that are shared by davex, exdav and exoledb. (other options are _prop, _sql)
  763. //$REVIEW: On the other hand, we definitely don't want add a new lib for this. so just
  764. //$REVIEW: add it here. Feel free to move them to a better location if you find one
  765. //
  766. // ------------------------------------------------------------------------
  767. //
  768. // ScDupPsid()
  769. //
  770. // Copies a SID properly (using CopySid()) into a heap-allocated buffer
  771. // that is returned to the caller. The caller must free the buffer when
  772. // it is done using it.
  773. //
  774. SCODE
  775. ScDupPsid (PSID psidSrc,
  776. DWORD dwcbSID,
  777. PSID * ppsidDst)
  778. {
  779. PSID psidDst;
  780. Assert (psidSrc);
  781. Assert (IsValidSid(psidSrc));
  782. Assert (GetLengthSid(psidSrc) == dwcbSID);
  783. psidDst = static_cast<PSID>(ExAlloc(dwcbSID));
  784. if (!psidDst)
  785. {
  786. DebugTrace ("ScDupPsid() - OOM allocating memory for dup'd SID\n");
  787. return E_OUTOFMEMORY;
  788. }
  789. // "Right way" -- since MSDN says not to touch the SID directly.
  790. if (!CopySid (dwcbSID, psidDst, psidSrc))
  791. {
  792. DWORD dwLastError = GetLastError();
  793. DebugTrace ("ScDupPsid() - CopySid() failed %d\n", dwLastError);
  794. ExFree (psidDst);
  795. return HRESULT_FROM_WIN32(dwLastError);
  796. }
  797. *ppsidDst = psidDst;
  798. return S_OK;
  799. }
  800. // ------------------------------------------------------------------------
  801. //
  802. // ScGetTokenInfo()
  803. //
  804. // Extracts a user's security ID (SID) from a security token. Returns the SID
  805. // in a heap-allocated buffer which the caller must free.
  806. //
  807. SCODE
  808. ScGetTokenInfo (HANDLE hTokenUser,
  809. DWORD * pdwcbSIDUser,
  810. PSID * ppsidUser)
  811. {
  812. CStackBuffer<TOKEN_USER> pTokenUser;
  813. DWORD dwcbTokenUser = pTokenUser.size(); //$OPT What is a good initial guess?
  814. Assert (pdwcbSIDUser);
  815. Assert (ppsidUser);
  816. // Fetch the token info into local memory. GetTokenInformation()
  817. // returns the size of the buffer needed if the one passed in is
  818. // not large enough so this loop should execute no more than twice.
  819. //
  820. #ifdef DBG
  821. for ( UINT iPass = 0;
  822. (Assert (iPass < 2), TRUE);
  823. ++iPass )
  824. #else
  825. for ( ;; )
  826. #endif
  827. {
  828. if (NULL == pTokenUser.resize(dwcbTokenUser))
  829. return E_OUTOFMEMORY;
  830. if (GetTokenInformation (hTokenUser,
  831. TokenUser,
  832. pTokenUser.get(),
  833. dwcbTokenUser,
  834. &dwcbTokenUser))
  835. {
  836. break;
  837. }
  838. else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  839. {
  840. return HRESULT_FROM_WIN32(GetLastError());
  841. }
  842. }
  843. // Dup and return the SID from the token info.
  844. //
  845. *pdwcbSIDUser = GetLengthSid(pTokenUser->User.Sid);
  846. return ScDupPsid (pTokenUser->User.Sid,
  847. *pdwcbSIDUser,
  848. ppsidUser);
  849. }
  850. // Our own version of WideCharToMultiByte(CP_UTF8, ...)
  851. //
  852. // It returns similarly to the system call WideCharToMultiByte:
  853. //
  854. // If the function succeeds, and cbDest is nonzero, the return value is
  855. // the number of bytes written to the buffer pointed to by psz.
  856. //
  857. // If the function succeeds, and cbDest is zero, the return value is
  858. // the required size, in bytes, for a buffer that can receive the translated
  859. // string.
  860. //
  861. // If the function fails, the return value is zero. To get extended error
  862. // information, call GetLastError. GetLastError may return one of the
  863. // following error codes:
  864. //
  865. // ERROR_INSUFFICIENT_BUFFER
  866. // ERROR_INVALID_FLAGS
  867. // ERROR_INVALID_PARAMETER
  868. //
  869. // See the WideCharToMultiByte MSDN pages to find out more about
  870. // this function and its use.
  871. //
  872. UINT WideCharToUTF8(/* [in] */ LPCWSTR pwszSrc,
  873. /* [in] */ UINT cchSrc,
  874. /* [out] */ LPSTR pszDest,
  875. /* [in] */ UINT cbDest)
  876. {
  877. // UTF-8 multi-byte encoding. See Appendix A.2 of the Unicode book for
  878. // more info.
  879. //
  880. // Unicode value 1st byte 2nd byte 3rd byte
  881. // 000000000xxxxxxx 0xxxxxxx
  882. // 00000yyyyyxxxxxx 110yyyyy 10xxxxxx
  883. // zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
  884. //
  885. // If cbDest == 0 is passed in then we should only calculate the length
  886. // needed, not use the "pszDest" parameter.
  887. //
  888. BOOL fCalculateOnly = FALSE;
  889. // (comment from nt\private\windows\winnls\mbcs.c, corrected for accuracy):
  890. // Invalid Parameter Check:
  891. // - length of WC string is 0
  892. // - multibyte buffer size is negative
  893. // - WC string is NULL
  894. // - length of MB string is NOT zero AND
  895. // (MB string is NULL OR src and dest pointers equal)
  896. //
  897. if ( (cchSrc == 0) ||
  898. (pwszSrc == NULL) ||
  899. ((cbDest != 0) &&
  900. ((pszDest == NULL) ||
  901. (reinterpret_cast<VOID *>(pszDest) ==
  902. reinterpret_cast<VOID *>(const_cast<LPWSTR>(pwszSrc))))) )
  903. {
  904. SetLastError(ERROR_INVALID_PARAMETER);
  905. return 0;
  906. }
  907. #ifdef DBG
  908. // Check our parameters. We must be given a non-NULL pwszSrc.
  909. //
  910. Assert(pwszSrc);
  911. // Make sure we have a valid string.
  912. //
  913. Assert(!IsBadStringPtrW(pwszSrc, (INVALID_INDEX == cchSrc) ? INFINITE : cchSrc));
  914. // If the user says that the length of the multi-byte string is non-Zero,
  915. // we must be given a non-NULL pszDest. We'll also check it with IsBadWritePtr().
  916. //
  917. if (cbDest)
  918. {
  919. Assert(pszDest);
  920. Assert(!IsBadWritePtr(pszDest, cbDest));
  921. }
  922. #endif
  923. // If -1 is passed in as the length of the string, then we calculate the
  924. // length of the string on the fly, and include the NULL terminator.
  925. //
  926. if (INVALID_INDEX == cchSrc)
  927. cchSrc = static_cast<UINT>(wcslen(pwszSrc) + 1);
  928. // If 0 is passed in as cbDest, then we calculate the length of the
  929. // buffer that would be needed to convert the string. We ignore the
  930. // pszDest parameter in this case.
  931. //
  932. if (0 == cbDest)
  933. fCalculateOnly = TRUE;
  934. UINT ich = 0;
  935. UINT iwch = 0;
  936. for (; iwch < cchSrc; iwch++)
  937. {
  938. WCHAR wch = pwszSrc[iwch];
  939. //
  940. // Single-Byte Case:
  941. // Unicode value 1st byte 2nd byte 3rd byte
  942. // 000000000xxxxxxx 0xxxxxxx
  943. //
  944. if (wch < 0x80)
  945. {
  946. if (!fCalculateOnly)
  947. {
  948. if (ich >= cbDest)
  949. {
  950. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  951. return 0;
  952. }
  953. pszDest[ich] = static_cast<BYTE>(wch);
  954. }
  955. ich++;
  956. }
  957. //
  958. // Double-Byte Case:
  959. // Unicode value 1st byte 2nd byte 3rd byte
  960. // 00000yyyyyxxxxxx 110yyyyy 10xxxxxx
  961. //
  962. else if (wch < 0x800)
  963. {
  964. if (!fCalculateOnly)
  965. {
  966. if ((ich + 1) >= cbDest)
  967. {
  968. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  969. return 0;
  970. }
  971. pszDest[ich] = static_cast<BYTE>((wch >> 6) | 0xC0);
  972. pszDest[ich + 1] = static_cast<BYTE>((wch & 0x3F) | 0x80);
  973. }
  974. ich += 2;
  975. }
  976. //
  977. // Triple-Byte Case:
  978. // Unicode value 1st byte 2nd byte 3rd byte
  979. // zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
  980. //
  981. else
  982. {
  983. if (!fCalculateOnly)
  984. {
  985. if ((ich + 2) >= cbDest)
  986. {
  987. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  988. return 0;
  989. }
  990. pszDest[ich] = static_cast<BYTE>((wch >> 12) | 0xE0);
  991. pszDest[ich + 1] = static_cast<BYTE>(((wch >> 6) & 0x3F) | 0x80);
  992. pszDest[ich + 2] = static_cast<BYTE>((wch & 0x3F) | 0x80);
  993. }
  994. ich += 3;
  995. }
  996. }
  997. return ich;
  998. }