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