vbscript: Fix possible memory leak/crash caused by race condition in VBScriptParse_In...
[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 #include "config.h"
20
21 #include <stdarg.h>
22 #include <assert.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "ole2.h"
29
30 #include "vbscript.h"
31
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
35
36 #ifdef _WIN64
37
38 #define CTXARG_T DWORDLONG
39 #define IActiveScriptParseVtbl IActiveScriptParse64Vtbl
40
41 #else
42
43 #define CTXARG_T DWORD
44 #define IActiveScriptParseVtbl IActiveScriptParse32Vtbl
45
46 #endif
47
48 struct VBScript {
49     IActiveScript IActiveScript_iface;
50     IActiveScriptParse IActiveScriptParse_iface;
51
52     LONG ref;
53
54     SCRIPTSTATE state;
55     IActiveScriptSite *site;
56     script_ctx_t *ctx;
57     LONG thread_id;
58     LCID lcid;
59 };
60
61 static void change_state(VBScript *This, SCRIPTSTATE state)
62 {
63     if(This->state == state)
64         return;
65
66     This->state = state;
67     if(This->site)
68         IActiveScriptSite_OnStateChange(This->site, state);
69 }
70
71 static HRESULT set_ctx_site(VBScript *This)
72 {
73     This->ctx->lcid = This->lcid;
74
75     IActiveScriptSite_AddRef(This->site);
76     This->ctx->site = This->site;
77
78     change_state(This, SCRIPTSTATE_INITIALIZED);
79     return S_OK;
80 }
81
82 static void destroy_script(script_ctx_t *ctx)
83 {
84     if(ctx->site)
85         IActiveScriptSite_Release(ctx->site);
86     heap_free(ctx);
87 }
88
89 static void decrease_state(VBScript *This, SCRIPTSTATE state)
90 {
91     switch(This->state) {
92     case SCRIPTSTATE_CONNECTED:
93     case SCRIPTSTATE_STARTED:
94     case SCRIPTSTATE_DISCONNECTED:
95         FIXME("unimplemented state %d\n", This->state);
96         if(state == SCRIPTSTATE_INITIALIZED)
97             break;
98         /* FALLTHROUGH */
99     case SCRIPTSTATE_INITIALIZED:
100         destroy_script(This->ctx);
101         This->ctx = NULL;
102         This->thread_id = 0;
103
104         change_state(This, state);
105         if(state == SCRIPTSTATE_UNINITIALIZED)
106             break;
107         /* FALLTHROUGH */
108     case SCRIPTSTATE_UNINITIALIZED:
109         if(This->site) {
110             IActiveScriptSite_Release(This->site);
111             This->site = NULL;
112         }
113
114         break;
115     default:
116         assert(0);
117     }
118 }
119
120 static inline VBScript *impl_from_IActiveScript(IActiveScript *iface)
121 {
122     return CONTAINING_RECORD(iface, VBScript, IActiveScript_iface);
123 }
124
125 static HRESULT WINAPI VBScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv)
126 {
127     VBScript *This = impl_from_IActiveScript(iface);
128
129     if(IsEqualGUID(riid, &IID_IUnknown)) {
130         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
131         *ppv = &This->IActiveScript_iface;
132     }else if(IsEqualGUID(riid, &IID_IActiveScript)) {
133         TRACE("(%p)->(IID_IActiveScript %p)\n", This, ppv);
134         *ppv = &This->IActiveScript_iface;
135     }else if(IsEqualGUID(riid, &IID_IActiveScriptParse)) {
136         TRACE("(%p)->(IID_IActiveScriptParse %p)\n", This, ppv);
137         *ppv = &This->IActiveScriptParse_iface;
138     }else {
139         FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
140         *ppv = NULL;
141         return E_NOINTERFACE;
142     }
143
144     IUnknown_AddRef((IUnknown*)*ppv);
145     return S_OK;
146 }
147
148 static ULONG WINAPI VBScript_AddRef(IActiveScript *iface)
149 {
150     VBScript *This = impl_from_IActiveScript(iface);
151     LONG ref = InterlockedIncrement(&This->ref);
152
153     TRACE("(%p) ref=%d\n", This, ref);
154
155     return ref;
156 }
157
158 static ULONG WINAPI VBScript_Release(IActiveScript *iface)
159 {
160     VBScript *This = impl_from_IActiveScript(iface);
161     LONG ref = InterlockedDecrement(&This->ref);
162
163     TRACE("(%p) ref=%d\n", iface, ref);
164
165     if(!ref) {
166         if(This->site)
167             IActiveScriptSite_Release(This->site);
168         heap_free(This);
169     }
170
171     return ref;
172 }
173
174 static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass)
175 {
176     VBScript *This = impl_from_IActiveScript(iface);
177     LCID lcid;
178     HRESULT hres;
179
180     TRACE("(%p)->(%p)\n", This, pass);
181
182     if(!pass)
183         return E_POINTER;
184
185     if(This->site)
186         return E_UNEXPECTED;
187
188     if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0))
189         return E_UNEXPECTED;
190
191     This->site = pass;
192     IActiveScriptSite_AddRef(This->site);
193
194     hres = IActiveScriptSite_GetLCID(This->site, &lcid);
195     if(hres == S_OK)
196         This->lcid = lcid;
197
198     return This->ctx ? set_ctx_site(This) : S_OK;
199 }
200
201 static HRESULT WINAPI VBScript_GetScriptSite(IActiveScript *iface, REFIID riid,
202                                             void **ppvObject)
203 {
204     VBScript *This = impl_from_IActiveScript(iface);
205     FIXME("(%p)->()\n", This);
206     return E_NOTIMPL;
207 }
208
209 static HRESULT WINAPI VBScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE ss)
210 {
211     VBScript *This = impl_from_IActiveScript(iface);
212     FIXME("(%p)->(%d)\n", This, ss);
213     return S_OK;
214 }
215
216 static HRESULT WINAPI VBScript_GetScriptState(IActiveScript *iface, SCRIPTSTATE *pssState)
217 {
218     VBScript *This = impl_from_IActiveScript(iface);
219
220     TRACE("(%p)->(%p)\n", This, pssState);
221
222     if(!pssState)
223         return E_POINTER;
224
225     if(This->thread_id && This->thread_id != GetCurrentThreadId())
226         return E_UNEXPECTED;
227
228     *pssState = This->state;
229     return S_OK;
230 }
231
232 static HRESULT WINAPI VBScript_Close(IActiveScript *iface)
233 {
234     VBScript *This = impl_from_IActiveScript(iface);
235
236     TRACE("(%p)->()\n", This);
237
238     if(This->thread_id && This->thread_id != GetCurrentThreadId())
239         return E_UNEXPECTED;
240
241     decrease_state(This, SCRIPTSTATE_CLOSED);
242     return S_OK;
243 }
244
245 static HRESULT WINAPI VBScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstrName, DWORD dwFlags)
246 {
247     VBScript *This = impl_from_IActiveScript(iface);
248     FIXME("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags);
249     return S_OK;
250 }
251
252 static HRESULT WINAPI VBScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib,
253         DWORD dwMajor, DWORD dwMinor, DWORD dwFlags)
254 {
255     VBScript *This = impl_from_IActiveScript(iface);
256     FIXME("(%p)->()\n", This);
257     return E_NOTIMPL;
258 }
259
260 static HRESULT WINAPI VBScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName, IDispatch **ppdisp)
261 {
262     VBScript *This = impl_from_IActiveScript(iface);
263     FIXME("(%p)->(%p)\n", This, ppdisp);
264     return E_NOTIMPL;
265 }
266
267 static HRESULT WINAPI VBScript_GetCurrentScriptThreadID(IActiveScript *iface,
268                                                        SCRIPTTHREADID *pstridThread)
269 {
270     VBScript *This = impl_from_IActiveScript(iface);
271     FIXME("(%p)->()\n", This);
272     return E_NOTIMPL;
273 }
274
275 static HRESULT WINAPI VBScript_GetScriptThreadID(IActiveScript *iface,
276                                                 DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread)
277 {
278     VBScript *This = impl_from_IActiveScript(iface);
279     FIXME("(%p)->()\n", This);
280     return E_NOTIMPL;
281 }
282
283 static HRESULT WINAPI VBScript_GetScriptThreadState(IActiveScript *iface,
284         SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState)
285 {
286     VBScript *This = impl_from_IActiveScript(iface);
287     FIXME("(%p)->()\n", This);
288     return E_NOTIMPL;
289 }
290
291 static HRESULT WINAPI VBScript_InterruptScriptThread(IActiveScript *iface,
292         SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags)
293 {
294     VBScript *This = impl_from_IActiveScript(iface);
295     FIXME("(%p)->()\n", This);
296     return E_NOTIMPL;
297 }
298
299 static HRESULT WINAPI VBScript_Clone(IActiveScript *iface, IActiveScript **ppscript)
300 {
301     VBScript *This = impl_from_IActiveScript(iface);
302     FIXME("(%p)->()\n", This);
303     return E_NOTIMPL;
304 }
305
306 static const IActiveScriptVtbl VBScriptVtbl = {
307     VBScript_QueryInterface,
308     VBScript_AddRef,
309     VBScript_Release,
310     VBScript_SetScriptSite,
311     VBScript_GetScriptSite,
312     VBScript_SetScriptState,
313     VBScript_GetScriptState,
314     VBScript_Close,
315     VBScript_AddNamedItem,
316     VBScript_AddTypeLib,
317     VBScript_GetScriptDispatch,
318     VBScript_GetCurrentScriptThreadID,
319     VBScript_GetScriptThreadID,
320     VBScript_GetScriptThreadState,
321     VBScript_InterruptScriptThread,
322     VBScript_Clone
323 };
324
325 static inline VBScript *impl_from_IActiveScriptParse(IActiveScriptParse *iface)
326 {
327     return CONTAINING_RECORD(iface, VBScript, IActiveScriptParse_iface);
328 }
329
330 static HRESULT WINAPI VBScriptParse_QueryInterface(IActiveScriptParse *iface, REFIID riid, void **ppv)
331 {
332     VBScript *This = impl_from_IActiveScriptParse(iface);
333     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
334 }
335
336 static ULONG WINAPI VBScriptParse_AddRef(IActiveScriptParse *iface)
337 {
338     VBScript *This = impl_from_IActiveScriptParse(iface);
339     return IActiveScript_AddRef(&This->IActiveScript_iface);
340 }
341
342 static ULONG WINAPI VBScriptParse_Release(IActiveScriptParse *iface)
343 {
344     VBScript *This = impl_from_IActiveScriptParse(iface);
345     return IActiveScript_Release(&This->IActiveScript_iface);
346 }
347
348 static HRESULT WINAPI VBScriptParse_InitNew(IActiveScriptParse *iface)
349 {
350     VBScript *This = impl_from_IActiveScriptParse(iface);
351     script_ctx_t *ctx, *old_ctx;
352
353     TRACE("(%p)\n", This);
354
355     if(This->ctx)
356         return E_UNEXPECTED;
357
358     ctx = heap_alloc_zero(sizeof(script_ctx_t));
359     if(!ctx)
360         return E_OUTOFMEMORY;
361
362     old_ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL);
363     if(old_ctx) {
364         destroy_script(ctx);
365         return E_UNEXPECTED;
366     }
367
368     return This->site ? set_ctx_site(This) : S_OK;
369 }
370
371 static HRESULT WINAPI VBScriptParse_AddScriptlet(IActiveScriptParse *iface,
372         LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode, LPCOLESTR pstrItemName,
373         LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, LPCOLESTR pstrDelimiter,
374         CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags,
375         BSTR *pbstrName, EXCEPINFO *pexcepinfo)
376 {
377     VBScript *This = impl_from_IActiveScriptParse(iface);
378     FIXME("(%p)->(%s %s %s %s %s %s %s %u %x %p %p)\n", This, debugstr_w(pstrDefaultName),
379           debugstr_w(pstrCode), debugstr_w(pstrItemName), debugstr_w(pstrSubItemName),
380           debugstr_w(pstrEventName), debugstr_w(pstrDelimiter), wine_dbgstr_longlong(dwSourceContextCookie),
381           ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo);
382     return E_NOTIMPL;
383 }
384
385 static HRESULT WINAPI VBScriptParse_ParseScriptText(IActiveScriptParse *iface,
386         LPCOLESTR pstrCode, LPCOLESTR pstrItemName, IUnknown *punkContext,
387         LPCOLESTR pstrDelimiter, CTXARG_T dwSourceContextCookie, ULONG ulStartingLine,
388         DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo)
389 {
390     VBScript *This = impl_from_IActiveScriptParse(iface);
391     FIXME("(%p)->(%s %s %p %s %s %u %x %p %p)\n", This, debugstr_w(pstrCode),
392           debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter),
393           wine_dbgstr_longlong(dwSourceContextCookie), ulStartingLine, dwFlags, pvarResult, pexcepinfo);
394     return E_NOTIMPL;
395 }
396
397 static const IActiveScriptParseVtbl VBScriptParseVtbl = {
398     VBScriptParse_QueryInterface,
399     VBScriptParse_AddRef,
400     VBScriptParse_Release,
401     VBScriptParse_InitNew,
402     VBScriptParse_AddScriptlet,
403     VBScriptParse_ParseScriptText
404 };
405
406 HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, void **ppv)
407 {
408     VBScript *ret;
409     HRESULT hres;
410
411     TRACE("(%p %s %p)\n", pUnkOuter, debugstr_guid(riid), ppv);
412
413     ret = heap_alloc_zero(sizeof(*ret));
414     if(!ret)
415         return E_OUTOFMEMORY;
416
417     ret->IActiveScript_iface.lpVtbl = &VBScriptVtbl;
418     ret->IActiveScriptParse_iface.lpVtbl = &VBScriptParseVtbl;
419
420     ret->ref = 1;
421     ret->state = SCRIPTSTATE_UNINITIALIZED;
422
423     hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv);
424     IActiveScript_Release(&ret->IActiveScript_iface);
425     return hres;
426 }