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.

478 lines
11 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // SYNOPSIS
  6. //
  7. // Defines the class XmlWriter.
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include "ias.h"
  11. #include "xmlwriter.h"
  12. #include <cwchar>
  13. #include <new>
  14. #include "classattr.h"
  15. #include "iasattr.h"
  16. #include "iasutf8.h"
  17. #include "iasutil.h"
  18. #include "sdoias.h"
  19. const wchar_t XmlWriter::rootElementName[] = L"Event";
  20. XmlWriter::XmlWriter()
  21. : begin(new wchar_t[initialCapacity]),
  22. next(begin),
  23. end(begin + initialCapacity),
  24. scratch(0),
  25. scratchCapacity(0)
  26. {
  27. }
  28. XmlWriter::~XmlWriter() throw ()
  29. {
  30. delete[] begin;
  31. delete[] scratch;
  32. }
  33. void XmlWriter::StartDocument()
  34. {
  35. AppendStartTag(rootElementName);
  36. }
  37. void XmlWriter::EndDocument()
  38. {
  39. AppendEndTag(rootElementName);
  40. Append(L'\0');
  41. }
  42. void XmlWriter::InsertElement(
  43. const wchar_t* name,
  44. const wchar_t* value,
  45. DataType dataType
  46. )
  47. {
  48. // XML markup characters. Technically, '>' only needs to be escaped if it's
  49. // part of a CDEnd sequence, but it's easier to do it every time.
  50. static const wchar_t markup[] = L"<>&";
  51. AppendStartTag(name, dataType);
  52. const wchar_t* end = value + wcslen(value);
  53. while (value < end)
  54. {
  55. // Look for markup.
  56. const wchar_t* p = wcspbrk(value, markup);
  57. // Copy until the markup or the end of the string.
  58. size_t nchar = (p == 0) ? (end - value) : (p - value);
  59. wmemcpy(Reserve(nchar), value, nchar);
  60. // Advance the cursor;
  61. value += nchar;
  62. // Escape the markup character if any.
  63. if (p != 0)
  64. {
  65. ++value;
  66. switch (*p)
  67. {
  68. case L'<':
  69. {
  70. Append(L"&lt;");
  71. break;
  72. }
  73. case L'>':
  74. {
  75. Append(L"&gt;");
  76. break;
  77. }
  78. case L'&':
  79. {
  80. Append(L"&amp;");
  81. break;
  82. }
  83. default:
  84. {
  85. __assume(0);
  86. break;
  87. }
  88. }
  89. }
  90. }
  91. AppendEndTag(name);
  92. }
  93. void XmlWriter::InsertAttribute(
  94. const wchar_t* name,
  95. const IASATTRIBUTE& value
  96. )
  97. {
  98. switch (value.Value.itType)
  99. {
  100. case IASTYPE_BOOLEAN:
  101. case IASTYPE_INTEGER:
  102. case IASTYPE_ENUM:
  103. {
  104. InsertInteger(name, value.Value.Integer);
  105. break;
  106. }
  107. case IASTYPE_INET_ADDR:
  108. {
  109. InsertInetAddr(name, value.Value.InetAddr);
  110. break;
  111. }
  112. case IASTYPE_STRING:
  113. {
  114. if (IASAttributeUnicodeAlloc(
  115. const_cast<IASATTRIBUTE*>(&value)
  116. ) != NO_ERROR)
  117. {
  118. throw std::bad_alloc();
  119. }
  120. InsertString(name, value.Value.String);
  121. break;
  122. }
  123. case IASTYPE_OCTET_STRING:
  124. case IASTYPE_PROV_SPECIFIC:
  125. {
  126. if (value.dwId == RADIUS_ATTRIBUTE_CLASS)
  127. {
  128. IASClass* cl = reinterpret_cast<IASClass*>(
  129. value.Value.OctetString.lpValue
  130. );
  131. if (cl->isMicrosoft(value.Value.OctetString.dwLength))
  132. {
  133. InsertMicrosoftClass(name, *cl);
  134. break;
  135. }
  136. }
  137. InsertOctetString(name, value.Value.OctetString);
  138. break;
  139. }
  140. case IASTYPE_UTC_TIME:
  141. {
  142. InsertUTCTime(name, value.Value.UTCTime);
  143. break;
  144. }
  145. case IASTYPE_INVALID:
  146. default:
  147. {
  148. InsertElement(name, L"", string);
  149. break;
  150. }
  151. }
  152. }
  153. inline void XmlWriter::InsertInteger(
  154. const wchar_t* name,
  155. DWORD value
  156. )
  157. {
  158. wchar_t buffer[33];
  159. _ultow(value, buffer, 10);
  160. InsertElement(name, buffer, nonNegativeInteger);
  161. }
  162. inline void XmlWriter::InsertInetAddr(
  163. const wchar_t* name,
  164. DWORD value
  165. )
  166. {
  167. wchar_t buffer[16];
  168. ias_inet_htow(value, buffer);
  169. InsertElement(name, buffer, ipv4Address);
  170. }
  171. inline void XmlWriter::InsertString(
  172. const wchar_t* name,
  173. const IAS_STRING& value
  174. )
  175. {
  176. InsertElement(name, ((value.pszWide != 0) ? value.pszWide : L""), string);
  177. }
  178. inline void XmlWriter::InsertOctetString(
  179. const wchar_t* name,
  180. const IAS_OCTET_STRING& value
  181. )
  182. {
  183. // First try to insert it as UTF-8.
  184. if (!InsertUtf8(
  185. name,
  186. reinterpret_cast<const char*>(value.lpValue),
  187. value.dwLength
  188. ))
  189. {
  190. // That failed, so insert as formatted octets.
  191. InsertBinHex(name, value);
  192. }
  193. }
  194. inline void XmlWriter::InsertUTCTime(
  195. const wchar_t* name,
  196. const FILETIME& value
  197. )
  198. {
  199. SYSTEMTIME st;
  200. FileTimeToSystemTime(&value, &st);
  201. // Use SQL Server format in case we need to convert to datetime.
  202. // 2002-01-11 11:00:12.239 plus null-terminator.
  203. const size_t maxLen = 24;
  204. wchar_t buffer[maxLen + 1];
  205. int nChar = _snwprintf(
  206. buffer,
  207. maxLen,
  208. L"%hu-%02hu-%02hu %02hu:%02hu:%02hu.%03hu",
  209. st.wYear,
  210. st.wMonth,
  211. st.wDay,
  212. st.wHour,
  213. st.wMinute,
  214. st.wSecond,
  215. st.wMilliseconds
  216. );
  217. if ((nChar < 0) || (nChar == maxLen))
  218. {
  219. buffer[maxLen] = L'\0';
  220. }
  221. InsertElement(name, buffer, sqlDateTime);
  222. }
  223. inline void XmlWriter::InsertMicrosoftClass(
  224. const wchar_t* name,
  225. const IASClass& value
  226. )
  227. {
  228. wchar_t addr[16];
  229. ias_inet_htow(value.getServerAddress(), addr);
  230. FILETIME ft = value.getLastReboot();
  231. SYSTEMTIME st;
  232. FileTimeToSystemTime(&ft, &st);
  233. // 311 65535 255.255.255.255 01/01/2001 12:00:00 18446744073709551615 + null
  234. const size_t maxLen = 66;
  235. wchar_t buffer[maxLen + 1];
  236. int nChar = _snwprintf(
  237. buffer,
  238. maxLen,
  239. L"311 %hu %s %02hu/%02hu/%04hu %02hu:%02hu:%02hu %I64u",
  240. value.getVersion(),
  241. addr,
  242. st.wMonth,
  243. st.wDay,
  244. st.wYear,
  245. st.wHour,
  246. st.wMinute,
  247. st.wSecond,
  248. value.getSerialNumber()
  249. );
  250. if ((nChar < 0) || (nChar == maxLen))
  251. {
  252. buffer[maxLen] = L'\0';
  253. }
  254. InsertElement(name, buffer, string);
  255. }
  256. inline bool XmlWriter::InsertUtf8(
  257. const wchar_t* name,
  258. const char* value,
  259. DWORD valueLen
  260. )
  261. {
  262. // Remove any trailing null terminator.
  263. if ((valueLen > 0) && (value[valueLen - 1] == '\0'))
  264. {
  265. --valueLen;
  266. }
  267. // Scan for control characters and embedded nulls.
  268. const char* end = value + valueLen;
  269. for (const char* i = value; i != end; ++i)
  270. {
  271. if (((*i) & 0x60) == 0)
  272. {
  273. return false;
  274. }
  275. }
  276. // Compute the space needed for the Unicode value.
  277. long nchar = IASUtf8ToUnicodeLength(value, valueLen);
  278. if (nchar < 0)
  279. {
  280. return false;
  281. }
  282. // Reserve space for the conversion.
  283. ReserveScratch(nchar + 1);
  284. // Convert to null-terminated Unicode.
  285. IASUtf8ToUnicode(value, valueLen, scratch);
  286. scratch[nchar] = L'\0';
  287. InsertElement(name, scratch, string);
  288. return true;
  289. }
  290. inline void XmlWriter::InsertBinHex(
  291. const wchar_t* name,
  292. const IAS_OCTET_STRING& value
  293. )
  294. {
  295. // 2 characters per octet plus a null terminator.
  296. ReserveScratch((2 * value.dwLength) + 1);
  297. wchar_t* dst = scratch;
  298. const unsigned char* src = value.lpValue;
  299. for (DWORD i = 0; i < value.dwLength; ++i)
  300. {
  301. *dst = ConvertIntegerToHexWChar((*src) >> 4);
  302. ++dst;
  303. *dst = ConvertIntegerToHexWChar((*src) & 0x0F);
  304. ++dst;
  305. ++src;
  306. }
  307. *dst = L'\0';
  308. InsertElement(name, scratch, hexBinary);
  309. }
  310. inline void XmlWriter::Append(wchar_t c)
  311. {
  312. *Reserve(1) = c;
  313. }
  314. void XmlWriter::Append(const wchar_t* sz)
  315. {
  316. size_t nchar = wcslen(sz);
  317. wmemcpy(Reserve(nchar), sz, nchar);
  318. }
  319. inline void XmlWriter::AppendStartTag(const wchar_t* name)
  320. {
  321. Append(L'<');
  322. Append(name);
  323. Append(L'>');
  324. }
  325. inline void XmlWriter::AppendStartTag(const wchar_t* name, DataType dataType)
  326. {
  327. Append(L'<');
  328. Append(name);
  329. Append(L" data_type=\"");
  330. wchar_t type = L'0' + dataType;
  331. Append(type);
  332. Append(L'\"');
  333. Append(L'>');
  334. }
  335. inline void XmlWriter::AppendEndTag(const wchar_t* name)
  336. {
  337. Append(L"</");
  338. Append(name);
  339. Append(L'>');
  340. }
  341. inline wchar_t XmlWriter::ConvertIntegerToHexWChar(unsigned char src) throw ()
  342. {
  343. return (src < 10) ? (src + L'0') : (src + (L'A' - 10));
  344. }
  345. wchar_t* XmlWriter::Reserve(size_t nchar)
  346. {
  347. wchar_t* retval = next;
  348. next += nchar;
  349. // Do we need more storage?
  350. if (next > end)
  351. {
  352. // Compute size needed and current size.
  353. size_t needed = next - begin;
  354. size_t size = needed - nchar;
  355. // At least double the capacity, but make sure it's big enough to satisfy
  356. // the request.
  357. size_t capacity = (end - begin) * 2;
  358. if (capacity < needed)
  359. {
  360. capacity = needed;
  361. }
  362. // Allocate the new buffer and copy in the data.
  363. wchar_t* newBuffer = new wchar_t[capacity];
  364. wmemcpy(newBuffer, begin, size);
  365. // Free up the old memory.
  366. delete[] begin;
  367. // Reset our state.
  368. begin = newBuffer;
  369. next = begin + size;
  370. end = begin + capacity;
  371. // Recompute now we that we have enough storage.
  372. retval = next;
  373. next += nchar;
  374. }
  375. return retval;
  376. }
  377. void XmlWriter::ReserveScratch(size_t nchar)
  378. {
  379. if (nchar > scratchCapacity)
  380. {
  381. if (nchar < minScratchCapcity)
  382. {
  383. nchar = minScratchCapcity;
  384. }
  385. wchar_t* newScratch = new wchar_t[nchar];
  386. delete[] scratch;
  387. scratch = newScratch;
  388. scratchCapacity = nchar;
  389. }
  390. }