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.

452 lines
10 KiB

  1. /*
  2. * X E M I T . C P P
  3. *
  4. * XML emitter processing
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_xml.h"
  9. #include <szsrc.h>
  10. // class CXNode - Emitting ---------------------------------------------------
  11. //
  12. // Our own version of WideCharToMultiByte(CP_UTF8, ...)
  13. //
  14. // UTF-8 multi-byte encoding. See Appendix A.2 of the Unicode book for
  15. // more info.
  16. //
  17. // Unicode value 1st byte 2nd byte 3rd byte
  18. // 000000000xxxxxxx 0xxxxxxx
  19. // 00000yyyyyxxxxxx 110yyyyy 10xxxxxx
  20. // zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
  21. //
  22. inline
  23. VOID WideCharToUTF8Chars (WCHAR wch, BYTE * pb, UINT * pib)
  24. {
  25. Assert (pb);
  26. Assert (pib);
  27. UINT ib = *pib;
  28. // single-byte: 0xxxxxxx
  29. //
  30. if (wch < 0x80)
  31. {
  32. pb[ib] = static_cast<BYTE>(wch);
  33. }
  34. //
  35. // two-byte: 110xxxxx 10xxxxxx
  36. //
  37. else if (wch < 0x800)
  38. {
  39. // Because we alloc'd two extra-bytes,
  40. // we know there is room at the tail of
  41. // the buffer for the overflow...
  42. //
  43. pb[ib++] = static_cast<BYTE>((wch >> 6) | 0xC0);
  44. pb[ib] = static_cast<BYTE>((wch & 0x3F) | 0x80);
  45. }
  46. //
  47. // three-byte: 1110xxxx 10xxxxxx 10xxxxxx
  48. //
  49. else
  50. {
  51. // Because we alloc'd two extra-bytes,
  52. // we know there is room at the tail of
  53. // the buffer for the overflow...
  54. //
  55. pb[ib++] = static_cast<BYTE>((wch >> 12) | 0xE0);
  56. pb[ib++] = static_cast<BYTE>(((wch >> 6) & 0x3F) | 0x80);
  57. pb[ib] = static_cast<BYTE>((wch & 0x3F) | 0x80);
  58. }
  59. *pib = ib;
  60. }
  61. // CXMLEmitter helper functions ----------------------------------------------
  62. //
  63. SCODE
  64. ScGetPropNode (
  65. /* [in] */ CEmitterNode& enItem,
  66. /* [in] */ ULONG hsc,
  67. /* [out] */ CEmitterNode& enPropStat,
  68. /* [out] */ CEmitterNode& enProp)
  69. {
  70. SCODE sc = S_OK;
  71. // <DAV:propstat> node
  72. //
  73. sc = enItem.ScAddNode (gc_wszPropstat, enPropStat);
  74. if (FAILED(sc))
  75. goto ret;
  76. // <DAV:status> node
  77. //
  78. sc = ScAddStatus (&enPropStat, hsc);
  79. if (FAILED(sc))
  80. goto ret;
  81. // <DAV:prop> node
  82. //
  83. sc = enPropStat.ScAddNode (gc_wszProp, enProp);
  84. if (FAILED(sc))
  85. goto ret;
  86. ret:
  87. return sc;
  88. }
  89. // CXNode helper functions ---------------------------------------------------
  90. //
  91. SCODE
  92. ScSetEscapedValue (CXNode* pxn, LPCWSTR pcwsz, UINT cch, BOOL fHandleStoragePathEscaping)
  93. {
  94. SCODE sc = S_OK;
  95. CStackBuffer<WCHAR> lpwsz;
  96. // Argh! We need to have a buffer to fill that is
  97. // at least 3 bytes long for the odd occurrence of a
  98. // single unicode char with significant bits above
  99. // 0x7f.
  100. //
  101. UINT cb = min (cch + 2, CB_XMLBODYPART_SIZE);
  102. // Make sure there is always room to terminate and allocate
  103. // an extra byte.
  104. // NOTE: cb is not an actual count of bytes
  105. // because of this. it does not include the NULL termination.
  106. //
  107. // We really can handle zero bytes being sloughed into
  108. // the buffer.
  109. //
  110. UINT ib;
  111. UINT iwch;
  112. CStackBuffer<BYTE> pb;
  113. if (NULL == pb.resize (cb+1))
  114. return E_OUTOFMEMORY;
  115. if (fHandleStoragePathEscaping)
  116. {
  117. // $REVIEW: this might cause a stack overflow for exceptionally
  118. // large values of cch! but this branch should only be executed
  119. // on the case for urls, so perhaps it's not possible...
  120. //
  121. if (NULL == lpwsz.resize((cch + 1) * sizeof(WCHAR)))
  122. return E_OUTOFMEMORY;
  123. CopyMemory(lpwsz.get(), pcwsz, (cch * sizeof(WCHAR)));
  124. lpwsz[cch] = L'\0';
  125. cch = static_cast<UINT>(wcslen(lpwsz.get()));
  126. pcwsz = lpwsz.get();
  127. }
  128. for (iwch = 0; iwch < cch; )
  129. {
  130. auto_heap_ptr<CHAR> pszEscaped;
  131. // While there are more characters to convert
  132. // and we have enough buffer space left for one UTF8 character
  133. // (max of 3 bytes). the NULL termination is not included in
  134. // cb, so it is already accounted for.
  135. //
  136. for (ib = 0;
  137. (ib < cb - 2) && (iwch < cch);
  138. ib++, iwch++)
  139. {
  140. WideCharToUTF8Chars (pcwsz[iwch], pb.get(), &ib);
  141. }
  142. // Terminate
  143. //
  144. pb[ib] = 0;
  145. // Escape the bytes
  146. //
  147. HttpUriEscape (reinterpret_cast<LPSTR>(pb.get()), pszEscaped);
  148. sc = pxn->ScSetUTF8Value (pszEscaped, static_cast<UINT>(strlen(pszEscaped)));
  149. if (FAILED(sc))
  150. goto ret;
  151. }
  152. ret:
  153. return sc;
  154. }
  155. SCODE
  156. ScEmitRawStoragePathValue (CXNode* pxn, LPCWSTR pcwsz, UINT cch)
  157. {
  158. return pxn->ScSetValue (pcwsz, cch);
  159. }
  160. // CEmitterNode helper functions ---------------------------------------------
  161. //
  162. VOID __fastcall
  163. FormatStatus (ULONG hsc, LPSTR sz, UINT cb)
  164. {
  165. UINT cch = CchConstString(gc_szHTTP_1_1);
  166. // Construct a status line from the HSC
  167. //
  168. memcpy (sz, gc_szHTTP_1_1, cch);
  169. // Add in a space
  170. //
  171. *(sz + cch++) = ' ';
  172. // Add in the HSC
  173. //
  174. _itoa (hsc, sz + cch, 10);
  175. Assert (cch + 3 == strlen (sz));
  176. cch += 3;
  177. // Add in a space
  178. //
  179. *(sz + cch++) = ' ';
  180. // Add the description text
  181. // Note, status line is not localized
  182. //
  183. //$REVIEW: Now that status line is not localized, do we still need to go through
  184. //$REVIEW: CResourceStringCache ?
  185. //
  186. LpszLoadString (hsc, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), sz + cch, cb - cch);
  187. }
  188. SCODE __fastcall
  189. ScAddStatus (CEmitterNode* pen, ULONG hsc)
  190. {
  191. CHAR sz[MAX_PATH];
  192. CEmitterNode enStatus;
  193. FormatStatus (hsc, sz, sizeof(sz));
  194. return pen->ScAddMultiByteNode (gc_wszStatus, enStatus, sz);
  195. }
  196. SCODE __fastcall
  197. ScAddError (CEmitterNode* pen, LPCWSTR pwszErrMsg)
  198. {
  199. CEmitterNode en;
  200. return pen->ScAddNode (gc_wszErrorMessage, en, gc_wszErrorMessage);
  201. }
  202. // class CStatusCache ------------------------------------------------------
  203. //
  204. BOOL
  205. CStatusCache::EmitStatusNodeOp::operator()(
  206. const CHsc& key, const auto_ref_ptr<CPropNameArray>& pna )
  207. {
  208. SCODE sc = S_OK;
  209. UINT iProp;
  210. CEmitterNode enPropStat;
  211. CEmitterNode enProp;
  212. sc = ScGetPropNode (m_enParent,
  213. key.m_hsc,
  214. enPropStat,
  215. enProp);
  216. // Add prop names
  217. //
  218. for (iProp = 0; iProp < pna->CProps(); iProp++)
  219. {
  220. CEmitterNode en;
  221. // Add one prop
  222. //
  223. sc = enProp.ScAddNode (pna->PwszProp(iProp), en);
  224. if (FAILED(sc))
  225. goto ret;
  226. }
  227. ret:
  228. return sc == S_OK;
  229. }
  230. SCODE
  231. CStatusCache::ScAddErrorStatus (ULONG hsc, LPCWSTR pwszProp)
  232. {
  233. SCODE sc = E_OUTOFMEMORY;
  234. auto_ref_ptr<CPropNameArray> pna;
  235. auto_ref_ptr<CPropNameArray> * ppna = NULL;
  236. // Lookup in the cache for the array for the specific hsc
  237. //
  238. ppna = m_cache.Lookup (hsc);
  239. // Add a new propname array if not exist
  240. //
  241. if (!ppna)
  242. {
  243. // Create new propname array object
  244. //
  245. pna.take_ownership (new CPropNameArray());
  246. if (!pna.get())
  247. goto ret;
  248. // Add it to the cache
  249. //
  250. if (!m_cache.FAdd (hsc, pna))
  251. goto ret;
  252. }
  253. else
  254. pna = *ppna;
  255. // Persist the prop name string
  256. //
  257. pwszProp = m_csbPropNames.AppendWithNull (pwszProp);
  258. if (!pwszProp)
  259. goto ret;
  260. // Add it to prop name array
  261. //
  262. sc = pna->ScAddPropName (pwszProp);
  263. if (FAILED(sc))
  264. goto ret;
  265. ret:
  266. return sc;
  267. }
  268. SCODE
  269. CStatusCache::ScEmitErrorStatus (CEmitterNode& enParent)
  270. {
  271. EmitStatusNodeOp op (enParent);
  272. //$REVIEW: Currently, ForEach does not return an error code
  273. //$REVIEW: even when it stops in the middle. we may want to
  274. //$REVIEW: have it return at least a boolean to allow caller
  275. //$REVIEW: to tell whether to continue
  276. //
  277. m_cache.ForEach(op);
  278. return S_OK;
  279. }
  280. // Property name escaping ----------------------------------------------------
  281. //
  282. DEC_CONST char gc_szEscape[] = "_xnnnn";
  283. __inline WCHAR
  284. WchFromEscape (const LPCWSTR wsz)
  285. {
  286. WCHAR wch = 0;
  287. if ((L'x' == *(wsz + 1)) || (L'X' == *(wsz + 1)))
  288. {
  289. // Convert the hex value into a wchar
  290. //
  291. LPWSTR wszEnd;
  292. wch = static_cast<WCHAR>(wcstoul(wsz + 2, &wszEnd, 16 /* hexidecimal */));
  293. // If the length of the sequence is not correct,
  294. // or the terminating character was not an underscore,
  295. // then we there was no escape sequence.
  296. //
  297. if (((wszEnd - wsz) != CchConstString(gc_szEscape)) || (L'_' != *wszEnd))
  298. wch = 0;
  299. }
  300. return wch;
  301. }
  302. __inline BOOL
  303. FIsXmlAllowedChar (WCHAR wch, BOOL fFirstChar)
  304. {
  305. if (fFirstChar)
  306. return isStartNameChar (wch);
  307. else
  308. return isNameChar (wch);
  309. }
  310. SCODE
  311. ScEscapePropertyName (LPCWSTR wszProp, UINT cchProp, LPWSTR wszEscaped, UINT* pcch, BOOL fRestrictFirstCharacter)
  312. {
  313. Assert (wszProp);
  314. Assert (wszEscaped);
  315. Assert (pcch);
  316. LPCWSTR wszStart = wszProp;
  317. SCODE sc = S_OK;
  318. UINT cch = 0;
  319. UINT cchLeft = cchProp;
  320. // The first character of an xml prop tag has different rules
  321. // regarding what is allowable (only characters and underscores
  322. // are allowed).
  323. //
  324. BOOL fFirstCharOfTag = TRUE;
  325. // However, if the caller doesn't want us to impose the additional
  326. // restrictions on the first character, treat the first character
  327. // as no different from any other.
  328. //
  329. if (!fRestrictFirstCharacter) fFirstCharOfTag = FALSE;
  330. while (wszProp < (wszStart + cchProp))
  331. {
  332. // If this is a supported character in a XML tag name,
  333. // copy it over now...
  334. //
  335. if (FIsXmlAllowedChar(*wszProp, fFirstCharOfTag))
  336. {
  337. // If there is room, copy it over.
  338. //
  339. if (cch < *pcch)
  340. *wszEscaped = *wszProp;
  341. }
  342. //
  343. // ... or if the chararacter is an underscore that does not
  344. // look like it preceeds an escape sequence, copy it over
  345. // now...
  346. //
  347. else if ((L'_' == *wszProp) &&
  348. ((cchLeft <= CchConstString(gc_szEscape)) ||
  349. (0 == WchFromEscape(wszProp))))
  350. {
  351. // If there is room, copy it over.
  352. //
  353. if (cch < *pcch)
  354. *wszEscaped = *wszProp;
  355. }
  356. //
  357. // ... and everything else gets escaped.
  358. //
  359. else
  360. {
  361. // Adjust the byte count as if there were room for all
  362. // but one of the characters in the escape sequence.
  363. //
  364. cch += CchConstString(gc_szEscape);
  365. // If there is room, insert the escape
  366. // sequence.
  367. //
  368. if (cch < *pcch)
  369. {
  370. wsprintfW (wszEscaped, L"_x%04x_", *wszProp);
  371. wszEscaped += CchConstString(gc_szEscape);
  372. }
  373. }
  374. // Account for the last character copied over
  375. //
  376. wszEscaped += 1;
  377. wszProp += 1;
  378. cch += 1;
  379. cchLeft--;
  380. fFirstCharOfTag = FALSE;
  381. }
  382. // If there was not room to escape the whole thing, then
  383. // pass back S_FALSE.
  384. //
  385. if (cch > *pcch)
  386. sc = S_FALSE;
  387. // Tell the caller how long the result is, and return
  388. //
  389. *pcch = cch;
  390. return sc;
  391. }