1 /*
2  * Copyright 2012 Detlef Riekenberg
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 #define COBJMACROS
20 
21 #include "initguid.h"
22 #include "taskschd.h"
23 
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26 
27 WINE_DEFAULT_DEBUG_CHANNEL(schtasks);
28 
29 static const WCHAR change_optW[] = {'/','c','h','a','n','g','e',0};
30 static const WCHAR create_optW[] = {'/','c','r','e','a','t','e',0};
31 static const WCHAR delete_optW[] = {'/','d','e','l','e','t','e',0};
32 static const WCHAR enable_optW[] = {'/','e','n','a','b','l','e',0};
33 static const WCHAR f_optW[] = {'/','f',0};
34 static const WCHAR ru_optW[] = {'/','r','u',0};
35 static const WCHAR tn_optW[] = {'/','t','n',0};
36 static const WCHAR tr_optW[] = {'/','t','r',0};
37 static const WCHAR xml_optW[] = {'/','x','m','l',0};
38 
39 static ITaskFolder *get_tasks_root_folder(void)
40 {
41     ITaskService *service;
42     ITaskFolder *root;
43     VARIANT empty;
44     HRESULT hres;
45 
46     hres = CoCreateInstance(&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER,
47                             &IID_ITaskService, (void**)&service);
48     if (FAILED(hres))
49         return NULL;
50 
51     V_VT(&empty) = VT_EMPTY;
52     hres = ITaskService_Connect(service, empty, empty, empty, empty);
53     if (FAILED(hres)) {
54         FIXME("Connect failed: %08x\n", hres);
55         return NULL;
56     }
57 
58     hres = ITaskService_GetFolder(service, NULL, &root);
59     ITaskService_Release(service);
60     if (FAILED(hres)) {
61         FIXME("GetFolder failed: %08x\n", hres);
62         return NULL;
63     }
64 
65     return root;
66 }
67 
68 static IRegisteredTask *get_registered_task(const WCHAR *name)
69 {
70     IRegisteredTask *registered_task;
71     ITaskFolder *root;
72     BSTR str;
73     HRESULT hres;
74 
75     root = get_tasks_root_folder();
76     if (!root)
77         return NULL;
78 
79     str = SysAllocString(name);
80     hres = ITaskFolder_GetTask(root, str, &registered_task);
81     SysFreeString(str);
82     ITaskFolder_Release(root);
83     if (FAILED(hres)) {
84         FIXME("GetTask failed: %08x\n", hres);
85         return NULL;
86     }
87 
88     return registered_task;
89 }
90 
91 static BSTR read_file_to_bstr(const WCHAR *file_name)
92 {
93     LARGE_INTEGER file_size;
94     DWORD read_size, size;
95     unsigned char *data;
96     HANDLE file;
97     BOOL r = FALSE;
98     BSTR ret;
99 
100     file = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL,
101                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
102     if (file == INVALID_HANDLE_VALUE) {
103         FIXME("Could not open file\n");
104         return NULL;
105     }
106 
107     if (!GetFileSizeEx(file, &file_size) || !file_size.QuadPart) {
108         FIXME("Empty file\n");
109         CloseHandle(file);
110         return NULL;
111     }
112 
113     data = HeapAlloc(GetProcessHeap(), 0, file_size.QuadPart);
114     if (data)
115         r = ReadFile(file, data, file_size.QuadPart, &read_size, NULL);
116     CloseHandle(file);
117     if (!r) {
118         FIXME("Read filed\n");
119         HeapFree(GetProcessHeap(), 0, data);
120         return NULL;
121     }
122 
123     if (read_size > 2 && data[0] == 0xff && data[1] == 0xfe) { /* UTF-16 BOM */
124         ret = SysAllocStringLen((const WCHAR *)(data + 2), (read_size - 2) / sizeof(WCHAR));
125     }else {
126         size = MultiByteToWideChar(CP_ACP, 0, (const char *)data, read_size, NULL, 0);
127         ret = SysAllocStringLen(NULL, size);
128         if (ret)
129             MultiByteToWideChar(CP_ACP, 0, (const char *)data, read_size, ret, size);
130     }
131     HeapFree(GetProcessHeap(), 0, data);
132 
133     return ret;
134 }
135 
136 static int change_command(int argc, WCHAR *argv[])
137 {
138     BOOL have_option = FALSE, enable = FALSE;
139     const WCHAR *task_name = NULL;
140     IRegisteredTask *task;
141     HRESULT hres;
142 
143     while (argc) {
144         if(!strcmpiW(argv[0], tn_optW)) {
145             if (argc < 2) {
146                 FIXME("Missing /tn value\n");
147                 return 1;
148             }
149 
150             if (task_name) {
151                 FIXME("Duplicated /tn argument\n");
152                 return 1;
153             }
154 
155             task_name = argv[1];
156             argc -= 2;
157             argv += 2;
158         }else if (!strcmpiW(argv[0], enable_optW)) {
159             enable = TRUE;
160             have_option = TRUE;
161             argc--;
162             argv++;
163         }else if (!strcmpiW(argv[0], tr_optW)) {
164             if (argc < 2) {
165                 FIXME("Missing /tr value\n");
166                 return 1;
167             }
168 
169             FIXME("Unsupported /tr option %s\n", debugstr_w(argv[1]));
170             have_option = TRUE;
171             argc -= 2;
172             argv += 2;
173         }else {
174             FIXME("Unsupported arguments %s\n", debugstr_w(argv[0]));
175             return 1;
176         }
177     }
178 
179     if (!task_name) {
180         FIXME("Missing /tn option\n");
181         return 1;
182     }
183 
184     if (!have_option) {
185         FIXME("Missing change options\n");
186         return 1;
187     }
188 
189     task = get_registered_task(task_name);
190     if (!task)
191         return 1;
192 
193     if (enable) {
194         hres = IRegisteredTask_put_Enabled(task, VARIANT_TRUE);
195         if (FAILED(hres)) {
196             IRegisteredTask_Release(task);
197             FIXME("put_Enabled failed: %08x\n", hres);
198             return 1;
199         }
200     }
201 
202     IRegisteredTask_Release(task);
203     return 0;
204 }
205 
206 static int create_command(int argc, WCHAR *argv[])
207 {
208     const WCHAR *task_name = NULL, *xml_file = NULL;
209     ITaskFolder *root = NULL;
210     LONG flags = TASK_CREATE;
211     IRegisteredTask *task;
212     VARIANT empty;
213     BSTR str, xml;
214     HRESULT hres;
215 
216     while (argc) {
217         if (!strcmpiW(argv[0], xml_optW)) {
218             if (argc < 2) {
219                 FIXME("Missing /xml value\n");
220                 return 1;
221             }
222 
223             if (xml_file) {
224                 FIXME("Duplicated /xml argument\n");
225                 return 1;
226             }
227 
228             xml_file = argv[1];
229             argc -= 2;
230             argv += 2;
231         }else if(!strcmpiW(argv[0], tn_optW)) {
232             if (argc < 2) {
233                 FIXME("Missing /tn value\n");
234                 return 1;
235             }
236 
237             if (task_name) {
238                 FIXME("Duplicated /tn argument\n");
239                 return 1;
240             }
241 
242             task_name = argv[1];
243             argc -= 2;
244             argv += 2;
245         }else if(!strcmpiW(argv[0], f_optW)) {
246             flags = TASK_CREATE_OR_UPDATE;
247             argc--;
248             argv++;
249         }else if (!strcmpiW(argv[0], ru_optW)) {
250             if (argc < 2) {
251                 FIXME("Missing /ru value\n");
252                 return 1;
253             }
254 
255             FIXME("Unsupported /ru option %s\n", debugstr_w(argv[1]));
256             argc -= 2;
257             argv += 2;
258         }else {
259             FIXME("Unsupported argument %s\n", debugstr_w(argv[0]));
260             return 1;
261         }
262     }
263 
264     if (!task_name) {
265         FIXME("Missing /tn argument\n");
266         return 1;
267     }
268 
269     if (!xml_file) {
270         FIXME("Missing /xml argument\n");
271         return E_FAIL;
272     }
273 
274     xml = read_file_to_bstr(xml_file);
275     if (!xml)
276         return 1;
277 
278     root = get_tasks_root_folder();
279     if (!root) {
280         SysFreeString(xml);
281         return 1;
282     }
283 
284     V_VT(&empty) = VT_EMPTY;
285     str = SysAllocString(task_name);
286     hres = ITaskFolder_RegisterTask(root, str, xml, flags, empty, empty,
287                                     TASK_LOGON_NONE, empty, &task);
288     SysFreeString(str);
289     SysFreeString(xml);
290     ITaskFolder_Release(root);
291     if (FAILED(hres))
292         return 1;
293 
294     IRegisteredTask_Release(task);
295     return 0;
296 }
297 
298 static int delete_command(int argc, WCHAR *argv[])
299 {
300     const WCHAR *task_name = NULL;
301     ITaskFolder *root = NULL;
302     BSTR str;
303     HRESULT hres;
304 
305     while (argc) {
306         if (!strcmpiW(argv[0], f_optW)) {
307             TRACE("force opt\n");
308             argc--;
309             argv++;
310         }else if(!strcmpiW(argv[0], tn_optW)) {
311             if (argc < 2) {
312                 FIXME("Missing /tn value\n");
313                 return 1;
314             }
315 
316             if (task_name) {
317                 FIXME("Duplicated /tn argument\n");
318                 return 1;
319             }
320 
321             task_name = argv[1];
322             argc -= 2;
323             argv += 2;
324         }else {
325             FIXME("Unsupported argument %s\n", debugstr_w(argv[0]));
326             return 1;
327         }
328     }
329 
330     if (!task_name) {
331         FIXME("Missing /tn argument\n");
332         return 1;
333     }
334 
335     root = get_tasks_root_folder();
336     if (!root)
337         return 1;
338 
339     str = SysAllocString(task_name);
340     hres = ITaskFolder_DeleteTask(root, str, 0);
341     SysFreeString(str);
342     ITaskFolder_Release(root);
343     if (FAILED(hres))
344         return 1;
345 
346     return 0;
347 }
348 
349 int wmain(int argc, WCHAR *argv[])
350 {
351     int i, ret = 0;
352 
353     for (i = 0; i < argc; i++)
354         TRACE(" %s", wine_dbgstr_w(argv[i]));
355     TRACE("\n");
356 
357     CoInitialize(NULL);
358 
359     if (argc < 2)
360         FIXME("Print current tasks state\n");
361     else if (!strcmpiW(argv[1], change_optW))
362         ret = change_command(argc - 2, argv + 2);
363     else if (!strcmpiW(argv[1], create_optW))
364         ret = create_command(argc - 2, argv + 2);
365     else if (!strcmpiW(argv[1], delete_optW))
366         ret = delete_command(argc - 2, argv + 2);
367     else
368         FIXME("Unsupported command %s\n", debugstr_w(argv[1]));
369 
370     CoUninitialize();
371     return ret;
372 }
373