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 #define CONST_VTABLE
23 
24 #include <windef.h>
25 #include <winbase.h>
26 #include <ole2.h>
27 
28 #include "wscript.h"
29 
30 #include <wine/debug.h>
31 #include <wine/heap.h>
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(wscript);
34 
35 #define BUILDVERSION 16535
36 
37 static const WCHAR wshNameW[] = {'W','i','n','d','o','w','s',' ','S','c','r','i','p','t',' ','H','o','s','t',0};
38 static const WCHAR wshVersionW[] = {'5','.','8'};
39 
40 VARIANT_BOOL wshInteractive =
41 #ifndef CSCRIPT_BUILD
42     VARIANT_TRUE;
43 #else
44     VARIANT_FALSE;
45 #endif
46 
47 static HRESULT to_string(VARIANT *src, BSTR *dst)
48 {
49     VARIANT v;
50     HRESULT hres;
51 
52     static const WCHAR nullW[] = {'n','u','l','l',0};
53 
54     if(V_VT(src) == VT_NULL) {
55         *dst = SysAllocString(nullW);
56         return *dst ? S_OK : E_OUTOFMEMORY;
57     }
58 
59     V_VT(&v) = VT_EMPTY;
60     hres = VariantChangeType(&v, src, 0, VT_BSTR);
61     if(FAILED(hres)) {
62         WARN("Could not convert argument %s to string\n", debugstr_variant(src));
63         return hres;
64     }
65 
66     *dst = V_BSTR(&v);
67     return S_OK;
68 }
69 
70 static void print_string(const WCHAR *string)
71 {
72     DWORD count, ret, len, lena;
73     char *buf;
74 
75     if(wshInteractive) {
76         static const WCHAR windows_script_hostW[] =
77             {'W','i','n','d','o','w','s',' ','S','c','r','i','p','t',' ','H','o','s','t',0};
78         MessageBoxW(NULL, string, windows_script_hostW, MB_OK);
79         return;
80     }
81 
82     len = lstrlenW(string);
83     ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), string, len, &count, NULL);
84     if(ret) {
85         static const WCHAR crnlW[] = {'\r','\n'};
86         WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), crnlW, ARRAY_SIZE(crnlW), &count, NULL);
87         return;
88     }
89 
90     lena = WideCharToMultiByte(GetConsoleOutputCP(), 0, string, len, NULL, 0, NULL, NULL);
91     buf = heap_alloc(len);
92     if(!buf)
93         return;
94 
95     WideCharToMultiByte(GetConsoleOutputCP(), 0, string, len, buf, lena, NULL, NULL);
96     WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, lena, &count, FALSE);
97     heap_free(buf);
98     WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &count, FALSE);
99 }
100 
101 static HRESULT WINAPI Host_QueryInterface(IHost *iface, REFIID riid, void **ppv)
102 {
103     WINE_TRACE("(%s %p)\n", wine_dbgstr_guid(riid), ppv);
104 
105     if(IsEqualGUID(&IID_IUnknown, riid)
106        || IsEqualGUID(&IID_IDispatch, riid)
107        || IsEqualGUID(&IID_IHost, riid)) {
108         *ppv = iface;
109         return S_OK;
110     }
111 
112     *ppv = NULL;
113     return E_NOINTERFACE;
114 }
115 
116 static ULONG WINAPI Host_AddRef(IHost *iface)
117 {
118     return 2;
119 }
120 
121 static ULONG WINAPI Host_Release(IHost *iface)
122 {
123     return 1;
124 }
125 
126 static HRESULT WINAPI Host_GetTypeInfoCount(IHost *iface, UINT *pctinfo)
127 {
128     WINE_TRACE("(%p)\n", pctinfo);
129     *pctinfo = 1;
130     return S_OK;
131 }
132 
133 static HRESULT WINAPI Host_GetTypeInfo(IHost *iface, UINT iTInfo, LCID lcid,
134         ITypeInfo **ppTInfo)
135 {
136     WINE_TRACE("(%x %x %p\n", iTInfo, lcid, ppTInfo);
137 
138     ITypeInfo_AddRef(host_ti);
139     *ppTInfo = host_ti;
140     return S_OK;
141 }
142 
143 static HRESULT WINAPI Host_GetIDsOfNames(IHost *iface, REFIID riid, LPOLESTR *rgszNames,
144         UINT cNames, LCID lcid, DISPID *rgDispId)
145 {
146     WINE_TRACE("(%s %p %d %x %p)\n", wine_dbgstr_guid(riid), rgszNames,
147         cNames, lcid, rgDispId);
148 
149     return ITypeInfo_GetIDsOfNames(host_ti, rgszNames, cNames, rgDispId);
150 }
151 
152 static HRESULT WINAPI Host_Invoke(IHost *iface, DISPID dispIdMember, REFIID riid,
153         LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
154         EXCEPINFO *pExcepInfo, UINT *puArgErr)
155 {
156     WINE_TRACE("(%d %p %p)\n", dispIdMember, pDispParams, pVarResult);
157 
158     return ITypeInfo_Invoke(host_ti, iface, dispIdMember, wFlags, pDispParams,
159             pVarResult, pExcepInfo, puArgErr);
160 }
161 
162 static HRESULT WINAPI Host_get_Name(IHost *iface, BSTR *out_Name)
163 {
164     WINE_TRACE("(%p)\n", out_Name);
165 
166     if(!(*out_Name = SysAllocString(wshNameW)))
167 	return E_OUTOFMEMORY;
168     return S_OK;
169 }
170 
171 static HRESULT WINAPI Host_get_Application(IHost *iface, IDispatch **out_Dispatch)
172 {
173     WINE_TRACE("(%p)\n", out_Dispatch);
174 
175     *out_Dispatch = (IDispatch*)&host_obj;
176     return S_OK;
177 }
178 
179 static HRESULT WINAPI Host_get_FullName(IHost *iface, BSTR *out_Path)
180 {
181     WCHAR fullPath[MAX_PATH];
182 
183     WINE_TRACE("(%p)\n", out_Path);
184 
185     if(GetModuleFileNameW(NULL, fullPath, ARRAY_SIZE(fullPath)) == 0)
186         return E_FAIL;
187     if(!(*out_Path = SysAllocString(fullPath)))
188         return E_OUTOFMEMORY;
189     return S_OK;
190 }
191 
192 static HRESULT WINAPI Host_get_Path(IHost *iface, BSTR *out_Path)
193 {
194     WCHAR path[MAX_PATH];
195     int howMany;
196     WCHAR *pos;
197 
198     WINE_TRACE("(%p)\n", out_Path);
199 
200     if(GetModuleFileNameW(NULL, path, ARRAY_SIZE(path)) == 0)
201         return E_FAIL;
202     pos = wcsrchr(path, '\\');
203     howMany = pos - path;
204     if(!(*out_Path = SysAllocStringLen(path, howMany)))
205         return E_OUTOFMEMORY;
206     return S_OK;
207 }
208 
209 static HRESULT WINAPI Host_get_Interactive(IHost *iface, VARIANT_BOOL *out_Interactive)
210 {
211     WINE_TRACE("(%p)\n", out_Interactive);
212 
213     *out_Interactive = wshInteractive;
214     return S_OK;
215 }
216 
217 static HRESULT WINAPI Host_put_Interactive(IHost *iface, VARIANT_BOOL v)
218 {
219     WINE_TRACE("(%x)\n", v);
220 
221     wshInteractive = v;
222     return S_OK;
223 }
224 
225 static HRESULT WINAPI Host_Quit(IHost *iface, int ExitCode)
226 {
227     FIXME("(%d) semi-stub: no script engine clean up\n", ExitCode);
228 
229     ExitProcess(ExitCode);
230     return S_OK;
231 }
232 
233 static HRESULT WINAPI Host_get_ScriptName(IHost *iface, BSTR *out_ScriptName)
234 {
235     WCHAR *scriptName;
236 
237     WINE_TRACE("(%p)\n", out_ScriptName);
238 
239     scriptName = wcsrchr(scriptFullName, '\\');
240     ++scriptName;
241     if(!(*out_ScriptName = SysAllocString(scriptName)))
242         return E_OUTOFMEMORY;
243     return S_OK;
244 }
245 
246 static HRESULT WINAPI Host_get_ScriptFullName(IHost *iface, BSTR *out_ScriptFullName)
247 {
248     WINE_TRACE("(%p)\n", out_ScriptFullName);
249 
250     if(!(*out_ScriptFullName = SysAllocString(scriptFullName)))
251         return E_OUTOFMEMORY;
252     return S_OK;
253 }
254 
255 static HRESULT WINAPI Host_get_Arguments(IHost *iface, IArguments2 **out_Arguments)
256 {
257     WINE_TRACE("(%p)\n", out_Arguments);
258 
259     *out_Arguments = &arguments_obj;
260     return S_OK;
261 }
262 
263 static HRESULT WINAPI Host_get_Version(IHost *iface, BSTR *out_Version)
264 {
265     WINE_TRACE("(%p)\n", out_Version);
266 
267     if(!(*out_Version = SysAllocString(wshVersionW)))
268 	return E_OUTOFMEMORY;
269     return S_OK;
270 }
271 
272 static HRESULT WINAPI Host_get_BuildVersion(IHost *iface, int *out_Build)
273 {
274     WINE_TRACE("(%p)\n", out_Build);
275 
276     *out_Build = BUILDVERSION;
277     return S_OK;
278 }
279 
280 static HRESULT WINAPI Host_get_Timeout(IHost *iface, LONG *out_Timeout)
281 {
282     WINE_FIXME("(%p)\n", out_Timeout);
283     return E_NOTIMPL;
284 }
285 
286 static HRESULT WINAPI Host_put_Timeout(IHost *iface, LONG v)
287 {
288     WINE_FIXME("(%d)\n", v);
289     return E_NOTIMPL;
290 }
291 
292 static HRESULT WINAPI Host_CreateObject(IHost *iface, BSTR ProgID, BSTR Prefix,
293         IDispatch **out_Dispatch)
294 {
295     IUnknown *unk;
296     GUID guid;
297     HRESULT hres;
298 
299     TRACE("(%s %s %p)\n", wine_dbgstr_w(ProgID), wine_dbgstr_w(Prefix), out_Dispatch);
300 
301     if(Prefix && *Prefix) {
302         FIXME("Prefix %s not supported\n", debugstr_w(Prefix));
303         return E_NOTIMPL;
304     }
305 
306     hres = CLSIDFromProgID(ProgID, &guid);
307     if(FAILED(hres))
308         return hres;
309 
310     hres = CoCreateInstance(&guid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER,
311             &IID_IUnknown, (void**)&unk);
312     if(FAILED(hres))
313         return hres;
314 
315     hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)out_Dispatch);
316     IUnknown_Release(unk);
317     return hres;
318 }
319 
320 static HRESULT WINAPI Host_Echo(IHost *iface, SAFEARRAY *args)
321 {
322     WCHAR *output = NULL, *ptr;
323     unsigned argc, i, len;
324 #ifdef __REACTOS__
325     LONG ubound, lbound;
326 #else
327     int ubound, lbound;
328 #endif
329     VARIANT *argv;
330     BSTR *strs;
331     HRESULT hres;
332 
333     TRACE("(%p)\n", args);
334 
335     if(SafeArrayGetDim(args) != 1) {
336         FIXME("Unsupported args dim %d\n", SafeArrayGetDim(args));
337         return E_NOTIMPL;
338     }
339 
340     SafeArrayGetLBound(args, 1, &lbound);
341     SafeArrayGetUBound(args, 1, &ubound);
342 
343     hres = SafeArrayAccessData(args, (void**)&argv);
344     if(FAILED(hres))
345         return hres;
346 
347     argc = ubound-lbound+1;
348     strs = heap_alloc_zero(argc*sizeof(*strs));
349     if(!strs) {
350         SafeArrayUnaccessData(args);
351         return E_OUTOFMEMORY;
352     }
353 
354     /* Len of spaces between arguments. */
355     len = argc-1;
356 
357     for(i=0; i < argc; i++) {
358         hres = to_string(argv+i, strs+i);
359         if(FAILED(hres))
360             break;
361 
362         len += SysStringLen(strs[i]);
363     }
364 
365     SafeArrayUnaccessData(args);
366     if(SUCCEEDED(hres)) {
367         ptr = output = heap_alloc((len+1)*sizeof(WCHAR));
368         if(output) {
369             for(i=0; i < argc; i++) {
370                 if(i)
371                     *ptr++ = ' ';
372                 len = SysStringLen(strs[i]);
373                 memcpy(ptr, strs[i], len*sizeof(WCHAR));
374                 ptr += len;
375             }
376             *ptr = 0;
377         }else {
378             hres = E_OUTOFMEMORY;
379         }
380     }
381 
382     for(i=0; i < argc; i++)
383         SysFreeString(strs[i]);
384     heap_free(strs);
385     if(FAILED(hres))
386         return hres;
387 
388     print_string(output);
389 
390     heap_free(output);
391     return S_OK;
392 }
393 
394 static HRESULT WINAPI Host_GetObject(IHost *iface, BSTR Pathname, BSTR ProgID,
395         BSTR Prefix, IDispatch **out_Dispatch)
396 {
397     WINE_FIXME("(%s %s %s %p)\n", wine_dbgstr_w(Pathname), wine_dbgstr_w(ProgID),
398         wine_dbgstr_w(Prefix), out_Dispatch);
399     return E_NOTIMPL;
400 }
401 
402 static HRESULT WINAPI Host_DisconnectObject(IHost *iface, IDispatch *Object)
403 {
404     WINE_FIXME("(%p)\n", Object);
405     return E_NOTIMPL;
406 }
407 
408 static HRESULT WINAPI Host_Sleep(IHost *iface, LONG Time)
409 {
410     WINE_FIXME("(%d)\n", Time);
411     return E_NOTIMPL;
412 }
413 
414 static HRESULT WINAPI Host_ConnectObject(IHost *iface, IDispatch *Object, BSTR Prefix)
415 {
416     WINE_FIXME("(%p %s)\n", Object, wine_dbgstr_w(Prefix));
417     return E_NOTIMPL;
418 }
419 
420 static HRESULT WINAPI Host_get_StdIn(IHost *iface, ITextStream **ppts)
421 {
422     WINE_FIXME("(%p)\n", ppts);
423     return E_NOTIMPL;
424 }
425 
426 static HRESULT WINAPI Host_get_StdOut(IHost *iface, ITextStream **ppts)
427 {
428     WINE_FIXME("(%p)\n", ppts);
429     return E_NOTIMPL;
430 }
431 
432 static HRESULT WINAPI Host_get_StdErr(IHost *iface, ITextStream **ppts)
433 {
434     WINE_FIXME("(%p)\n", ppts);
435     return E_NOTIMPL;
436 }
437 
438 static const IHostVtbl HostVtbl = {
439     Host_QueryInterface,
440     Host_AddRef,
441     Host_Release,
442     Host_GetTypeInfoCount,
443     Host_GetTypeInfo,
444     Host_GetIDsOfNames,
445     Host_Invoke,
446     Host_get_Name,
447     Host_get_Application,
448     Host_get_FullName,
449     Host_get_Path,
450     Host_get_Interactive,
451     Host_put_Interactive,
452     Host_Quit,
453     Host_get_ScriptName,
454     Host_get_ScriptFullName,
455     Host_get_Arguments,
456     Host_get_Version,
457     Host_get_BuildVersion,
458     Host_get_Timeout,
459     Host_put_Timeout,
460     Host_CreateObject,
461     Host_Echo,
462     Host_GetObject,
463     Host_DisconnectObject,
464     Host_Sleep,
465     Host_ConnectObject,
466     Host_get_StdIn,
467     Host_get_StdOut,
468     Host_get_StdErr
469 };
470 
471 IHost host_obj = { &HostVtbl };
472