vbscript: Added AddNamedItem implementation.
[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     while(!list_empty(&ctx->named_items)) {
91         named_item_t *iter = LIST_ENTRY(list_head(&ctx->named_items), named_item_t, entry);
92
93         list_remove(&iter->entry);
94         if(iter->disp)
95             IDispatch_Release(iter->disp);
96         heap_free(iter->name);
97         heap_free(iter);
98     }
99
100     if(ctx->host_global)
101         IDispatch_Release(ctx->host_global);
102     if(ctx->site)
103         IActiveScriptSite_Release(ctx->site);
104     if(ctx->script_obj)
105         IDispatchEx_Release(&ctx->script_obj->IDispatchEx_iface);
106     heap_free(ctx);
107 }
108
109 static void decrease_state(VBScript *This, SCRIPTSTATE state)
110 {
111     switch(This->state) {
112     case SCRIPTSTATE_CONNECTED:
113         change_state(This, SCRIPTSTATE_DISCONNECTED);
114         if(state == SCRIPTSTATE_DISCONNECTED)
115             return;
116         /* FALLTHROUGH */
117     case SCRIPTSTATE_STARTED:
118     case SCRIPTSTATE_DISCONNECTED:
119         if(This->state == SCRIPTSTATE_DISCONNECTED)
120             change_state(This, SCRIPTSTATE_INITIALIZED);
121         if(state == SCRIPTSTATE_INITIALIZED)
122             break;
123         /* FALLTHROUGH */
124     case SCRIPTSTATE_INITIALIZED:
125     case SCRIPTSTATE_UNINITIALIZED:
126         change_state(This, state);
127
128         if(This->site) {
129             IActiveScriptSite_Release(This->site);
130             This->site = NULL;
131         }
132
133         This->thread_id = 0;
134
135         if(state == SCRIPTSTATE_CLOSED) {
136             destroy_script(This->ctx);
137             This->ctx = NULL;
138         }
139
140         break;
141     default:
142         assert(0);
143     }
144 }
145
146 static inline VBScript *impl_from_IActiveScript(IActiveScript *iface)
147 {
148     return CONTAINING_RECORD(iface, VBScript, IActiveScript_iface);
149 }
150
151 static HRESULT WINAPI VBScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv)
152 {
153     VBScript *This = impl_from_IActiveScript(iface);
154
155     if(IsEqualGUID(riid, &IID_IUnknown)) {
156         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
157         *ppv = &This->IActiveScript_iface;
158     }else if(IsEqualGUID(riid, &IID_IActiveScript)) {
159         TRACE("(%p)->(IID_IActiveScript %p)\n", This, ppv);
160         *ppv = &This->IActiveScript_iface;
161     }else if(IsEqualGUID(riid, &IID_IActiveScriptParse)) {
162         TRACE("(%p)->(IID_IActiveScriptParse %p)\n", This, ppv);
163         *ppv = &This->IActiveScriptParse_iface;
164     }else if(IsEqualGUID(riid, &IID_IObjectSafety)) {
165         TRACE("(%p)->(IID_IObjectSafety %p)\n", This, ppv);
166         *ppv = &This->IObjectSafety_iface;
167     }else {
168         FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
169         *ppv = NULL;
170         return E_NOINTERFACE;
171     }
172
173     IUnknown_AddRef((IUnknown*)*ppv);
174     return S_OK;
175 }
176
177 static ULONG WINAPI VBScript_AddRef(IActiveScript *iface)
178 {
179     VBScript *This = impl_from_IActiveScript(iface);
180     LONG ref = InterlockedIncrement(&This->ref);
181
182     TRACE("(%p) ref=%d\n", This, ref);
183
184     return ref;
185 }
186
187 static ULONG WINAPI VBScript_Release(IActiveScript *iface)
188 {
189     VBScript *This = impl_from_IActiveScript(iface);
190     LONG ref = InterlockedDecrement(&This->ref);
191
192     TRACE("(%p) ref=%d\n", iface, ref);
193
194     if(!ref) {
195         if(This->site)
196             IActiveScriptSite_Release(This->site);
197         heap_free(This);
198     }
199
200     return ref;
201 }
202
203 static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass)
204 {
205     VBScript *This = impl_from_IActiveScript(iface);
206     LCID lcid;
207     HRESULT hres;
208
209     TRACE("(%p)->(%p)\n", This, pass);
210
211     if(!pass)
212         return E_POINTER;
213
214     if(This->site)
215         return E_UNEXPECTED;
216
217     if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0))
218         return E_UNEXPECTED;
219
220     This->site = pass;
221     IActiveScriptSite_AddRef(This->site);
222
223     hres = IActiveScriptSite_GetLCID(This->site, &lcid);
224     if(hres == S_OK)
225         This->lcid = lcid;
226
227     return This->ctx ? set_ctx_site(This) : S_OK;
228 }
229
230 static HRESULT WINAPI VBScript_GetScriptSite(IActiveScript *iface, REFIID riid,
231                                             void **ppvObject)
232 {
233     VBScript *This = impl_from_IActiveScript(iface);
234     FIXME("(%p)->()\n", This);
235     return E_NOTIMPL;
236 }
237
238 static HRESULT WINAPI VBScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE ss)
239 {
240     VBScript *This = impl_from_IActiveScript(iface);
241
242     TRACE("(%p)->(%d)\n", This, ss);
243
244     if(This->thread_id && GetCurrentThreadId() != This->thread_id)
245         return E_UNEXPECTED;
246
247     if(ss == SCRIPTSTATE_UNINITIALIZED) {
248         if(This->state == SCRIPTSTATE_CLOSED)
249             return E_UNEXPECTED;
250
251         decrease_state(This, SCRIPTSTATE_UNINITIALIZED);
252         return S_OK;
253     }
254
255     if(!This->ctx)
256         return E_UNEXPECTED;
257
258     switch(ss) {
259     case SCRIPTSTATE_STARTED:
260     case SCRIPTSTATE_CONNECTED: /* FIXME */
261         if(This->state == SCRIPTSTATE_CLOSED)
262             return E_UNEXPECTED;
263
264         exec_queued_code(This);
265         break;
266     case SCRIPTSTATE_INITIALIZED:
267         FIXME("unimplemented SCRIPTSTATE_INITIALIZED\n");
268         return S_OK;
269     default:
270         FIXME("unimplemented state %d\n", ss);
271         return E_NOTIMPL;
272     }
273
274     change_state(This, ss);
275     return S_OK;
276 }
277
278 static HRESULT WINAPI VBScript_GetScriptState(IActiveScript *iface, SCRIPTSTATE *pssState)
279 {
280     VBScript *This = impl_from_IActiveScript(iface);
281
282     TRACE("(%p)->(%p)\n", This, pssState);
283
284     if(!pssState)
285         return E_POINTER;
286
287     if(This->thread_id && This->thread_id != GetCurrentThreadId())
288         return E_UNEXPECTED;
289
290     *pssState = This->state;
291     return S_OK;
292 }
293
294 static HRESULT WINAPI VBScript_Close(IActiveScript *iface)
295 {
296     VBScript *This = impl_from_IActiveScript(iface);
297
298     TRACE("(%p)->()\n", This);
299
300     if(This->thread_id && This->thread_id != GetCurrentThreadId())
301         return E_UNEXPECTED;
302
303     decrease_state(This, SCRIPTSTATE_CLOSED);
304     return S_OK;
305 }
306
307 static HRESULT WINAPI VBScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstrName, DWORD dwFlags)
308 {
309     VBScript *This = impl_from_IActiveScript(iface);
310     named_item_t *item;
311     IDispatch *disp = NULL;
312     HRESULT hres;
313
314     TRACE("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags);
315
316     if(This->thread_id != GetCurrentThreadId() || !This->ctx || This->state == SCRIPTSTATE_CLOSED)
317         return E_UNEXPECTED;
318
319     if(dwFlags & SCRIPTITEM_GLOBALMEMBERS) {
320         IUnknown *unk;
321
322         hres = IActiveScriptSite_GetItemInfo(This->site, pstrName, SCRIPTINFO_IUNKNOWN, &unk, NULL);
323         if(FAILED(hres)) {
324             WARN("GetItemInfo failed: %08x\n", hres);
325             return hres;
326         }
327
328         hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&disp);
329         IUnknown_Release(unk);
330         if(FAILED(hres)) {
331             WARN("object does not implement IDispatch\n");
332             return hres;
333         }
334
335         if(This->ctx->host_global)
336             IDispatch_Release(This->ctx->host_global);
337         IDispatch_AddRef(disp);
338         This->ctx->host_global = disp;
339     }
340
341     item = heap_alloc(sizeof(*item));
342     if(!item) {
343         if(disp)
344             IDispatch_Release(disp);
345         return E_OUTOFMEMORY;
346     }
347
348     item->disp = disp;
349     item->flags = dwFlags;
350     item->name = heap_strdupW(pstrName);
351     if(!item->name) {
352         if(disp)
353             IDispatch_Release(disp);
354         heap_free(item);
355         return E_OUTOFMEMORY;
356     }
357
358     list_add_tail(&This->ctx->named_items, &item->entry);
359     return S_OK;
360 }
361
362 static HRESULT WINAPI VBScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib,
363         DWORD dwMajor, DWORD dwMinor, DWORD dwFlags)
364 {
365     VBScript *This = impl_from_IActiveScript(iface);
366     FIXME("(%p)->()\n", This);
367     return E_NOTIMPL;
368 }
369
370 static HRESULT WINAPI VBScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName, IDispatch **ppdisp)
371 {
372     VBScript *This = impl_from_IActiveScript(iface);
373
374     TRACE("(%p)->(%p)\n", This, ppdisp);
375
376     if(!ppdisp)
377         return E_POINTER;
378
379     if(This->thread_id != GetCurrentThreadId() || !This->ctx->script_obj) {
380         *ppdisp = NULL;
381         return E_UNEXPECTED;
382     }
383
384     *ppdisp = (IDispatch*)&This->ctx->script_obj->IDispatchEx_iface;
385     IDispatch_AddRef(*ppdisp);
386     return S_OK;
387 }
388
389 static HRESULT WINAPI VBScript_GetCurrentScriptThreadID(IActiveScript *iface,
390                                                        SCRIPTTHREADID *pstridThread)
391 {
392     VBScript *This = impl_from_IActiveScript(iface);
393     FIXME("(%p)->()\n", This);
394     return E_NOTIMPL;
395 }
396
397 static HRESULT WINAPI VBScript_GetScriptThreadID(IActiveScript *iface,
398                                                 DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread)
399 {
400     VBScript *This = impl_from_IActiveScript(iface);
401     FIXME("(%p)->()\n", This);
402     return E_NOTIMPL;
403 }
404
405 static HRESULT WINAPI VBScript_GetScriptThreadState(IActiveScript *iface,
406         SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState)
407 {
408     VBScript *This = impl_from_IActiveScript(iface);
409     FIXME("(%p)->()\n", This);
410     return E_NOTIMPL;
411 }
412
413 static HRESULT WINAPI VBScript_InterruptScriptThread(IActiveScript *iface,
414         SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags)
415 {
416     VBScript *This = impl_from_IActiveScript(iface);
417     FIXME("(%p)->()\n", This);
418     return E_NOTIMPL;
419 }
420
421 static HRESULT WINAPI VBScript_Clone(IActiveScript *iface, IActiveScript **ppscript)
422 {
423     VBScript *This = impl_from_IActiveScript(iface);
424     FIXME("(%p)->()\n", This);
425     return E_NOTIMPL;
426 }
427
428 static const IActiveScriptVtbl VBScriptVtbl = {
429     VBScript_QueryInterface,
430     VBScript_AddRef,
431     VBScript_Release,
432     VBScript_SetScriptSite,
433     VBScript_GetScriptSite,
434     VBScript_SetScriptState,
435     VBScript_GetScriptState,
436     VBScript_Close,
437     VBScript_AddNamedItem,
438     VBScript_AddTypeLib,
439     VBScript_GetScriptDispatch,
440     VBScript_GetCurrentScriptThreadID,
441     VBScript_GetScriptThreadID,
442     VBScript_GetScriptThreadState,
443     VBScript_InterruptScriptThread,
444     VBScript_Clone
445 };
446
447 static inline VBScript *impl_from_IActiveScriptParse(IActiveScriptParse *iface)
448 {
449     return CONTAINING_RECORD(iface, VBScript, IActiveScriptParse_iface);
450 }
451
452 static HRESULT WINAPI VBScriptParse_QueryInterface(IActiveScriptParse *iface, REFIID riid, void **ppv)
453 {
454     VBScript *This = impl_from_IActiveScriptParse(iface);
455     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
456 }
457
458 static ULONG WINAPI VBScriptParse_AddRef(IActiveScriptParse *iface)
459 {
460     VBScript *This = impl_from_IActiveScriptParse(iface);
461     return IActiveScript_AddRef(&This->IActiveScript_iface);
462 }
463
464 static ULONG WINAPI VBScriptParse_Release(IActiveScriptParse *iface)
465 {
466     VBScript *This = impl_from_IActiveScriptParse(iface);
467     return IActiveScript_Release(&This->IActiveScript_iface);
468 }
469
470 static HRESULT WINAPI VBScriptParse_InitNew(IActiveScriptParse *iface)
471 {
472     VBScript *This = impl_from_IActiveScriptParse(iface);
473     script_ctx_t *ctx, *old_ctx;
474
475     TRACE("(%p)\n", This);
476
477     if(This->ctx)
478         return E_UNEXPECTED;
479
480     ctx = heap_alloc_zero(sizeof(script_ctx_t));
481     if(!ctx)
482         return E_OUTOFMEMORY;
483
484     list_init(&ctx->named_items);
485
486     old_ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL);
487     if(old_ctx) {
488         destroy_script(ctx);
489         return E_UNEXPECTED;
490     }
491
492     return This->site ? set_ctx_site(This) : S_OK;
493 }
494
495 static HRESULT WINAPI VBScriptParse_AddScriptlet(IActiveScriptParse *iface,
496         LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode, LPCOLESTR pstrItemName,
497         LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, LPCOLESTR pstrDelimiter,
498         CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags,
499         BSTR *pbstrName, EXCEPINFO *pexcepinfo)
500 {
501     VBScript *This = impl_from_IActiveScriptParse(iface);
502     FIXME("(%p)->(%s %s %s %s %s %s %s %u %x %p %p)\n", This, debugstr_w(pstrDefaultName),
503           debugstr_w(pstrCode), debugstr_w(pstrItemName), debugstr_w(pstrSubItemName),
504           debugstr_w(pstrEventName), debugstr_w(pstrDelimiter), wine_dbgstr_longlong(dwSourceContextCookie),
505           ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo);
506     return E_NOTIMPL;
507 }
508
509 static HRESULT WINAPI VBScriptParse_ParseScriptText(IActiveScriptParse *iface,
510         LPCOLESTR pstrCode, LPCOLESTR pstrItemName, IUnknown *punkContext,
511         LPCOLESTR pstrDelimiter, CTXARG_T dwSourceContextCookie, ULONG ulStartingLine,
512         DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo)
513 {
514     VBScript *This = impl_from_IActiveScriptParse(iface);
515     FIXME("(%p)->(%s %s %p %s %s %u %x %p %p)\n", This, debugstr_w(pstrCode),
516           debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter),
517           wine_dbgstr_longlong(dwSourceContextCookie), ulStartingLine, dwFlags, pvarResult, pexcepinfo);
518     return E_NOTIMPL;
519 }
520
521 static const IActiveScriptParseVtbl VBScriptParseVtbl = {
522     VBScriptParse_QueryInterface,
523     VBScriptParse_AddRef,
524     VBScriptParse_Release,
525     VBScriptParse_InitNew,
526     VBScriptParse_AddScriptlet,
527     VBScriptParse_ParseScriptText
528 };
529
530 static inline VBScript *impl_from_IObjectSafety(IObjectSafety *iface)
531 {
532     return CONTAINING_RECORD(iface, VBScript, IObjectSafety_iface);
533 }
534
535 static HRESULT WINAPI VBScriptSafety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
536 {
537     VBScript *This = impl_from_IObjectSafety(iface);
538     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
539 }
540
541 static ULONG WINAPI VBScriptSafety_AddRef(IObjectSafety *iface)
542 {
543     VBScript *This = impl_from_IObjectSafety(iface);
544     return IActiveScript_AddRef(&This->IActiveScript_iface);
545 }
546
547 static ULONG WINAPI VBScriptSafety_Release(IObjectSafety *iface)
548 {
549     VBScript *This = impl_from_IObjectSafety(iface);
550     return IActiveScript_Release(&This->IActiveScript_iface);
551 }
552
553 #define SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_DISPEX|INTERFACE_USES_SECURITY_MANAGER)
554
555 static HRESULT WINAPI VBScriptSafety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
556         DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
557 {
558     VBScript *This = impl_from_IObjectSafety(iface);
559
560     TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), pdwSupportedOptions, pdwEnabledOptions);
561
562     if(!pdwSupportedOptions || !pdwEnabledOptions)
563         return E_POINTER;
564
565     *pdwSupportedOptions = SUPPORTED_OPTIONS;
566     *pdwEnabledOptions = This->safeopt;
567     return S_OK;
568 }
569
570 static HRESULT WINAPI VBScriptSafety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
571         DWORD dwOptionSetMask, DWORD dwEnabledOptions)
572 {
573     VBScript *This = impl_from_IObjectSafety(iface);
574
575     TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), dwOptionSetMask, dwEnabledOptions);
576
577     if(dwOptionSetMask & ~SUPPORTED_OPTIONS)
578         return E_FAIL;
579
580     This->safeopt = (dwEnabledOptions & dwOptionSetMask) | (This->safeopt & ~dwOptionSetMask) | INTERFACE_USES_DISPEX;
581     return S_OK;
582 }
583
584 static const IObjectSafetyVtbl VBScriptSafetyVtbl = {
585     VBScriptSafety_QueryInterface,
586     VBScriptSafety_AddRef,
587     VBScriptSafety_Release,
588     VBScriptSafety_GetInterfaceSafetyOptions,
589     VBScriptSafety_SetInterfaceSafetyOptions
590 };
591
592 HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, void **ppv)
593 {
594     VBScript *ret;
595     HRESULT hres;
596
597     TRACE("(%p %s %p)\n", pUnkOuter, debugstr_guid(riid), ppv);
598
599     ret = heap_alloc_zero(sizeof(*ret));
600     if(!ret)
601         return E_OUTOFMEMORY;
602
603     ret->IActiveScript_iface.lpVtbl = &VBScriptVtbl;
604     ret->IActiveScriptParse_iface.lpVtbl = &VBScriptParseVtbl;
605     ret->IObjectSafety_iface.lpVtbl = &VBScriptSafetyVtbl;
606
607     ret->ref = 1;
608     ret->state = SCRIPTSTATE_UNINITIALIZED;
609     ret->safeopt = INTERFACE_USES_DISPEX;
610
611     hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv);
612     IActiveScript_Release(&ret->IActiveScript_iface);
613     return hres;
614 }