xref: /reactos/dll/win32/mscoree/config.c (revision 84ccccab)
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