1 /*
2  * Copyright 2010 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 <stdarg.h>
20 
21 #define COBJMACROS
22 #ifdef __REACTOS__
23 #define CONST_VTABLE
24 #endif
25 
26 #include <windef.h>
27 #include <winbase.h>
28 #include <winreg.h>
29 #include <ole2.h>
30 #include <shellapi.h>
31 #include <activscp.h>
32 #include <initguid.h>
33 
34 #include "wscript.h"
35 
36 #include <wine/debug.h>
37 
38 #ifdef _WIN64
39 
40 #define IActiveScriptParse_Release IActiveScriptParse64_Release
41 #define IActiveScriptParse_InitNew IActiveScriptParse64_InitNew
42 #define IActiveScriptParse_ParseScriptText IActiveScriptParse64_ParseScriptText
43 
44 #else
45 
46 #define IActiveScriptParse_Release IActiveScriptParse32_Release
47 #define IActiveScriptParse_InitNew IActiveScriptParse32_InitNew
48 #define IActiveScriptParse_ParseScriptText IActiveScriptParse32_ParseScriptText
49 
50 #endif
51 
52 WINE_DEFAULT_DEBUG_CHANNEL(wscript);
53 
54 static const WCHAR wscriptW[] = {'W','S','c','r','i','p','t',0};
55 static const WCHAR wshW[] = {'W','S','H',0};
56 WCHAR scriptFullName[MAX_PATH];
57 
58 ITypeInfo *host_ti;
59 ITypeInfo *arguments_ti;
60 
61 static HRESULT query_interface(REFIID,void**);
62 
63 #ifdef __REACTOS__
64 #include <commctrl.h>
65 
66 typedef struct {
67     UINT itemsize, count;
68     void *mem;
69 } SIMPLEVECTOR;
70 
71 static void SVect_Free(SIMPLEVECTOR *pV)
72 {
73     if (pV->mem)
74         LocalFree(pV->mem);
75     pV->mem = NULL;
76 }
77 
78 static void* SVect_Add(SIMPLEVECTOR *pV)
79 {
80     void *p = NULL;
81     if (pV->mem)
82     {
83         p = LocalReAlloc(pV->mem, pV->itemsize * (pV->count + 1), LMEM_FIXED | LMEM_MOVEABLE);
84         if (p)
85         {
86             pV->mem = p;
87             p = (char*)p + (pV->count * pV->itemsize);
88             pV->count++;
89         }
90     }
91     else
92     {
93         p = pV->mem = LocalAlloc(LMEM_FIXED, pV->itemsize);
94         if (p)
95         {
96             pV->count = 1;
97         }
98     }
99     return p;
100 }
101 
102 #define SVect_Delete(pV, pItem) ( (pV), (pItem) ) /* Should not be required for global items */
103 
104 static void* SVect_Get(SIMPLEVECTOR *pV, UINT i)
105 {
106     return pV->mem && i < pV->count ? (char*)pV->mem + (i * pV->itemsize) : NULL;
107 }
108 
109 typedef struct {
110     BSTR name;
111     IUnknown *punk;
112 } GLOBAL_ITEM;
113 
114 SIMPLEVECTOR g_global_items = { sizeof(GLOBAL_ITEM) };
115 
116 static void free_globals(void)
117 {
118     UINT i;
119     for (i = 0;; ++i)
120     {
121         GLOBAL_ITEM *p = (GLOBAL_ITEM*)SVect_Get(&g_global_items, i);
122         if (!p)
123             break;
124         IUnknown_Release(p->punk);
125         SysFreeString(p->name);
126     }
127     SVect_Free(&g_global_items);
128 }
129 
130 static HRESULT add_globalitem(IActiveScript *script, BSTR name, IUnknown *punk, DWORD siflags)
131 {
132     GLOBAL_ITEM *item;
133     HRESULT hr;
134 
135     name = SysAllocString(name);
136     if (!name)
137         return E_OUTOFMEMORY;
138 
139     item = SVect_Add(&g_global_items);
140     if (item)
141     {
142         item->name = name;
143         item->punk = punk;
144         hr = IActiveScript_AddNamedItem(script, name, siflags);
145         if (SUCCEEDED(hr))
146         {
147             IUnknown_AddRef(punk);
148             return hr;
149         }
150         SVect_Delete(&g_global_items, item);
151     }
152     SysFreeString(name);
153     return E_OUTOFMEMORY;
154 }
155 
156 static HRESULT add_globalitem_from_clsid(IActiveScript *script, BSTR name, REFCLSID clsid, DWORD siflags)
157 {
158     IUnknown *punk;
159     HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&punk);
160     if (SUCCEEDED(hr))
161     {
162         hr = add_globalitem(script, name, punk, siflags);
163         IUnknown_Release(punk);
164     }
165     return hr;
166 }
167 
168 static HRESULT get_globalitem_info(LPCOLESTR Name, DWORD Mask, IUnknown **ppunk, ITypeInfo **ppti, BOOL *pHandled)
169 {
170     HRESULT hr = S_FALSE;
171     UINT i;
172     for (i = 0;; ++i)
173     {
174         GLOBAL_ITEM *p = (GLOBAL_ITEM*)SVect_Get(&g_global_items, i);
175         if (!p)
176             break;
177         if (!lstrcmpiW(Name, p->name))
178         {
179             if (ppti)
180                 *ppti = NULL;
181             if (Mask & SCRIPTINFO_IUNKNOWN)
182             {
183                 *ppunk = p->punk;
184                 if (p->punk)
185                 {
186                     IUnknown_AddRef(p->punk);
187                     *pHandled = TRUE;
188                 }
189                 return S_OK;
190             }
191             break;
192         }
193     }
194     return hr;
195 }
196 #endif
197 
198 static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface,
199                                                       REFIID riid, void **ppv)
200 {
201     return query_interface(riid, ppv);
202 }
203 
204 static ULONG WINAPI ActiveScriptSite_AddRef(IActiveScriptSite *iface)
205 {
206     return 2;
207 }
208 
209 static ULONG WINAPI ActiveScriptSite_Release(IActiveScriptSite *iface)
210 {
211     return 1;
212 }
213 
214 static HRESULT WINAPI ActiveScriptSite_GetLCID(IActiveScriptSite *iface, LCID *plcid)
215 {
216     WINE_TRACE("()\n");
217 
218     *plcid = GetUserDefaultLCID();
219     return S_OK;
220 }
221 
222 static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface,
223         LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppunkItem, ITypeInfo **ppti)
224 {
225     WINE_TRACE("(%s %x %p %p)\n", wine_dbgstr_w(pstrName), dwReturnMask, ppunkItem, ppti);
226 
227 #ifdef __REACTOS__
228     {
229         BOOL handled = FALSE;
230         HRESULT hr = get_globalitem_info(pstrName, dwReturnMask, ppunkItem, ppti, &handled);
231         if (handled)
232             return hr;
233     }
234 #endif
235 
236     if(lstrcmpW(pstrName, wshW) && lstrcmpW(pstrName, wscriptW))
237         return E_FAIL;
238 
239     if(dwReturnMask & SCRIPTINFO_ITYPEINFO) {
240         ITypeInfo_AddRef(host_ti);
241         *ppti = host_ti;
242     }
243 
244     if(dwReturnMask & SCRIPTINFO_IUNKNOWN) {
245         IHost_AddRef(&host_obj);
246         *ppunkItem = (IUnknown*)&host_obj;
247     }
248 
249     return S_OK;
250 }
251 
252 static HRESULT WINAPI ActiveScriptSite_GetDocVersionString(IActiveScriptSite *iface,
253         BSTR *pbstrVersion)
254 {
255     WINE_FIXME("()\n");
256     return E_NOTIMPL;
257 }
258 
259 static HRESULT WINAPI ActiveScriptSite_OnScriptTerminate(IActiveScriptSite *iface,
260         const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo)
261 {
262     WINE_FIXME("()\n");
263     return E_NOTIMPL;
264 }
265 
266 static HRESULT WINAPI ActiveScriptSite_OnStateChange(IActiveScriptSite *iface,
267         SCRIPTSTATE ssScriptState)
268 {
269     WINE_TRACE("(%x)\n", ssScriptState);
270     return S_OK;
271 }
272 
273 static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface,
274         IActiveScriptError *pscripterror)
275 {
276     WINE_FIXME("()\n");
277     return E_NOTIMPL;
278 }
279 
280 static HRESULT WINAPI ActiveScriptSite_OnEnterScript(IActiveScriptSite *iface)
281 {
282     WINE_TRACE("()\n");
283     return S_OK;
284 }
285 
286 static HRESULT WINAPI ActiveScriptSite_OnLeaveScript(IActiveScriptSite *iface)
287 {
288     WINE_TRACE("()\n");
289     return S_OK;
290 }
291 
292 static IActiveScriptSiteVtbl ActiveScriptSiteVtbl = {
293     ActiveScriptSite_QueryInterface,
294     ActiveScriptSite_AddRef,
295     ActiveScriptSite_Release,
296     ActiveScriptSite_GetLCID,
297     ActiveScriptSite_GetItemInfo,
298     ActiveScriptSite_GetDocVersionString,
299     ActiveScriptSite_OnScriptTerminate,
300     ActiveScriptSite_OnStateChange,
301     ActiveScriptSite_OnScriptError,
302     ActiveScriptSite_OnEnterScript,
303     ActiveScriptSite_OnLeaveScript
304 };
305 
306 static IActiveScriptSite script_site = { &ActiveScriptSiteVtbl };
307 
308 static HRESULT WINAPI ActiveScriptSiteWindow_QueryInterface(IActiveScriptSiteWindow *iface, REFIID riid, void **ppv)
309 {
310     return query_interface(riid, ppv);
311 }
312 
313 static ULONG WINAPI ActiveScriptSiteWindow_AddRef(IActiveScriptSiteWindow *iface)
314 {
315     return 2;
316 }
317 
318 static ULONG WINAPI ActiveScriptSiteWindow_Release(IActiveScriptSiteWindow *iface)
319 {
320     return 1;
321 }
322 
323 static HRESULT WINAPI ActiveScriptSiteWindow_GetWindow(IActiveScriptSiteWindow *iface, HWND *phwnd)
324 {
325     TRACE("(%p)\n", phwnd);
326 
327     *phwnd = NULL;
328     return S_OK;
329 }
330 
331 static HRESULT WINAPI ActiveScriptSiteWindow_EnableModeless(IActiveScriptSiteWindow *iface, BOOL fEnable)
332 {
333     TRACE("(%x)\n", fEnable);
334     return S_OK;
335 }
336 
337 static const IActiveScriptSiteWindowVtbl ActiveScriptSiteWindowVtbl = {
338     ActiveScriptSiteWindow_QueryInterface,
339     ActiveScriptSiteWindow_AddRef,
340     ActiveScriptSiteWindow_Release,
341     ActiveScriptSiteWindow_GetWindow,
342     ActiveScriptSiteWindow_EnableModeless
343 };
344 
345 static IActiveScriptSiteWindow script_site_window = { &ActiveScriptSiteWindowVtbl };
346 
347 static HRESULT query_interface(REFIID riid, void **ppv)
348 {
349     if(IsEqualGUID(riid, &IID_IUnknown)) {
350         TRACE("(IID_IUnknown %p)\n", ppv);
351         *ppv = &script_site;
352     }else if(IsEqualGUID(riid, &IID_IActiveScriptSite)) {
353         TRACE("(IID_IActiveScriptSite %p)\n", ppv);
354         *ppv = &script_site;
355     }else if(IsEqualGUID(riid, &IID_IActiveScriptSiteWindow)) {
356         TRACE("(IID_IActiveScriptSiteWindow %p)\n", ppv);
357         *ppv = &script_site_window;
358     }else {
359         *ppv = NULL;
360         TRACE("(%s %p)\n", wine_dbgstr_guid(riid), ppv);
361         return E_NOINTERFACE;
362     }
363 
364     IUnknown_AddRef((IUnknown*)*ppv);
365     return S_OK;
366 }
367 
368 static BOOL load_typelib(void)
369 {
370     ITypeLib *typelib;
371     HRESULT hres;
372 
373     static const WCHAR wscript_exeW[] = {'w','s','c','r','i','p','t','.','e','x','e',0};
374 
375     hres = LoadTypeLib(wscript_exeW, &typelib);
376     if(FAILED(hres))
377         return FALSE;
378 
379     hres = ITypeLib_GetTypeInfoOfGuid(typelib, &IID_IHost, &host_ti);
380     if(SUCCEEDED(hres))
381         hres = ITypeLib_GetTypeInfoOfGuid(typelib, &IID_IArguments2, &arguments_ti);
382 
383     ITypeLib_Release(typelib);
384     return SUCCEEDED(hres);
385 }
386 
387 static BOOL get_engine_clsid(const WCHAR *ext, CLSID *clsid)
388 {
389     WCHAR fileid[64], progid[64];
390     DWORD res;
391     LONG size;
392     HKEY hkey;
393     HRESULT hres;
394 
395     static const WCHAR script_engineW[] =
396         {'\\','S','c','r','i','p','t','E','n','g','i','n','e',0};
397 
398     res = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey);
399     if(res != ERROR_SUCCESS)
400         return FALSE;
401 
402     size = ARRAY_SIZE(fileid);
403     res = RegQueryValueW(hkey, NULL, fileid, &size);
404     RegCloseKey(hkey);
405     if(res != ERROR_SUCCESS)
406         return FALSE;
407 
408     WINE_TRACE("fileid is %s\n", wine_dbgstr_w(fileid));
409 
410     lstrcatW(fileid, script_engineW);
411     res = RegOpenKeyW(HKEY_CLASSES_ROOT, fileid, &hkey);
412     if(res != ERROR_SUCCESS)
413         return FALSE;
414 
415     size = ARRAY_SIZE(progid);
416     res = RegQueryValueW(hkey, NULL, progid, &size);
417     RegCloseKey(hkey);
418     if(res != ERROR_SUCCESS)
419         return FALSE;
420 
421     WINE_TRACE("ProgID is %s\n", wine_dbgstr_w(progid));
422 
423     hres = CLSIDFromProgID(progid, clsid);
424     return SUCCEEDED(hres);
425 }
426 
427 static BOOL create_engine(CLSID *clsid, IActiveScript **script_ret,
428         IActiveScriptParse **parser)
429 {
430     IActiveScript *script;
431     IUnknown *unk;
432     HRESULT hres;
433 
434     hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
435             &IID_IUnknown, (void**)&unk);
436     if(FAILED(hres))
437         return FALSE;
438 
439     hres = IUnknown_QueryInterface(unk, &IID_IActiveScript, (void**)&script);
440     IUnknown_Release(unk);
441     if(FAILED(hres))
442         return FALSE;
443 
444     hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)parser);
445     if(FAILED(hres)) {
446         IActiveScript_Release(script);
447         return FALSE;
448     }
449 
450     *script_ret = script;
451     return TRUE;
452 }
453 
454 static BOOL init_engine(IActiveScript *script, IActiveScriptParse *parser)
455 {
456     HRESULT hres;
457 
458     if(!load_typelib())
459         return FALSE;
460 
461     hres = IActiveScript_SetScriptSite(script, &script_site);
462     if(FAILED(hres))
463         return FALSE;
464 
465     hres = IActiveScriptParse_InitNew(parser);
466     if(FAILED(hres))
467         return FALSE;
468 
469     hres = IActiveScript_AddNamedItem(script, wscriptW, SCRIPTITEM_ISVISIBLE);
470     if(FAILED(hres))
471         return FALSE;
472 
473     hres = IActiveScript_AddNamedItem(script, wshW, SCRIPTITEM_ISVISIBLE);
474     if(FAILED(hres))
475         return FALSE;
476 
477     hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_INITIALIZED);
478     return SUCCEEDED(hres);
479 }
480 
481 static BSTR get_script_str(const WCHAR *filename)
482 {
483     const char *file_map;
484     HANDLE file, map;
485     DWORD size, len;
486     BSTR ret;
487 
488     file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
489     if(file == INVALID_HANDLE_VALUE)
490         return NULL;
491 
492     size = GetFileSize(file, NULL);
493     map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
494     CloseHandle(file);
495     if(map == INVALID_HANDLE_VALUE)
496         return NULL;
497 
498     file_map = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
499     CloseHandle(map);
500     if(!file_map)
501         return NULL;
502 
503     len = MultiByteToWideChar(CP_ACP, 0, file_map, size, NULL, 0);
504     ret = SysAllocStringLen(NULL, len);
505     MultiByteToWideChar(CP_ACP, 0, file_map, size, ret, len);
506 
507     UnmapViewOfFile(file_map);
508     return ret;
509 }
510 
511 static void run_script(const WCHAR *filename, IActiveScript *script, IActiveScriptParse *parser)
512 {
513     BSTR text;
514     HRESULT hres;
515 
516     text = get_script_str(filename);
517     if(!text) {
518         WINE_FIXME("Could not get script text\n");
519         return;
520     }
521 
522     hres = IActiveScriptParse_ParseScriptText(parser, text, NULL, NULL, NULL, 1, 1,
523             SCRIPTTEXT_HOSTMANAGESSOURCE|SCRIPTITEM_ISVISIBLE, NULL, NULL);
524     SysFreeString(text);
525     if(FAILED(hres)) {
526         WINE_FIXME("ParseScriptText failed: %08x\n", hres);
527         return;
528     }
529 
530     hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_STARTED);
531     if(FAILED(hres))
532         WINE_FIXME("SetScriptState failed: %08x\n", hres);
533 }
534 
535 #ifdef __REACTOS__
536 #include <msxml2.h>
537 #include <shlwapi.h>
538 
539 static HRESULT xmldomnode_getattributevalue(IXMLDOMNode *pnode, LPCWSTR name, BSTR *pout)
540 {
541     IXMLDOMNamedNodeMap *pmap;
542     HRESULT hr = E_OUTOFMEMORY;
543     BSTR bsname = SysAllocString(name);
544     *pout = NULL;
545     if (bsname && SUCCEEDED(hr = IXMLDOMNode_get_attributes(pnode, &pmap)))
546     {
547         if (SUCCEEDED(hr = IXMLDOMNamedNodeMap_getNamedItem(pmap, bsname, &pnode)))
548         {
549             hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
550             if (pnode)
551             {
552                 hr = IXMLDOMNode_get_text(pnode, pout);
553                 if (SUCCEEDED(hr) && !*pout)
554                     hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
555                 IXMLDOMNode_Release(pnode);
556             }
557         }
558         IXMLDOMNamedNodeMap_Release(pmap);
559     }
560     SysFreeString(bsname);
561     return hr;
562 }
563 
564 static HRESULT xmldomelem_getelembytag(IXMLDOMElement *pelem, LPCWSTR name, long index, IXMLDOMNode**ppout)
565 {
566     HRESULT hr = E_OUTOFMEMORY;
567     IXMLDOMNodeList *pnl;
568     BSTR bsname = SysAllocString(name);
569     *ppout = NULL;
570     if (bsname && SUCCEEDED(hr = IXMLDOMElement_getElementsByTagName(pelem, bsname, &pnl)))
571     {
572         hr = IXMLDOMNodeList_get_item(pnl, index, ppout);
573         if (SUCCEEDED(hr) && !*ppout)
574             hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
575         IUnknown_Release(pnl);
576     }
577     SysFreeString(bsname);
578     return hr;
579 }
580 
581 static HRESULT xmldomelem_getelembytagasdomelem(IXMLDOMElement *pelem, LPCWSTR name, long index, IXMLDOMElement**ppout)
582 {
583     IXMLDOMNode *pnode;
584     HRESULT hr = xmldomelem_getelembytag(pelem, name, index, &pnode);
585     *ppout = NULL;
586     if (SUCCEEDED(hr))
587     {
588         hr = IUnknown_QueryInterface(pnode, &IID_IXMLDOMElement, (void**)ppout);
589         IUnknown_Release(pnode);
590     }
591     return hr;
592 }
593 
594 static void wsf_addobjectfromnode(IActiveScript *script, IXMLDOMNode *obj)
595 {
596     BSTR bsid, bsclsid = NULL;
597     if (SUCCEEDED(xmldomnode_getattributevalue(obj, L"id", &bsid)))
598     {
599         CLSID clsid;
600         HRESULT hr;
601         hr = xmldomnode_getattributevalue(obj, L"clsid", &bsclsid);
602         if (FAILED(hr) || FAILED(CLSIDFromString(bsclsid, &clsid)))
603         {
604             SysFreeString(bsclsid);
605             if (SUCCEEDED(hr = xmldomnode_getattributevalue(obj, L"progid", &bsclsid)))
606             {
607                 hr = CLSIDFromProgID(bsclsid, &clsid);
608                 SysFreeString(bsclsid);
609             }
610         }
611         if (SUCCEEDED(hr))
612         {
613             hr = add_globalitem_from_clsid(script, bsid, &clsid, SCRIPTITEM_ISVISIBLE);
614         }
615         SysFreeString(bsid);
616     }
617 }
618 
619 static HRESULT run_wsfjob(IXMLDOMElement *jobtag)
620 {
621     // FIXME: We are supposed to somehow handle multiple languages in the same IActiveScript.
622     IActiveScript *script;
623     LPCWSTR deflang = L"JScript";
624     IXMLDOMNode *scripttag;
625     HRESULT hr = S_OK;
626     if (SUCCEEDED(xmldomelem_getelembytag(jobtag, L"script", 0, &scripttag)))
627     {
628         CLSID clsid;
629         IActiveScriptParse *parser;
630         BSTR lang, code;
631         if (FAILED(xmldomnode_getattributevalue(scripttag, L"language", &lang)))
632             lang = NULL;
633         hr = CLSIDFromProgID(lang ? lang : deflang, &clsid);
634         SysFreeString(lang);
635 
636         if (SUCCEEDED(hr))
637         {
638             hr = E_FAIL;
639             if (create_engine(&clsid, &script, &parser))
640             {
641                 if (init_engine(script, parser))
642                 {
643                     long index;
644                     for (index = 0; index < 0x7fffffff; ++index)
645                     {
646                         IXMLDOMNode *obj;
647                         if (SUCCEEDED(xmldomelem_getelembytag(jobtag, L"object", index, &obj)))
648                         {
649                             wsf_addobjectfromnode(script, obj);
650                             IUnknown_Release(obj);
651                         }
652                         else
653                         {
654                             break;
655                         }
656                     }
657 
658                     if (SUCCEEDED(hr = IXMLDOMNode_get_text(scripttag, &code)))
659                     {
660                         hr = IActiveScriptParse_ParseScriptText(parser, code, NULL, NULL, NULL, 1, 1,
661                                                                 SCRIPTTEXT_HOSTMANAGESSOURCE|SCRIPTITEM_ISVISIBLE,
662                                                                 NULL, NULL);
663                         if (SUCCEEDED(hr))
664                         {
665                             hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_STARTED);
666                             IActiveScript_Close(script);
667                         }
668                         SysFreeString(code);
669                     }
670                     ITypeInfo_Release(host_ti);
671                 }
672                 IUnknown_Release(parser);
673                 IUnknown_Release(script);
674             }
675         }
676         IUnknown_Release(scripttag);
677     }
678     return hr;
679 }
680 
681 /*
682 .WSF files can contain a single job, or multiple jobs if contained in a package.
683 Jobs are identified by their id and if no id is specified, the first job is used.
684 Each job can contain multiple script tags and all scripts are merged into one.
685 
686 <job><script language="JScript">WScript.Echo("JS");</script></job>
687 or
688 <package>
689 <job><script language="JScript">WScript.Echo("JS");</script></job>
690 </package>
691 or
692 <?xml version="1.0" ?>
693 <job>
694 <script language="JScript"><![CDATA[function JS(s) {WScript.Echo(s)}]]></script>
695 <script language="VBScript">JS "VB2JS"</script>
696 </job>
697 */
698 static HRESULT run_wsf(LPCWSTR xmlpath)
699 {
700     WCHAR url[ARRAY_SIZE("file://") + max(ARRAY_SIZE(scriptFullName), MAX_PATH)];
701     DWORD cch = ARRAY_SIZE(url);
702     IXMLDOMDocument *pdoc;
703     HRESULT hr = UrlCreateFromPathW(xmlpath, url, &cch, 0), hrCom;
704     if (FAILED(hr))
705         return hr;
706 
707     hrCom = CoInitialize(NULL);
708     hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER,
709                           &IID_IXMLDOMDocument, (void**)&pdoc);
710     if (SUCCEEDED(hr))
711     {
712         VARIANT_BOOL succ = VARIANT_FALSE;
713         IXMLDOMElement *pdocelm;
714         BSTR bsurl = SysAllocString(url);
715         VARIANT v;
716         V_VT(&v) = VT_BSTR;
717         V_BSTR(&v) = bsurl;
718         if (!bsurl || (hr = IXMLDOMDocument_load(pdoc, v, &succ)) > 0 || (SUCCEEDED(hr) && !succ))
719         {
720             hr = E_FAIL;
721         }
722         if (SUCCEEDED(hr) && SUCCEEDED(hr = IXMLDOMDocument_get_documentElement(pdoc, &pdocelm)))
723         {
724             BSTR tagName = NULL;
725             if (SUCCEEDED(hr = IXMLDOMElement_get_tagName(pdocelm, &tagName)))
726             {
727                 if (lstrcmpiW(tagName, L"package") == 0)
728                 {
729                     // FIXME: Accept job id as a function parameter and find the job here
730                     IXMLDOMElement *p;
731                     if (SUCCEEDED(hr = xmldomelem_getelembytagasdomelem(pdocelm, L"job", 0, &p)))
732                     {
733                         IUnknown_Release(pdocelm);
734                         pdocelm = p;
735                     }
736                 }
737                 else if (lstrcmpiW(tagName, L"job") != 0)
738                 {
739                     hr = 0x800400C0ul;
740                 }
741                 SysFreeString(tagName);
742             }
743             if (SUCCEEDED(hr))
744             {
745                 // FIXME: Only support CDATA blocks if the xml tag is present?
746                 hr = run_wsfjob(pdocelm);
747             }
748             IUnknown_Release(pdocelm);
749         }
750         VariantClear(&v);
751         IUnknown_Release(pdoc);
752     }
753     free_globals();
754     if (SUCCEEDED(hrCom))
755         CoUninitialize();
756     return hr;
757 }
758 #endif
759 
760 static BOOL set_host_properties(const WCHAR *prop)
761 {
762     static const WCHAR nologoW[] = {'n','o','l','o','g','o',0};
763     static const WCHAR iactive[] = {'i',0};
764     static const WCHAR batch[] = {'b',0};
765 
766     if(*prop == '/') {
767         ++prop;
768         if(*prop == '/')
769             ++prop;
770     }
771     else
772         ++prop;
773 
774     if(wcsicmp(prop, iactive) == 0)
775         wshInteractive = VARIANT_TRUE;
776     else if(wcsicmp(prop, batch) == 0)
777         wshInteractive = VARIANT_FALSE;
778     else if(wcsicmp(prop, nologoW) == 0)
779         WINE_FIXME("ignored %s switch\n", debugstr_w(nologoW));
780     else
781     {
782         WINE_FIXME("unsupported switch %s\n", debugstr_w(prop));
783         return FALSE;
784     }
785     return TRUE;
786 }
787 
788 int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cmdshow)
789 {
790     WCHAR *ext, *filepart, *filename = NULL;
791     IActiveScriptParse *parser;
792     IActiveScript *script;
793     WCHAR **argv;
794     CLSID clsid;
795     int argc, i;
796     DWORD res;
797 
798     WINE_TRACE("(%p %p %s %x)\n", hInst, hPrevInst, wine_dbgstr_w(cmdline), cmdshow);
799 
800     argv = CommandLineToArgvW(GetCommandLineW(), &argc);
801     if(!argv)
802         return 1;
803 
804     for(i=1; i<argc; i++) {
805         if(*argv[i] == '/' || *argv[i] == '-') {
806             if(!set_host_properties(argv[i]))
807                 return 1;
808         }else {
809             filename = argv[i];
810             argums = argv+i+1;
811             numOfArgs = argc-i-1;
812             break;
813         }
814     }
815 
816     if(!filename) {
817         WINE_FIXME("No file name specified\n");
818         return 1;
819     }
820     res = GetFullPathNameW(filename, ARRAY_SIZE(scriptFullName), scriptFullName, &filepart);
821     if(!res || res > ARRAY_SIZE(scriptFullName))
822         return 1;
823 
824     ext = wcsrchr(filepart, '.');
825 #ifdef __REACTOS__
826     if (ext && !lstrcmpiW(ext, L".wsf")) {
827         return run_wsf(scriptFullName);
828     }
829 #endif
830     if(!ext || !get_engine_clsid(ext, &clsid)) {
831         WINE_FIXME("Could not find engine for %s\n", wine_dbgstr_w(ext));
832         return 1;
833     }
834 
835     CoInitialize(NULL);
836 
837     if(!create_engine(&clsid, &script, &parser)) {
838         WINE_FIXME("Could not create script engine\n");
839         CoUninitialize();
840         return 1;
841     }
842 
843     if(init_engine(script, parser)) {
844         run_script(filename, script, parser);
845         IActiveScript_Close(script);
846         ITypeInfo_Release(host_ti);
847     }else {
848         WINE_FIXME("Script initialization failed\n");
849     }
850 
851     IActiveScript_Release(script);
852     IActiveScriptParse_Release(parser);
853 
854 #ifdef __REACTOS__
855     free_globals();
856 #endif
857 
858     CoUninitialize();
859 
860     return 0;
861 }
862