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.

609 lines
15 KiB

  1. /*
  2. * N M S P C . H
  3. *
  4. * XML namespace processing
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #ifndef _EX_NMSPC_H_
  9. #define _EX_NMSPC_H_
  10. #include <ex\calcom.h>
  11. #include <ex\autoptr.h>
  12. #include <ex\gencache.h>
  13. #include <ex\buffer.h>
  14. #include <ex\sz.h>
  15. #include <crc.h>
  16. // Debugging -----------------------------------------------------------------
  17. //
  18. DEFINE_TRACE(Nmspc);
  19. #define NmspcTrace DO_TRACE(Nmspc)
  20. // Namespaces ----------------------------------------------------------------
  21. //
  22. DEC_CONST WCHAR gc_wszXmlns[] = L"xmlns";
  23. // Namespace support functions -----------------------------------------------
  24. //
  25. inline ULONG CchGenerateAlias (LONG lAlias, LPWSTR pwszAlias)
  26. {
  27. UINT i = 0;
  28. Assert (pwszAlias);
  29. do
  30. {
  31. // We don't have to use 'A'-'Z', use the first 16 capital
  32. // letters to facilitate our computing.
  33. //
  34. pwszAlias[i++] = static_cast<WCHAR>(L'a' + (lAlias & 0xF));
  35. lAlias >>= 4;
  36. }
  37. while (lAlias);
  38. // Ensure termination
  39. //
  40. pwszAlias[i] = 0;
  41. // Return the length
  42. //
  43. NmspcTrace ("Nmspc: generating '%ws' as alias\n", pwszAlias);
  44. return i;
  45. }
  46. DEC_CONST WCHAR wchHiddenNmspcSep = L'#';
  47. inline BOOL FIsNmspcSeparator (WCHAR wch)
  48. {
  49. return ((wch == L':') ||
  50. (wch == L'/') ||
  51. (wch == L'?') ||
  52. (wch == L';') ||
  53. (wch == wchHiddenNmspcSep));
  54. }
  55. inline UINT CchAliasFromSizedTag (LPCWSTR pwszTag, UINT cch)
  56. {
  57. LPCWSTR pwsz;
  58. for (pwsz = pwszTag; (pwsz - pwszTag) < static_cast<LONG>(cch); pwsz++)
  59. if (*pwsz == L':')
  60. return static_cast<UINT>(pwsz - pwszTag);
  61. return 0;
  62. }
  63. inline UINT CchNmspcFromTag (UINT cchTag, LPCWSTR pwszTag, LPCWSTR* ppwszOut)
  64. {
  65. LPCWSTR pwsz;
  66. Assert (ppwszOut);
  67. *ppwszOut = pwszTag;
  68. for (pwsz = pwszTag + cchTag - 1; pwsz >= pwszTag; pwsz--)
  69. {
  70. if (FIsNmspcSeparator (*pwsz))
  71. {
  72. // Since the separator is a part of the namespace,
  73. // adjust accourdingly..
  74. //
  75. //$ REVIEW: We are being forced down the path of allowing namespaces
  76. // that are not properly terminated. The way we do this, is if the
  77. // namespace is not properly terminated, or ends in an '#', we will
  78. // append an '#' character.
  79. //
  80. // What this means is the namespace "urn:xml-data", when assembled
  81. // into a fully qualified tag would become "urn:xml-data#dt". Also,
  82. // the namespace "urn:exch-data#" would become "urn:exch-data##dt".
  83. //
  84. // What we are catching here is the breaking down of a fully qualified
  85. // tag into it's namespace and tag components.
  86. //
  87. // The length of the namespace will not include the trailing '#'
  88. // character -- ever!
  89. //
  90. *ppwszOut = pwsz + 1;
  91. if (wchHiddenNmspcSep == *pwsz)
  92. --pwsz;
  93. //
  94. //$ REVIEW: end.
  95. break;
  96. }
  97. }
  98. return static_cast<UINT>(1 + pwsz - pwszTag);
  99. }
  100. // class CNmspc --------------------------------------------------------------
  101. //
  102. class CNmspc
  103. {
  104. private:
  105. // Ref' counting.
  106. //
  107. // !!! Please note that this is NON-THREADSAFE !!!
  108. //
  109. // CXNodes should be operated on a single thread at
  110. // any given time.
  111. //
  112. LONG m_cRef;
  113. public:
  114. void AddRef() { m_cRef++; }
  115. void Release() { if (0 == --m_cRef) delete this; }
  116. private:
  117. auto_heap_ptr<WCHAR> m_pszHref;
  118. UINT m_cchAlias;
  119. UINT m_cchHref;
  120. auto_heap_ptr<WCHAR> m_pszLongAlias;
  121. WCHAR m_szAlias[20];
  122. LPWSTR m_pszAlias;
  123. // Used for the scoping of namespaces
  124. //
  125. auto_ref_ptr<CNmspc> m_pnsScoped;
  126. auto_ref_ptr<CNmspc> m_pnsSiblings;
  127. // non-implemented
  128. //
  129. CNmspc(const CNmspc& p);
  130. CNmspc& operator=(const CNmspc& p);
  131. public:
  132. CNmspc () :
  133. m_cRef(1), // com-style refcounting
  134. m_cchHref(0),
  135. m_cchAlias(0)
  136. {
  137. m_szAlias[0] = 0;
  138. m_pszAlias = m_szAlias;
  139. }
  140. SCODE ScSetHref (LPCWSTR pszHref, UINT cch)
  141. {
  142. Assert (m_pszHref.get() == NULL);
  143. // Copy the namespace locally
  144. //
  145. UINT cb = CbSizeWsz(cch);
  146. //$ REVIEW: We are being forced down the path of allowing namespaces
  147. // that are not properly terminated. The way we do this, is if the
  148. // namespace is not properly terminated, or ends in an '#', we will
  149. // append an '#' character.
  150. //
  151. // What this means is the namespace "urn:xml-data", when assembled
  152. // into a fully qualified tag would become "urn:xml-data#dt". Also,
  153. // the namespace "urn:exch-data#" would become "urn:exch-data##dt".
  154. //
  155. // What we catch here, is the handling for an unterminated namespace.
  156. // If the namespace ends in a non-terminator or a '#' character, then
  157. // we will want to append one.
  158. //
  159. // It is important to note that the appended char is NOT included in
  160. // the total character count of the href.
  161. //
  162. // If we are dealing with the empty namespace, we will not append a #.
  163. //
  164. BOOL fUnterminated = FALSE;
  165. if (0 != cch)
  166. {
  167. Assert (pszHref);
  168. WCHAR wch = pszHref[cch - 1];
  169. if ((wchHiddenNmspcSep == wch) || !FIsNmspcSeparator(wch))
  170. {
  171. NmspcTrace ("Nmspc: WARNING: namespace does not have a separator\n"
  172. " as the last character of the namespace. DAV will\n"
  173. " add a '#' to the namespace for internal processing.\n");
  174. fUnterminated = TRUE;
  175. // Make sure there is space for the appended character
  176. //
  177. cb += sizeof(WCHAR);
  178. }
  179. }
  180. //
  181. //$ REVIEW: end;
  182. // Allocate space and copy everything over
  183. //
  184. m_pszHref = static_cast<LPWSTR>(ExAlloc(cb));
  185. if (NULL == m_pszHref.get())
  186. return E_OUTOFMEMORY;
  187. // Note: CopyMemory does not dereference pszHref if cch equals 0.
  188. //
  189. CopyMemory (m_pszHref, pszHref, cch * sizeof(WCHAR));
  190. m_cchHref = cch;
  191. // If it is unterminated, handle that here
  192. //
  193. if (fUnterminated)
  194. {
  195. NmspcTrace ("Nmspc: WARNING: '#' appended to mis-terminated namespace\n");
  196. m_pszHref[cch++] = wchHiddenNmspcSep;
  197. Assert (CchHref() == m_cchHref);
  198. Assert (PszHref() == m_pszHref.get());
  199. Assert (wchHiddenNmspcSep == m_pszHref[m_cchHref]);
  200. }
  201. // Ensure termination
  202. //
  203. m_pszHref[cch] = 0;
  204. NmspcTrace ("Nmspc: href defined\n"
  205. "-- m_pszHref: %ws\n"
  206. "-- m_szAlias: %ws\n",
  207. m_pszHref,
  208. m_szAlias);
  209. return S_OK;
  210. }
  211. SCODE ScSetAlias (LPCWSTR pszAlias, UINT cchAlias)
  212. {
  213. // Copy the alias locally
  214. //
  215. Assert (pszAlias);
  216. UINT cb = CbSizeWsz(cchAlias);
  217. if (cb <= sizeof(m_szAlias))
  218. {
  219. CopyMemory (m_szAlias, pszAlias, cchAlias * sizeof(WCHAR));
  220. m_pszAlias = m_szAlias;
  221. }
  222. else
  223. {
  224. m_pszLongAlias.realloc (cb);
  225. if (NULL == m_pszLongAlias.get())
  226. return E_OUTOFMEMORY;
  227. CopyMemory (m_pszLongAlias, pszAlias, cchAlias * sizeof(WCHAR));
  228. m_pszAlias = m_pszLongAlias.get();
  229. }
  230. m_pszAlias[cchAlias] = L'\0';
  231. m_cchAlias = cchAlias;
  232. NmspcTrace ("Nmspc: alias defined\n"
  233. "-- m_pszHref: %ws\n"
  234. "-- m_szAlias: %ws\n",
  235. m_pszHref,
  236. m_pszAlias);
  237. return S_OK;
  238. }
  239. UINT CchHref() const { return m_cchHref; }
  240. UINT CchAlias() const { return m_cchAlias; }
  241. LPCWSTR PszHref() const { return m_pszHref; }
  242. LPCWSTR PszAlias() const { return m_pszAlias; }
  243. // Namespace Scoping -----------------------------------------------------
  244. //
  245. CNmspc* PnsScopedNamespace() const { return m_pnsScoped.get(); }
  246. void SetScopedNamespace (CNmspc* pns)
  247. {
  248. m_pnsScoped = pns;
  249. }
  250. CNmspc* PnsSibling() const { return m_pnsSiblings.get(); }
  251. void SetSibling (CNmspc* pns)
  252. {
  253. m_pnsSiblings = pns;
  254. }
  255. };
  256. // class CNmspcCache ---------------------------------------------------------
  257. //
  258. class CNmspcCache
  259. {
  260. public:
  261. typedef CCache<CRCWszN, auto_ref_ptr<CNmspc> > NmspcCache;
  262. NmspcCache m_cache;
  263. protected:
  264. ChainedStringBuffer<WCHAR> m_sb;
  265. // Key generation
  266. //
  267. virtual CRCWszN IndexKey (auto_ref_ptr<CNmspc>& pns) = 0;
  268. // non-implemented
  269. //
  270. CNmspcCache(const CNmspcCache& p);
  271. CNmspcCache& operator=(const CNmspcCache& p);
  272. public:
  273. CNmspcCache()
  274. {
  275. INIT_TRACE(Nmspc);
  276. }
  277. SCODE ScInit() { return m_cache.FInit() ? S_OK : E_OUTOFMEMORY; }
  278. void CachePersisted (auto_ref_ptr<CNmspc>& pns)
  279. {
  280. auto_ref_ptr<CNmspc>* parp = NULL;
  281. CRCWszN key = IndexKey(pns);
  282. // Take a quick peek to see if the index already
  283. // exists in the cache. If it does, then setup
  284. // the scoping such that when the namespace falls
  285. // out of scope, the original namespace will be
  286. // restored.
  287. //
  288. if (NULL != (parp = m_cache.Lookup (key)))
  289. {
  290. NmspcTrace ("Nmspc: scoped redefinition of namespace:\n"
  291. "-- old: '%ws' as '%ws'\n"
  292. "-- new: '%ws' as '%ws'\n",
  293. (*parp)->PszHref(),
  294. (*parp)->PszAlias(),
  295. pns->PszHref(),
  296. pns->PszAlias());
  297. pns->SetScopedNamespace(parp->get());
  298. }
  299. // Setup the index
  300. //
  301. NmspcTrace ("Nmspc: indexing namespace\n"
  302. "-- ns: '%ws' as '%ws'\n",
  303. pns->PszHref(),
  304. pns->PszAlias());
  305. (void) m_cache.FAdd (key, pns);
  306. }
  307. void RemovePersisted (auto_ref_ptr<CNmspc>& pns)
  308. {
  309. auto_ref_ptr<CNmspc> pnsScoped;
  310. // Disconnect the index to this namespace
  311. //
  312. NmspcTrace ("Nmspc: namespace falling out of scope\n"
  313. "-- ns: '%ws' as '%ws'\n",
  314. pns->PszHref(),
  315. pns->PszAlias());
  316. m_cache.Remove (IndexKey(pns));
  317. // If there was an index in existance before this
  318. // namespace came into scope, reinstate it here.
  319. //
  320. pnsScoped = pns->PnsScopedNamespace();
  321. if (pnsScoped.get())
  322. {
  323. NmspcTrace ("Nmspc: restoring redefined namespace:\n"
  324. "-- restored: '%ws' as '%ws'\n"
  325. "-- from: '%ws' as '%ws'\n",
  326. pnsScoped->PszHref(),
  327. pnsScoped->PszAlias(),
  328. pns->PszHref(),
  329. pns->PszAlias());
  330. (void) m_cache.FAdd (IndexKey(pnsScoped), pnsScoped);
  331. }
  332. }
  333. };
  334. // class CParseNmspcCache ----------------------------------------------------
  335. //
  336. class CParseNmspcCache : public CNmspcCache
  337. {
  338. // non-implemented
  339. //
  340. CParseNmspcCache(const CNmspcCache& p);
  341. CParseNmspcCache& operator=(const CNmspcCache& p);
  342. virtual CRCWszN IndexKey (auto_ref_ptr<CNmspc>& pns)
  343. {
  344. return CRCWszN (pns->PszAlias(), pns->CchAlias());
  345. }
  346. // Namespace lookup ------------------------------------------------------
  347. //
  348. BOOL FNmspcFromAlias (LPCWSTR pszAlias, UINT cch, auto_ref_ptr<CNmspc>& pns)
  349. {
  350. // In this scenario, the namespace should already exist.
  351. // if it doesn't things will not go well.
  352. //
  353. auto_ref_ptr<CNmspc> * parp = NULL;
  354. parp = m_cache.Lookup (CRCWszN(pszAlias, cch));
  355. if (parp)
  356. {
  357. pns = *parp;
  358. return TRUE;
  359. }
  360. return FALSE;
  361. }
  362. public:
  363. CParseNmspcCache()
  364. {
  365. }
  366. // Token translations ----------------------------------------------------
  367. //
  368. SCODE TranslateToken (LPCWSTR* ppwszTok,
  369. ULONG* pcchTok,
  370. LPCWSTR* ppwszNmspc,
  371. ULONG* pcchNmspc)
  372. {
  373. auto_ref_ptr<CNmspc> pns;
  374. SCODE sc = S_FALSE;
  375. Assert (pcchTok && (*pcchTok != 0));
  376. Assert (ppwszTok && *ppwszTok);
  377. Assert (pcchNmspc);
  378. Assert (ppwszNmspc);
  379. // See if there is an namespace that matches the persisted
  380. // alias.
  381. //
  382. if (FNmspcFromAlias (*ppwszTok, *pcchNmspc, pns))
  383. {
  384. // Passback the namespace
  385. //
  386. *pcchNmspc = pns->CchHref();
  387. *ppwszNmspc = pns->PszHref();
  388. //$ REVIEW: We are being forced down the path of allowing namespaces
  389. // that are not properly terminated. The way we do this, is if the
  390. // namespace is not properly terminated, or ends in an '#', we will
  391. // append an '#' character.
  392. //
  393. // What this means is the namespace "urn:xml-data", when assembled
  394. // into a fully qualified tag would become "urn:xml-data#dt". Also,
  395. // the namespace "urn:exch-data#" would become "urn:exch-data##dt".
  396. //
  397. // What we catch here, is the first part of reconstruction of a fully
  398. // qualified namespace. If the href we want to pass back is non-
  399. // terminated or ends in a '#' character, then we want to append one.
  400. // When we cached the namespace object, we did the appending there.
  401. // So, the character already exists, the character count just doesn't
  402. // include it (see CNmspc::SetHref() above).
  403. //
  404. // If we are dealing with the empty namespace, we will not append a #.
  405. //
  406. if (0 != pns->CchHref())
  407. {
  408. WCHAR wch = pns->PszHref()[pns->CchHref() - 1];
  409. if ((wchHiddenNmspcSep == wch) || !FIsNmspcSeparator(wch))
  410. {
  411. NmspcTrace ("Nmspc: WARNING: namespace is not properly terminated\n"
  412. " and DAV will add in a '#' for internal processing.\n");
  413. Assert (wchHiddenNmspcSep == pns->PszHref()[pns->CchHref()]);
  414. *pcchNmspc = *pcchNmspc + 1;
  415. }
  416. }
  417. //
  418. //$ REVIEW: end.
  419. // Adjust the token to refer to the tagname -- ie. the
  420. // text after the namespace alias and colon. If the alias
  421. // is zero-length, then this maps to the "default" namespace
  422. // and no colon skipping is done.
  423. //
  424. if (0 != pns->CchAlias())
  425. {
  426. *pcchTok = *pcchTok - (pns->CchAlias() + 1);
  427. *ppwszTok = *ppwszTok + (pns->CchAlias() + 1);
  428. }
  429. // Tell the caller there is a translation
  430. //
  431. sc = S_OK;
  432. }
  433. else
  434. {
  435. // If the caller expected a namespace, but one did not
  436. // exist, it is an error. If they didn't expect one to
  437. // to exist -- ie. *pcchNmspc was 0 -- then it is not an
  438. // error. Make sure the caller knows what the real
  439. // situation is.
  440. //
  441. if (0 == *pcchNmspc)
  442. sc = S_OK;
  443. // It looks like no alias was specified, so we can just
  444. // return the name as persisted.
  445. //
  446. *ppwszNmspc = NULL;
  447. *pcchNmspc = 0;
  448. }
  449. return sc;
  450. }
  451. };
  452. // class CEmitterNmspcCache --------------------------------------------------
  453. //
  454. class CEmitterNmspcCache : public CNmspcCache
  455. {
  456. LONG m_lAlias;
  457. // non-implemented
  458. //
  459. CEmitterNmspcCache(const CEmitterNmspcCache& p);
  460. CEmitterNmspcCache& operator=(const CEmitterNmspcCache& p);
  461. protected:
  462. void AdjustAliasNumber(LONG lOffset) { m_lAlias += lOffset; }
  463. // Key generation
  464. //
  465. virtual CRCWszN IndexKey (auto_ref_ptr<CNmspc>& pns)
  466. {
  467. return CRCWszN (pns->PszHref(), pns->CchHref());
  468. }
  469. SCODE ScNmspcFromHref (LPCWSTR pszHref, UINT cch, auto_ref_ptr<CNmspc>& pns)
  470. {
  471. // Lookup to see if the namespace already exists
  472. //
  473. auto_ref_ptr<CNmspc>* parp = m_cache.Lookup (CRCWszN(pszHref, cch));
  474. // If it doesn't exist, then create a new one and cache it
  475. //
  476. if (parp == NULL)
  477. {
  478. WCHAR wszAlias[10];
  479. ULONG cchAlias;
  480. SCODE sc;
  481. // Generate an alias to apply to this namespace and then
  482. // check to see if this alias has been already used. If
  483. // not, then go ahead an use it.
  484. //
  485. cchAlias = CchGenerateAlias (m_lAlias++, wszAlias);
  486. // Create a new cache item
  487. //
  488. pns.take_ownership(new CNmspc());
  489. if (NULL == pns.get())
  490. return E_OUTOFMEMORY;
  491. // Set the HREF
  492. //
  493. sc = pns->ScSetHref (pszHref, cch);
  494. if (FAILED (sc))
  495. return sc;
  496. // Set the alias
  497. //
  498. sc = pns->ScSetAlias (wszAlias, cchAlias);
  499. if (FAILED (sc))
  500. return sc;
  501. // It is important, that the key and the return value are taken
  502. // from items in the cache, otherwise the lifetime of the data
  503. // may not scope the usage.
  504. //
  505. CachePersisted (pns);
  506. return S_FALSE;
  507. }
  508. pns = *parp;
  509. return S_OK;
  510. }
  511. public:
  512. CEmitterNmspcCache() :
  513. m_lAlias(0)
  514. {
  515. }
  516. };
  517. #endif // _EX_NMSPC_H_