1 /* 2 * Configuration file parsing 3 * 4 * Copyright 2010 Vincent Povirk 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include "mscoree_private.h" 22 23 #include <ole2.h> 24 #include <shlwapi.h> 25 #include <initguid.h> 26 #include <msxml2.h> 27 28 enum parse_state 29 { 30 STATE_ROOT, 31 STATE_CONFIGURATION, 32 STATE_STARTUP, 33 STATE_UNKNOWN 34 }; 35 36 typedef struct ConfigFileHandler 37 { 38 ISAXContentHandler ISAXContentHandler_iface; 39 ISAXErrorHandler ISAXErrorHandler_iface; 40 LONG ref; 41 enum parse_state states[16]; 42 int statenum; 43 parsed_config_file *result; 44 } ConfigFileHandler; 45 46 static inline ConfigFileHandler *impl_from_ISAXContentHandler(ISAXContentHandler *iface) 47 { 48 return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXContentHandler_iface); 49 } 50 51 static inline ConfigFileHandler *impl_from_ISAXErrorHandler(ISAXErrorHandler *iface) 52 { 53 return CONTAINING_RECORD(iface, ConfigFileHandler, ISAXErrorHandler_iface); 54 } 55 56 static HRESULT WINAPI ConfigFileHandler_QueryInterface(ISAXContentHandler *iface, 57 REFIID riid, void **ppvObject) 58 { 59 if (IsEqualGUID(riid, &IID_ISAXContentHandler) || 60 IsEqualGUID(riid, &IID_IUnknown)) 61 { 62 *ppvObject = iface; 63 } 64 else 65 { 66 WARN("Unsupported interface %s\n", debugstr_guid(riid)); 67 return E_NOINTERFACE; 68 } 69 70 ISAXContentHandler_AddRef(iface); 71 72 return S_OK; 73 } 74 75 static ULONG WINAPI ConfigFileHandler_AddRef(ISAXContentHandler *iface) 76 { 77 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); 78 return InterlockedIncrement(&This->ref); 79 } 80 81 static ULONG WINAPI ConfigFileHandler_Release(ISAXContentHandler *iface) 82 { 83 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); 84 ULONG ref = InterlockedDecrement(&This->ref); 85 86 if (ref == 0) 87 HeapFree(GetProcessHeap(), 0, This); 88 89 return ref; 90 } 91 92 static HRESULT WINAPI ConfigFileHandler_putDocumentLocator(ISAXContentHandler *iface, 93 ISAXLocator *pLocator) 94 { 95 return S_OK; 96 } 97 98 static HRESULT WINAPI ConfigFileHandler_startDocument(ISAXContentHandler *iface) 99 { 100 return S_OK; 101 } 102 103 static HRESULT WINAPI ConfigFileHandler_endDocument(ISAXContentHandler *iface) 104 { 105 return S_OK; 106 } 107 108 static HRESULT WINAPI ConfigFileHandler_startPrefixMapping(ISAXContentHandler *iface, 109 const WCHAR *pPrefix, int nPrefix, const WCHAR *pUri, int nUri) 110 { 111 return S_OK; 112 } 113 114 static HRESULT WINAPI ConfigFileHandler_endPrefixMapping(ISAXContentHandler *iface, 115 const WCHAR *pPrefix, int nPrefix) 116 { 117 return S_OK; 118 } 119 120 static HRESULT parse_startup(ConfigFileHandler *This, ISAXAttributes *pAttr) 121 { 122 static const WCHAR legacy[] = {'u','s','e','L','e','g','a','c','y','V','2','R','u','n','t','i','m','e','A','c','t','i','v','a','t','i','o','n','P','o','l','i','c','y',0}; 123 static const WCHAR empty[] = {0}; 124 LPCWSTR value; 125 int value_size; 126 HRESULT hr; 127 128 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, legacy, lstrlenW(legacy), &value, &value_size); 129 if (SUCCEEDED(hr)) 130 FIXME("useLegacyV2RuntimeActivationPolicy=%s not implemented\n", debugstr_wn(value, value_size)); 131 hr = S_OK; 132 133 return hr; 134 } 135 136 static HRESULT parse_supported_runtime(ConfigFileHandler *This, ISAXAttributes *pAttr) 137 { 138 static const WCHAR version[] = {'v','e','r','s','i','o','n',0}; 139 static const WCHAR sku[] = {'s','k','u',0}; 140 static const WCHAR empty[] = {0}; 141 LPCWSTR value; 142 int value_size; 143 HRESULT hr; 144 supported_runtime *entry; 145 146 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, version, lstrlenW(version), &value, &value_size); 147 if (SUCCEEDED(hr)) 148 { 149 TRACE("%s\n", debugstr_wn(value, value_size)); 150 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(supported_runtime)); 151 if (entry) 152 { 153 entry->version = HeapAlloc(GetProcessHeap(), 0, (value_size + 1) * sizeof(WCHAR)); 154 if (entry->version) 155 { 156 lstrcpyW(entry->version, value); 157 list_add_tail(&This->result->supported_runtimes, &entry->entry); 158 } 159 else 160 { 161 HeapFree(GetProcessHeap(), 0, entry); 162 hr = E_OUTOFMEMORY; 163 } 164 } 165 else 166 hr = E_OUTOFMEMORY; 167 } 168 else 169 WARN("Missing version attribute\n"); 170 171 if (SUCCEEDED(hr)) 172 { 173 hr = ISAXAttributes_getValueFromName(pAttr, empty, 0, sku, lstrlenW(sku), &value, &value_size); 174 if (SUCCEEDED(hr)) 175 FIXME("sku=%s not implemented\n", debugstr_wn(value, value_size)); 176 hr = S_OK; 177 } 178 179 return hr; 180 } 181 182 static HRESULT WINAPI ConfigFileHandler_startElement(ISAXContentHandler *iface, 183 const WCHAR *pNamespaceUri, int nNamespaceUri, const WCHAR *pLocalName, 184 int nLocalName, const WCHAR *pQName, int nQName, ISAXAttributes *pAttr) 185 { 186 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); 187 static const WCHAR configuration[] = {'c','o','n','f','i','g','u','r','a','t','i','o','n',0}; 188 static const WCHAR startup[] = {'s','t','a','r','t','u','p',0}; 189 static const WCHAR supportedRuntime[] = {'s','u','p','p','o','r','t','e','d','R','u','n','t','i','m','e',0}; 190 HRESULT hr = S_OK; 191 192 TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri), 193 debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName)); 194 195 if (This->statenum == sizeof(This->states) / sizeof(This->states[0]) - 1) 196 { 197 ERR("file has too much nesting\n"); 198 return E_FAIL; 199 } 200 201 switch (This->states[This->statenum]) 202 { 203 case STATE_ROOT: 204 if (nLocalName == sizeof(configuration)/sizeof(WCHAR)-1 && 205 lstrcmpW(pLocalName, configuration) == 0) 206 { 207 This->states[++This->statenum] = STATE_CONFIGURATION; 208 break; 209 } 210 else 211 goto unknown; 212 case STATE_CONFIGURATION: 213 if (nLocalName == sizeof(startup)/sizeof(WCHAR)-1 && 214 lstrcmpW(pLocalName, startup) == 0) 215 { 216 hr = parse_startup(This, pAttr); 217 This->states[++This->statenum] = STATE_STARTUP; 218 break; 219 } 220 else 221 goto unknown; 222 case STATE_STARTUP: 223 if (nLocalName == sizeof(supportedRuntime)/sizeof(WCHAR)-1 && 224 lstrcmpW(pLocalName, supportedRuntime) == 0) 225 { 226 hr = parse_supported_runtime(This, pAttr); 227 This->states[++This->statenum] = STATE_UNKNOWN; 228 break; 229 } 230 else 231 goto unknown; 232 default: 233 goto unknown; 234 } 235 236 return hr; 237 238 unknown: 239 FIXME("Unknown element %s in state %u\n", debugstr_wn(pLocalName,nLocalName), 240 This->states[This->statenum]); 241 242 This->states[++This->statenum] = STATE_UNKNOWN; 243 244 return S_OK; 245 } 246 247 static HRESULT WINAPI ConfigFileHandler_endElement(ISAXContentHandler *iface, 248 const WCHAR *pNamespaceUri, int nNamespaceUri, const WCHAR *pLocalName, 249 int nLocalName, const WCHAR *pQName, int nQName) 250 { 251 ConfigFileHandler *This = impl_from_ISAXContentHandler(iface); 252 253 TRACE("%s %s %s\n", debugstr_wn(pNamespaceUri,nNamespaceUri), 254 debugstr_wn(pLocalName,nLocalName), debugstr_wn(pQName,nQName)); 255 256 if (This->statenum > 0) 257 { 258 This->statenum--; 259 } 260 else 261 { 262 ERR("element end does not match a start\n"); 263 return E_FAIL; 264 } 265 266 return S_OK; 267 } 268 269 static HRESULT WINAPI ConfigFileHandler_characters(ISAXContentHandler *iface, 270 const WCHAR *pChars, int nChars) 271 { 272 TRACE("%s\n", debugstr_wn(pChars,nChars)); 273 274 return S_OK; 275 } 276 277 static HRESULT WINAPI ConfigFileHandler_ignorableWhitespace(ISAXContentHandler *iface, 278 const WCHAR *pChars, int nChars) 279 { 280 return S_OK; 281 } 282 283 static HRESULT WINAPI ConfigFileHandler_processingInstruction(ISAXContentHandler *iface, 284 const WCHAR *pTarget, int nTarget, const WCHAR *pData, int nData) 285 { 286 return S_OK; 287 } 288 289 static HRESULT WINAPI ConfigFileHandler_skippedEntity(ISAXContentHandler *iface, 290 const WCHAR * pName, int nName) 291 { 292 TRACE("%s\n", debugstr_wn(pName,nName)); 293 return S_OK; 294 } 295 296 static const struct ISAXContentHandlerVtbl ConfigFileHandlerVtbl = 297 { 298 ConfigFileHandler_QueryInterface, 299 ConfigFileHandler_AddRef, 300 ConfigFileHandler_Release, 301 ConfigFileHandler_putDocumentLocator, 302 ConfigFileHandler_startDocument, 303 ConfigFileHandler_endDocument, 304 ConfigFileHandler_startPrefixMapping, 305 ConfigFileHandler_endPrefixMapping, 306 ConfigFileHandler_startElement, 307 ConfigFileHandler_endElement, 308 ConfigFileHandler_characters, 309 ConfigFileHandler_ignorableWhitespace, 310 ConfigFileHandler_processingInstruction, 311 ConfigFileHandler_skippedEntity 312 }; 313 314 static HRESULT WINAPI ConfigFileHandler_Error_QueryInterface(ISAXErrorHandler *iface, 315 REFIID riid, void **ppvObject) 316 { 317 if (IsEqualGUID(riid, &IID_ISAXErrorHandler) || 318 IsEqualGUID(riid, &IID_IUnknown)) 319 { 320 *ppvObject = iface; 321 } 322 else 323 { 324 WARN("Unsupported interface %s\n", debugstr_guid(riid)); 325 return E_NOINTERFACE; 326 } 327 328 ISAXErrorHandler_AddRef(iface); 329 330 return S_OK; 331 } 332 333 static ULONG WINAPI ConfigFileHandler_Error_AddRef(ISAXErrorHandler *iface) 334 { 335 ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); 336 return IUnknown_AddRef((IUnknown*)This); 337 } 338 339 static ULONG WINAPI ConfigFileHandler_Error_Release(ISAXErrorHandler *iface) 340 { 341 ConfigFileHandler *This = impl_from_ISAXErrorHandler(iface); 342 return IUnknown_Release((IUnknown*)This); 343 } 344 345 static HRESULT WINAPI ConfigFileHandler_error(ISAXErrorHandler *iface, 346 ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) 347 { 348 WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); 349 return S_OK; 350 } 351 352 static HRESULT WINAPI ConfigFileHandler_fatalError(ISAXErrorHandler *iface, 353 ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) 354 { 355 WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); 356 return S_OK; 357 } 358 359 static HRESULT WINAPI ConfigFileHandler_ignorableWarning(ISAXErrorHandler *iface, 360 ISAXLocator * pLocator, const WCHAR * pErrorMessage, HRESULT hrErrorCode) 361 { 362 WARN("%s,%x\n", debugstr_w(pErrorMessage), hrErrorCode); 363 return S_OK; 364 } 365 366 static const struct ISAXErrorHandlerVtbl ConfigFileHandlerErrorVtbl = 367 { 368 ConfigFileHandler_Error_QueryInterface, 369 ConfigFileHandler_Error_AddRef, 370 ConfigFileHandler_Error_Release, 371 ConfigFileHandler_error, 372 ConfigFileHandler_fatalError, 373 ConfigFileHandler_ignorableWarning 374 }; 375 376 static void init_config(parsed_config_file *config) 377 { 378 list_init(&config->supported_runtimes); 379 } 380 381 static HRESULT parse_config(VARIANT input, parsed_config_file *result) 382 { 383 ISAXXMLReader *reader; 384 ConfigFileHandler *handler; 385 HRESULT hr; 386 387 handler = HeapAlloc(GetProcessHeap(), 0, sizeof(ConfigFileHandler)); 388 if (!handler) 389 return E_OUTOFMEMORY; 390 391 handler->ISAXContentHandler_iface.lpVtbl = &ConfigFileHandlerVtbl; 392 handler->ISAXErrorHandler_iface.lpVtbl = &ConfigFileHandlerErrorVtbl; 393 handler->ref = 1; 394 handler->states[0] = STATE_ROOT; 395 handler->statenum = 0; 396 handler->result = result; 397 398 hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER, 399 &IID_ISAXXMLReader, (LPVOID*)&reader); 400 401 if (SUCCEEDED(hr)) 402 { 403 hr = ISAXXMLReader_putContentHandler(reader, &handler->ISAXContentHandler_iface); 404 405 if (SUCCEEDED(hr)) 406 hr = ISAXXMLReader_putErrorHandler(reader, &handler->ISAXErrorHandler_iface); 407 408 if (SUCCEEDED(hr)) 409 hr = ISAXXMLReader_parse(reader, input); 410 411 ISAXXMLReader_Release(reader); 412 } 413 414 ISAXContentHandler_Release(&handler->ISAXContentHandler_iface); 415 416 return S_OK; 417 } 418 419 HRESULT parse_config_file(LPCWSTR filename, parsed_config_file *result) 420 { 421 IStream *stream; 422 VARIANT var; 423 HRESULT hr; 424 HRESULT initresult; 425 426 init_config(result); 427 428 initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 429 430 hr = SHCreateStreamOnFileW(filename, STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE, &stream); 431 432 if (SUCCEEDED(hr)) 433 { 434 V_VT(&var) = VT_UNKNOWN; 435 V_UNKNOWN(&var) = (IUnknown*)stream; 436 437 hr = parse_config(var, result); 438 439 IStream_Release(stream); 440 } 441 442 if (SUCCEEDED(initresult)) 443 CoUninitialize(); 444 445 return hr; 446 } 447 448 void free_parsed_config_file(parsed_config_file *file) 449 { 450 supported_runtime *cursor, *cursor2; 451 452 LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &file->supported_runtimes, supported_runtime, entry) 453 { 454 HeapFree(GetProcessHeap(), 0, cursor->version); 455 list_remove(&cursor->entry); 456 HeapFree(GetProcessHeap(), 0, cursor); 457 } 458 } 459