vbscript: Added more uninitialization tests and fixes.
[wine] / dlls / vbscript / vbscript.c
1 /*
2  * Copyright 2011 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19
20 #include <assert.h>
21
22 #include "vbscript.h"
23 #include "objsafe.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
28
29 #ifdef _WIN64
30
31 #define CTXARG_T DWORDLONG
32 #define IActiveScriptParseVtbl IActiveScriptParse64Vtbl
33
34 #else
35
36 #define CTXARG_T DWORD
37 #define IActiveScriptParseVtbl IActiveScriptParse32Vtbl
38
39 #endif
40
41 struct VBScript {
42     IActiveScript IActiveScript_iface;
43     IActiveScriptParse IActiveScriptParse_iface;
44     IObjectSafety IObjectSafety_iface;
45
46     LONG ref;
47
48     DWORD safeopt;
49     SCRIPTSTATE state;
50     IActiveScriptSite *site;
51     script_ctx_t *ctx;
52     LONG thread_id;
53     LCID lcid;
54 };
55
56 static void change_state(VBScript *This, SCRIPTSTATE state)
57 {
58     if(This->state == state)
59         return;
60
61     This->state = state;
62     if(This->site)
63         IActiveScriptSite_OnStateChange(This->site, state);
64 }
65
66 static void exec_queued_code(VBScript *This)
67 {
68     FIXME("\n");
69 }
70
71 static HRESULT set_ctx_site(VBScript *This)
72 {
73     HRESULT hres;
74
75     This->ctx->lcid = This->lcid;
76
77     hres = init_global(This->ctx);
78     if(FAILED(hres))
79         return hres;
80
81     IActiveScriptSite_AddRef(This->site);
82     This->ctx->site = This->site;
83
84     change_state(This, SCRIPTSTATE_INITIALIZED);
85     return S_OK;
86 }
87
88 static void destroy_script(script_ctx_t *ctx)
89 {
90     if(ctx->site)
91         IActiveScriptSite_Release(ctx->site);
92     if(ctx->script_obj)
93         IDispatchEx_Release(&ctx->script_obj->IDispatchEx_iface);
94     heap_free(ctx);
95 }
96
97 static void decrease_state(VBScript *This, SCRIPTSTATE state)
98 {
99     switch(This->state) {
100     case SCRIPTSTATE_CONNECTED:
101         change_state(This, SCRIPTSTATE_DISCONNECTED);
102         if(state == SCRIPTSTATE_DISCONNECTED)
103             return;
104         /* FALLTHROUGH */
105     case SCRIPTSTATE_STARTED:
106     case SCRIPTSTATE_DISCONNECTED:
107         if(This->state == SCRIPTSTATE_DISCONNECTED)
108             change_state(This, SCRIPTSTATE_INITIALIZED);
109         if(state == SCRIPTSTATE_INITIALIZED)
110             break;
111         /* FALLTHROUGH */
112     case SCRIPTSTATE_INITIALIZED:
113     case SCRIPTSTATE_UNINITIALIZED:
114         change_state(This, state);
115
116         if(This->site) {
117             IActiveScriptSite_Release(This->site);
118             This->site = NULL;
119         }
120
121         This->thread_id = 0;
122
123         if(state == SCRIPTSTATE_CLOSED) {
124             destroy_script(This->ctx);
125             This->ctx = NULL;
126         }
127
128         break;
129     default:
130         assert(0);
131     }
132 }
133
134 static inline VBScript *impl_from_IActiveScript(IActiveScript *iface)
135 {
136     return CONTAINING_RECORD(iface, VBScript, IActiveScript_iface);
137 }
138
139 static HRESULT WINAPI VBScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv)
140 {
141     VBScript *This = impl_from_IActiveScript(iface);
142
143     if(IsEqualGUID(riid, &IID_IUnknown)) {
144         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
145         *ppv = &This->IActiveScript_iface;
146     }else if(IsEqualGUID(riid, &IID_IActiveScript)) {
147         TRACE("(%p)->(IID_IActiveScript %p)\n", This, ppv);
148         *ppv = &This->IActiveScript_iface;
149     }else if(IsEqualGUID(riid, &IID_IActiveScriptParse)) {
150         TRACE("(%p)->(IID_IActiveScriptParse %p)\n", This, ppv);
151         *ppv = &This->IActiveScriptParse_iface;
152     }else if(IsEqualGUID(riid, &IID_IObjectSafety)) {
153         TRACE("(%p)->(IID_IObjectSafety %p)\n", This, ppv);
154         *ppv = &This->IObjectSafety_iface;
155     }else {
156         FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
157         *ppv = NULL;
158         return E_NOINTERFACE;
159     }
160
161     IUnknown_AddRef((IUnknown*)*ppv);
162     return S_OK;
163 }
164
165 static ULONG WINAPI VBScript_AddRef(IActiveScript *iface)
166 {
167     VBScript *This = impl_from_IActiveScript(iface);
168     LONG ref = InterlockedIncrement(&This->ref);
169
170     TRACE("(%p) ref=%d\n", This, ref);
171
172     return ref;
173 }
174
175 static ULONG WINAPI VBScript_Release(IActiveScript *iface)
176 {
177     VBScript *This = impl_from_IActiveScript(iface);
178     LONG ref = InterlockedDecrement(&This->ref);
179
180     TRACE("(%p) ref=%d\n", iface, ref);
181
182     if(!ref) {
183         if(This->site)
184             IActiveScriptSite_Release(This->site);
185         heap_free(This);
186     }
187
188     return ref;
189 }
190
191 static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass)
192 {
193     VBScript *This = impl_from_IActiveScript(iface);
194     LCID lcid;
195     HRESULT hres;
196
197     TRACE("(%p)->(%p)\n", This, pass);
198
199     if(!pass)
200         return E_POINTER;
201
202     if(This->site)
203         return E_UNEXPECTED;
204
205     if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0))
206         return E_UNEXPECTED;
207
208     This->site = pass;
209     IActiveScriptSite_AddRef(This->site);
210
211     hres = IActiveScriptSite_GetLCID(This->site, &lcid);
212     if(hres == S_OK)
213         This->lcid = lcid;
214
215     return This->ctx ? set_ctx_site(This) : S_OK;
216 }
217
218 static HRESULT WINAPI VBScript_GetScriptSite(IActiveScript *iface, REFIID riid,
219                                             void **ppvObject)
220 {
221     VBScript *This = impl_from_IActiveScript(iface);
222     FIXME("(%p)->()\n", This);
223     return E_NOTIMPL;
224 }
225
226 static HRESULT WINAPI VBScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE ss)
227 {
228     VBScript *This = impl_from_IActiveScript(iface);
229
230     TRACE("(%p)->(%d)\n", This, ss);
231
232     if(This->thread_id && GetCurrentThreadId() != This->thread_id)
233         return E_UNEXPECTED;
234
235     if(ss == SCRIPTSTATE_UNINITIALIZED) {
236         if(This->state == SCRIPTSTATE_CLOSED)
237             return E_UNEXPECTED;
238
239         decrease_state(This, SCRIPTSTATE_UNINITIALIZED);
240         return S_OK;
241     }
242
243     if(!This->ctx)
244         return E_UNEXPECTED;
245
246     switch(ss) {
247     case SCRIPTSTATE_STARTED:
248     case SCRIPTSTATE_CONNECTED: /* FIXME */
249         if(This->state == SCRIPTSTATE_CLOSED)
250             return E_UNEXPECTED;
251
252         exec_queued_code(This);
253         break;
254     case SCRIPTSTATE_INITIALIZED:
255         FIXME("unimplemented SCRIPTSTATE_INITIALIZED\n");
256         return S_OK;
257     default:
258         FIXME("unimplemented state %d\n", ss);
259         return E_NOTIMPL;
260     }
261
262     change_state(This, ss);
263     return S_OK;
264 }
265
266 static HRESULT WINAPI VBScript_GetScriptState(IActiveScript *iface, SCRIPTSTATE *pssState)
267 {
268     VBScript *This = impl_from_IActiveScript(iface);
269
270     TRACE("(%p)->(%p)\n", This, pssState);
271
272     if(!pssState)
273         return E_POINTER;
274
275     if(This->thread_id && This->thread_id != GetCurrentThreadId())
276         return E_UNEXPECTED;
277
278     *pssState = This->state;
279     return S_OK;
280 }
281
282 static HRESULT WINAPI VBScript_Close(IActiveScript *iface)
283 {
284     VBScript *This = impl_from_IActiveScript(iface);
285
286     TRACE("(%p)->()\n", This);
287
288     if(This->thread_id && This->thread_id != GetCurrentThreadId())
289         return E_UNEXPECTED;
290
291     decrease_state(This, SCRIPTSTATE_CLOSED);
292     return S_OK;
293 }
294
295 static HRESULT WINAPI VBScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstrName, DWORD dwFlags)
296 {
297     VBScript *This = impl_from_IActiveScript(iface);
298     FIXME("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags);
299     return S_OK;
300 }
301
302 static HRESULT WINAPI VBScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib,
303         DWORD dwMajor, DWORD dwMinor, DWORD dwFlags)
304 {
305     VBScript *This = impl_from_IActiveScript(iface);
306     FIXME("(%p)->()\n", This);
307     return E_NOTIMPL;
308 }
309
310 static HRESULT WINAPI VBScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName, IDispatch **ppdisp)
311 {
312     VBScript *This = impl_from_IActiveScript(iface);
313
314     TRACE("(%p)->(%p)\n", This, ppdisp);
315
316     if(!ppdisp)
317         return E_POINTER;
318
319     if(This->thread_id != GetCurrentThreadId() || !This->ctx->script_obj) {
320         *ppdisp = NULL;
321         return E_UNEXPECTED;
322     }
323
324     *ppdisp = (IDispatch*)&This->ctx->script_obj->IDispatchEx_iface;
325     IDispatch_AddRef(*ppdisp);
326     return S_OK;
327 }
328
329 static HRESULT WINAPI VBScript_GetCurrentScriptThreadID(IActiveScript *iface,
330                                                        SCRIPTTHREADID *pstridThread)
331 {
332     VBScript *This = impl_from_IActiveScript(iface);
333     FIXME("(%p)->()\n", This);
334     return E_NOTIMPL;
335 }
336
337 static HRESULT WINAPI VBScript_GetScriptThreadID(IActiveScript *iface,
338                                                 DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread)
339 {
340     VBScript *This = impl_from_IActiveScript(iface);
341     FIXME("(%p)->()\n", This);
342     return E_NOTIMPL;
343 }
344
345 static HRESULT WINAPI VBScript_GetScriptThreadState(IActiveScript *iface,
346         SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState)
347 {
348     VBScript *This = impl_from_IActiveScript(iface);
349     FIXME("(%p)->()\n", This);
350     return E_NOTIMPL;
351 }
352
353 static HRESULT WINAPI VBScript_InterruptScriptThread(IActiveScript *iface,
354         SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags)
355 {
356     VBScript *This = impl_from_IActiveScript(iface);
357     FIXME("(%p)->()\n", This);
358     return E_NOTIMPL;
359 }
360
361 static HRESULT WINAPI VBScript_Clone(IActiveScript *iface, IActiveScript **ppscript)
362 {
363     VBScript *This = impl_from_IActiveScript(iface);
364     FIXME("(%p)->()\n", This);
365     return E_NOTIMPL;
366 }
367
368 static const IActiveScriptVtbl VBScriptVtbl = {
369     VBScript_QueryInterface,
370     VBScript_AddRef,
371     VBScript_Release,
372     VBScript_SetScriptSite,
373     VBScript_GetScriptSite,
374     VBScript_SetScriptState,
375     VBScript_GetScriptState,
376     VBScript_Close,
377     VBScript_AddNamedItem,
378     VBScript_AddTypeLib,
379     VBScript_GetScriptDispatch,
380     VBScript_GetCurrentScriptThreadID,
381     VBScript_GetScriptThreadID,
382     VBScript_GetScriptThreadState,
383     VBScript_InterruptScriptThread,
384     VBScript_Clone
385 };
386
387 static inline VBScript *impl_from_IActiveScriptParse(IActiveScriptParse *iface)
388 {
389     return CONTAINING_RECORD(iface, VBScript, IActiveScriptParse_iface);
390 }
391
392 static HRESULT WINAPI VBScriptParse_QueryInterface(IActiveScriptParse *iface, REFIID riid, void **ppv)
393 {
394     VBScript *This = impl_from_IActiveScriptParse(iface);
395     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
396 }
397
398 static ULONG WINAPI VBScriptParse_AddRef(IActiveScriptParse *iface)
399 {
400     VBScript *This = impl_from_IActiveScriptParse(iface);
401     return IActiveScript_AddRef(&This->IActiveScript_iface);
402 }
403
404 static ULONG WINAPI VBScriptParse_Release(IActiveScriptParse *iface)
405 {
406     VBScript *This = impl_from_IActiveScriptParse(iface);
407     return IActiveScript_Release(&This->IActiveScript_iface);
408 }
409
410 static HRESULT WINAPI VBScriptParse_InitNew(IActiveScriptParse *iface)
411 {
412     VBScript *This = impl_from_IActiveScriptParse(iface);
413     script_ctx_t *ctx, *old_ctx;
414
415     TRACE("(%p)\n", This);
416
417     if(This->ctx)
418         return E_UNEXPECTED;
419
420     ctx = heap_alloc_zero(sizeof(script_ctx_t));
421     if(!ctx)
422         return E_OUTOFMEMORY;
423
424     old_ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL);
425     if(old_ctx) {
426         destroy_script(ctx);
427         return E_UNEXPECTED;
428     }
429
430     return This->site ? set_ctx_site(This) : S_OK;
431 }
432
433 static HRESULT WINAPI VBScriptParse_AddScriptlet(IActiveScriptParse *iface,
434         LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode, LPCOLESTR pstrItemName,
435         LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, LPCOLESTR pstrDelimiter,
436         CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags,
437         BSTR *pbstrName, EXCEPINFO *pexcepinfo)
438 {
439     VBScript *This = impl_from_IActiveScriptParse(iface);
440     FIXME("(%p)->(%s %s %s %s %s %s %s %u %x %p %p)\n", This, debugstr_w(pstrDefaultName),
441           debugstr_w(pstrCode), debugstr_w(pstrItemName), debugstr_w(pstrSubItemName),
442           debugstr_w(pstrEventName), debugstr_w(pstrDelimiter), wine_dbgstr_longlong(dwSourceContextCookie),
443           ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo);
444     return E_NOTIMPL;
445 }
446
447 static HRESULT WINAPI VBScriptParse_ParseScriptText(IActiveScriptParse *iface,
448         LPCOLESTR pstrCode, LPCOLESTR pstrItemName, IUnknown *punkContext,
449         LPCOLESTR pstrDelimiter, CTXARG_T dwSourceContextCookie, ULONG ulStartingLine,
450         DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo)
451 {
452     VBScript *This = impl_from_IActiveScriptParse(iface);
453     FIXME("(%p)->(%s %s %p %s %s %u %x %p %p)\n", This, debugstr_w(pstrCode),
454           debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter),
455           wine_dbgstr_longlong(dwSourceContextCookie), ulStartingLine, dwFlags, pvarResult, pexcepinfo);
456     return E_NOTIMPL;
457 }
458
459 static const IActiveScriptParseVtbl VBScriptParseVtbl = {
460     VBScriptParse_QueryInterface,
461     VBScriptParse_AddRef,
462     VBScriptParse_Release,
463     VBScriptParse_InitNew,
464     VBScriptParse_AddScriptlet,
465     VBScriptParse_ParseScriptText
466 };
467
468 static inline VBScript *impl_from_IObjectSafety(IObjectSafety *iface)
469 {
470     return CONTAINING_RECORD(iface, VBScript, IObjectSafety_iface);
471 }
472
473 static HRESULT WINAPI VBScriptSafety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
474 {
475     VBScript *This = impl_from_IObjectSafety(iface);
476     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
477 }
478
479 static ULONG WINAPI VBScriptSafety_AddRef(IObjectSafety *iface)
480 {
481     VBScript *This = impl_from_IObjectSafety(iface);
482     return IActiveScript_AddRef(&This->IActiveScript_iface);
483 }
484
485 static ULONG WINAPI VBScriptSafety_Release(IObjectSafety *iface)
486 {
487     VBScript *This = impl_from_IObjectSafety(iface);
488     return IActiveScript_Release(&This->IActiveScript_iface);
489 }
490
491 #define SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_DISPEX|INTERFACE_USES_SECURITY_MANAGER)
492
493 static HRESULT WINAPI VBScriptSafety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
494         DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
495 {
496     VBScript *This = impl_from_IObjectSafety(iface);
497
498     TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), pdwSupportedOptions, pdwEnabledOptions);
499
500     if(!pdwSupportedOptions || !pdwEnabledOptions)
501         return E_POINTER;
502
503     *pdwSupportedOptions = SUPPORTED_OPTIONS;
504     *pdwEnabledOptions = This->safeopt;
505     return S_OK;
506 }
507
508 static HRESULT WINAPI VBScriptSafety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
509         DWORD dwOptionSetMask, DWORD dwEnabledOptions)
510 {
511     VBScript *This = impl_from_IObjectSafety(iface);
512
513     TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), dwOptionSetMask, dwEnabledOptions);
514
515     if(dwOptionSetMask & ~SUPPORTED_OPTIONS)
516         return E_FAIL;
517
518     This->safeopt = (dwEnabledOptions & dwOptionSetMask) | (This->safeopt & ~dwOptionSetMask) | INTERFACE_USES_DISPEX;
519     return S_OK;
520 }
521
522 static const IObjectSafetyVtbl VBScriptSafetyVtbl = {
523     VBScriptSafety_QueryInterface,
524     VBScriptSafety_AddRef,
525     VBScriptSafety_Release,
526     VBScriptSafety_GetInterfaceSafetyOptions,
527     VBScriptSafety_SetInterfaceSafetyOptions
528 };
529
530 HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, void **ppv)
531 {
532     VBScript *ret;
533     HRESULT hres;
534
535     TRACE("(%p %s %p)\n", pUnkOuter, debugstr_guid(riid), ppv);
536
537     ret = heap_alloc_zero(sizeof(*ret));
538     if(!ret)
539         return E_OUTOFMEMORY;
540
541     ret->IActiveScript_iface.lpVtbl = &VBScriptVtbl;
542     ret->IActiveScriptParse_iface.lpVtbl = &VBScriptParseVtbl;
543     ret->IObjectSafety_iface.lpVtbl = &VBScriptSafetyVtbl;
544
545     ret->ref = 1;
546     ret->state = SCRIPTSTATE_UNINITIALIZED;
547     ret->safeopt = INTERFACE_USES_DISPEX;
548
549     hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv);
550     IActiveScript_Release(&ret->IActiveScript_iface);
551     return hres;
552 }