Sync with trunk r63174.
[reactos.git] / dll / win32 / mscoree / config.c
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 }