taskschd: Forward IRegisteredTask::get_State to SchRpcGetTaskInfo.
[wine/multimedia.git] / dlls / schedsvc / schedsvc.c
blobeb84998a698b4e483ec1e63e30ad496b4acc82db
1 /*
2 * Task Scheduler Service
4 * Copyright 2014 Dmitry Timoshkov
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "schrpc.h"
25 #include "taskschd.h"
26 #include "wine/debug.h"
28 #include "schedsvc_private.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(schedsvc);
32 static const char bom_utf8[] = { 0xef,0xbb,0xbf };
34 HRESULT __cdecl SchRpcHighestVersion(DWORD *version)
36 TRACE("%p\n", version);
38 *version = MAKELONG(3, 1);
39 return S_OK;
42 static WCHAR *get_full_name(const WCHAR *path, WCHAR **relative_path)
44 static const WCHAR tasksW[] = { '\\','t','a','s','k','s','\\',0 };
45 WCHAR *target;
46 int len;
48 len = GetSystemDirectoryW(NULL, 0);
49 len += strlenW(tasksW) + strlenW(path);
51 target = heap_alloc(len * sizeof(WCHAR));
52 if (target)
54 GetSystemDirectoryW(target, len);
55 strcatW(target, tasksW);
56 if (relative_path)
57 *relative_path = target + strlenW(target) - 1;
58 while (*path == '\\') path++;
59 strcatW(target, path);
61 return target;
65 * Recursively create all directories in the path.
67 static HRESULT create_directory(const WCHAR *path)
69 HRESULT hr = S_OK;
70 WCHAR *new_path;
71 int len;
73 new_path = heap_alloc((strlenW(path) + 1) * sizeof(WCHAR));
74 if (!new_path) return E_OUTOFMEMORY;
76 strcpyW(new_path, path);
78 len = strlenW(new_path);
79 while (len && new_path[len - 1] == '\\')
81 new_path[len - 1] = 0;
82 len--;
85 while (!CreateDirectoryW(new_path, NULL))
87 WCHAR *slash;
88 DWORD last_error = GetLastError();
90 if (last_error == ERROR_ALREADY_EXISTS || last_error != ERROR_PATH_NOT_FOUND ||
91 !(slash = strrchrW(new_path, '\\')))
93 hr = HRESULT_FROM_WIN32(last_error);
94 break;
97 len = slash - new_path;
98 new_path[len] = 0;
99 hr = create_directory(new_path);
100 if (hr != S_OK) break;
101 new_path[len] = '\\';
104 heap_free(new_path);
105 return hr;
108 static HRESULT write_xml_utf8(const WCHAR *name, DWORD disposition, const WCHAR *xmlW)
110 static const char comment[] = "<!-- Task definition created by Wine -->\n";
111 HANDLE hfile;
112 DWORD size;
113 char *xml;
114 HRESULT hr = S_OK;
116 hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, disposition, 0, 0);
117 if (hfile == INVALID_HANDLE_VALUE)
119 if (GetLastError() == ERROR_FILE_EXISTS)
120 return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
122 return HRESULT_FROM_WIN32(GetLastError());
125 size = WideCharToMultiByte(CP_UTF8, 0, xmlW, -1, NULL, 0, NULL, NULL);
126 xml = heap_alloc(size);
127 if (!xml)
129 CloseHandle(hfile);
130 return E_OUTOFMEMORY;
132 WideCharToMultiByte(CP_UTF8, 0, xmlW, -1, xml, size, NULL, NULL);
134 if (!WriteFile(hfile, bom_utf8, sizeof(bom_utf8), &size, NULL))
136 hr = HRESULT_FROM_WIN32(GetLastError());
137 goto failed;
139 if (!WriteFile(hfile, comment, strlen(comment), &size, NULL))
141 hr = HRESULT_FROM_WIN32(GetLastError());
142 goto failed;
145 /* skip XML declaration with UTF-16 specifier */
146 if (!memcmp(xml, "<?xml", 5))
148 const char *p = strchr(xml, '>');
149 if (p++) while (isspace(*p)) p++;
150 else p = xml;
151 if (!WriteFile(hfile, p, strlen(p), &size, NULL))
152 hr = HRESULT_FROM_WIN32(GetLastError());
154 else
156 if (!WriteFile(hfile, xml, strlen(xml), &size, NULL))
157 hr = HRESULT_FROM_WIN32(GetLastError());
160 failed:
161 heap_free(xml);
162 CloseHandle(hfile);
163 return hr;
166 HRESULT __cdecl SchRpcRegisterTask(const WCHAR *path, const WCHAR *xml, DWORD flags, const WCHAR *sddl,
167 DWORD task_logon_type, DWORD n_creds, const TASK_USER_CRED *creds,
168 WCHAR **actual_path, TASK_XML_ERROR_INFO **xml_error_info)
170 WCHAR *full_name, *relative_path;
171 DWORD disposition;
172 HRESULT hr;
174 TRACE("%s,%s,%#x,%s,%u,%u,%p,%p,%p\n", debugstr_w(path), debugstr_w(xml), flags,
175 debugstr_w(sddl), task_logon_type, n_creds, creds, actual_path, xml_error_info);
177 *actual_path = NULL;
178 *xml_error_info = NULL;
180 /* FIXME: assume that validation is performed on the client side */
181 if (flags & TASK_VALIDATE_ONLY) return S_OK;
183 full_name = get_full_name(path, &relative_path);
184 if (!full_name) return E_OUTOFMEMORY;
186 if (strchrW(path, '\\') || strchrW(path, '/'))
188 WCHAR *p = strrchrW(full_name, '/');
189 if (!p) p = strrchrW(full_name, '\\');
190 *p = 0;
191 hr = create_directory(full_name);
192 if (hr != S_OK && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
194 heap_free(full_name);
195 return hr;
197 *p = '\\';
200 switch (flags & (TASK_CREATE | TASK_UPDATE))
202 default:
203 case TASK_CREATE:
204 disposition = CREATE_NEW;
205 break;
207 case TASK_UPDATE:
208 disposition = OPEN_EXISTING;
209 break;
211 case (TASK_CREATE | TASK_UPDATE):
212 disposition = OPEN_ALWAYS;
213 break;
216 hr = write_xml_utf8(full_name, disposition, xml);
217 if (hr == S_OK)
219 *actual_path = heap_strdupW(relative_path);
220 schedsvc_auto_start();
223 heap_free(full_name);
224 return hr;
227 static int detect_encoding(const void *buffer, DWORD size)
229 if (size >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
230 return CP_UTF8;
231 else
233 int flags = IS_TEXT_UNICODE_SIGNATURE |
234 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
235 IS_TEXT_UNICODE_ODD_LENGTH;
236 IsTextUnicode(buffer, size, &flags);
237 if (flags & IS_TEXT_UNICODE_SIGNATURE)
238 return -1;
239 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
240 return -2;
241 return CP_ACP;
245 static HRESULT read_xml(const WCHAR *name, WCHAR **xml)
247 HANDLE hfile;
248 DWORD size, attrs;
249 char *src;
250 int cp;
252 attrs = GetFileAttributesW(name);
253 if (attrs == INVALID_FILE_ATTRIBUTES)
254 return HRESULT_FROM_WIN32(GetLastError());
255 if (attrs & FILE_ATTRIBUTE_DIRECTORY)
256 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
258 hfile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
259 if (hfile == INVALID_HANDLE_VALUE)
260 return HRESULT_FROM_WIN32(GetLastError());
262 size = GetFileSize(hfile, NULL);
263 src = heap_alloc(size + 2);
264 if (!src)
266 CloseHandle(hfile);
267 return E_OUTOFMEMORY;
270 src[size] = 0;
271 src[size + 1] = 0;
273 ReadFile(hfile, src, size, &size, NULL);
274 CloseHandle(hfile);
276 cp = detect_encoding(src, size);
277 if (cp < 0)
279 *xml = (WCHAR *)src;
280 return S_OK;
283 if (cp == CP_UTF8 && size >= sizeof(bom_utf8) && !memcmp(src, bom_utf8, sizeof(bom_utf8)))
284 src += sizeof(bom_utf8);
286 size = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
287 *xml = heap_alloc(size * sizeof(WCHAR));
288 if (!*xml) return E_OUTOFMEMORY;
289 MultiByteToWideChar(cp, 0, src, -1, *xml, size);
290 return S_OK;
293 HRESULT __cdecl SchRpcRetrieveTask(const WCHAR *path, const WCHAR *languages, ULONG *n_languages, WCHAR **xml)
295 WCHAR *full_name;
296 HRESULT hr;
298 TRACE("%s,%s,%p,%p\n", debugstr_w(path), debugstr_w(languages), n_languages, xml);
300 full_name = get_full_name(path, NULL);
301 if (!full_name) return E_OUTOFMEMORY;
303 hr = read_xml(full_name, xml);
304 if (hr != S_OK) *xml = NULL;
306 heap_free(full_name);
307 return hr;
310 HRESULT __cdecl SchRpcCreateFolder(const WCHAR *path, const WCHAR *sddl, DWORD flags)
312 WCHAR *full_name;
313 HRESULT hr;
315 TRACE("%s,%s,%#x\n", debugstr_w(path), debugstr_w(sddl), flags);
317 if (flags) return E_INVALIDARG;
319 full_name = get_full_name(path, NULL);
320 if (!full_name) return E_OUTOFMEMORY;
322 hr = create_directory(full_name);
324 heap_free(full_name);
325 return hr;
328 HRESULT __cdecl SchRpcSetSecurity(const WCHAR *path, const WCHAR *sddl, DWORD flags)
330 FIXME("%s,%s,%#x: stub\n", debugstr_w(path), debugstr_w(sddl), flags);
331 return E_NOTIMPL;
334 HRESULT __cdecl SchRpcGetSecurity(const WCHAR *path, DWORD flags, WCHAR **sddl)
336 FIXME("%s,%#x,%p: stub\n", debugstr_w(path), flags, sddl);
337 return E_NOTIMPL;
340 static void free_list(TASK_NAMES list, LONG count)
342 LONG i;
344 for (i = 0; i < count; i++)
345 heap_free(list[i]);
347 heap_free(list);
350 static inline BOOL is_directory(const WIN32_FIND_DATAW *data)
352 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
354 if (data->cFileName[0] == '.')
356 if (!data->cFileName[1] || (data->cFileName[1] == '.' && !data->cFileName[2]))
357 return FALSE;
359 return TRUE;
361 return FALSE;
364 static inline BOOL is_file(const WIN32_FIND_DATAW *data)
366 return !(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
369 HRESULT __cdecl SchRpcEnumFolders(const WCHAR *path, DWORD flags, DWORD *start_index, DWORD n_requested,
370 DWORD *n_names, TASK_NAMES *names)
372 static const WCHAR allW[] = {'\\','*',0};
373 HRESULT hr = S_OK;
374 WCHAR *full_name;
375 WCHAR pathW[MAX_PATH];
376 WIN32_FIND_DATAW data;
377 HANDLE handle;
378 DWORD allocated, count, index;
379 TASK_NAMES list;
381 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path), flags, *start_index, n_requested, n_names, names);
383 *n_names = 0;
384 *names = NULL;
386 if (flags & ~TASK_ENUM_HIDDEN) return E_INVALIDARG;
388 if (!n_requested) n_requested = ~0u;
390 full_name = get_full_name(path, NULL);
391 if (!full_name) return E_OUTOFMEMORY;
393 if (strlenW(full_name) + 2 > MAX_PATH)
395 heap_free(full_name);
396 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
399 strcpyW(pathW, full_name);
400 strcatW(pathW, allW);
402 heap_free(full_name);
404 allocated = 64;
405 list = heap_alloc(allocated * sizeof(list[0]));
406 if (!list) return E_OUTOFMEMORY;
408 index = count = 0;
410 handle = FindFirstFileW(pathW, &data);
411 if (handle == INVALID_HANDLE_VALUE)
413 heap_free(list);
414 if (GetLastError() == ERROR_PATH_NOT_FOUND)
415 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
416 return HRESULT_FROM_WIN32(GetLastError());
421 if (is_directory(&data) && index++ >= *start_index)
423 if (count >= allocated)
425 TASK_NAMES new_list;
426 allocated *= 2;
427 new_list = heap_realloc(list, allocated * sizeof(list[0]));
428 if (!new_list)
430 hr = E_OUTOFMEMORY;
431 break;
433 list = new_list;
436 TRACE("adding %s\n", debugstr_w(data.cFileName));
438 list[count] = heap_strdupW(data.cFileName);
439 if (!list[count])
441 hr = E_OUTOFMEMORY;
442 break;
445 count++;
447 if (count >= n_requested)
449 hr = S_FALSE;
450 break;
453 } while (FindNextFileW(handle, &data));
455 FindClose(handle);
457 if (FAILED(hr))
459 free_list(list, count);
460 return hr;
463 *n_names = count;
465 if (count)
467 *names = list;
468 *start_index = index;
469 return hr;
472 heap_free(list);
473 *names = NULL;
474 return *start_index ? S_FALSE : S_OK;
477 HRESULT __cdecl SchRpcEnumTasks(const WCHAR *path, DWORD flags, DWORD *start_index, DWORD n_requested,
478 DWORD *n_names, TASK_NAMES *names)
480 static const WCHAR allW[] = {'\\','*',0};
481 HRESULT hr = S_OK;
482 WCHAR *full_name;
483 WCHAR pathW[MAX_PATH];
484 WIN32_FIND_DATAW data;
485 HANDLE handle;
486 DWORD allocated, count, index;
487 TASK_NAMES list;
489 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path), flags, *start_index, n_requested, n_names, names);
491 *n_names = 0;
492 *names = NULL;
494 if (flags & ~TASK_ENUM_HIDDEN) return E_INVALIDARG;
496 if (!n_requested) n_requested = ~0u;
498 full_name = get_full_name(path, NULL);
499 if (!full_name) return E_OUTOFMEMORY;
501 if (strlenW(full_name) + 2 > MAX_PATH)
503 heap_free(full_name);
504 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
507 strcpyW(pathW, full_name);
508 strcatW(pathW, allW);
510 heap_free(full_name);
512 allocated = 64;
513 list = heap_alloc(allocated * sizeof(list[0]));
514 if (!list) return E_OUTOFMEMORY;
516 index = count = 0;
518 handle = FindFirstFileW(pathW, &data);
519 if (handle == INVALID_HANDLE_VALUE)
521 heap_free(list);
522 if (GetLastError() == ERROR_PATH_NOT_FOUND)
523 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
524 return HRESULT_FROM_WIN32(GetLastError());
529 if (is_file(&data) && index++ >= *start_index)
531 if (count >= allocated)
533 TASK_NAMES new_list;
534 allocated *= 2;
535 new_list = heap_realloc(list, allocated * sizeof(list[0]));
536 if (!new_list)
538 hr = E_OUTOFMEMORY;
539 break;
541 list = new_list;
544 TRACE("adding %s\n", debugstr_w(data.cFileName));
546 list[count] = heap_strdupW(data.cFileName);
547 if (!list[count])
549 hr = E_OUTOFMEMORY;
550 break;
553 count++;
555 if (count >= n_requested)
557 hr = S_FALSE;
558 break;
561 } while (FindNextFileW(handle, &data));
563 FindClose(handle);
565 if (FAILED(hr))
567 free_list(list, count);
568 return hr;
571 *n_names = count;
573 if (count)
575 *names = list;
576 *start_index = index;
577 return hr;
580 heap_free(list);
581 *names = NULL;
582 return *start_index ? S_FALSE : S_OK;
585 HRESULT __cdecl SchRpcEnumInstances(const WCHAR *path, DWORD flags, DWORD *n_guids, GUID **guids)
587 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path), flags, n_guids, guids);
588 return E_NOTIMPL;
591 HRESULT __cdecl SchRpcGetInstanceInfo(GUID guid, WCHAR **path, DWORD *task_state, WCHAR **action,
592 WCHAR **info, DWORD *n_instances, GUID **instances, DWORD *pid)
594 FIXME("%s,%p,%p,%p,%p,%p,%p,%p: stub\n", wine_dbgstr_guid(&guid), path, task_state, action,
595 info, n_instances, instances, pid);
596 return E_NOTIMPL;
599 HRESULT __cdecl SchRpcStopInstance(GUID guid, DWORD flags)
601 FIXME("%s,%#x: stub\n", wine_dbgstr_guid(&guid), flags);
602 return E_NOTIMPL;
605 HRESULT __cdecl SchRpcStop(const WCHAR *path, DWORD flags)
607 FIXME("%s,%#x: stub\n", debugstr_w(path), flags);
608 return E_NOTIMPL;
611 HRESULT __cdecl SchRpcRun(const WCHAR *path, DWORD n_args, const WCHAR **args, DWORD flags,
612 DWORD session_id, const WCHAR *user, GUID *guid)
614 FIXME("%s,%u,%p,%#x,%#x,%s,%p: stub\n", debugstr_w(path), n_args, args, flags,
615 session_id, debugstr_w(user), guid);
616 return E_NOTIMPL;
619 HRESULT __cdecl SchRpcDelete(const WCHAR *path, DWORD flags)
621 WCHAR *full_name;
622 HRESULT hr = S_OK;
624 TRACE("%s,%#x\n", debugstr_w(path), flags);
626 if (flags) return E_INVALIDARG;
628 while (*path == '\\' || *path == '/') path++;
629 if (!*path) return E_ACCESSDENIED;
631 full_name = get_full_name(path, NULL);
632 if (!full_name) return E_OUTOFMEMORY;
634 if (!RemoveDirectoryW(full_name))
636 hr = HRESULT_FROM_WIN32(GetLastError());
637 if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY))
638 hr = DeleteFileW(full_name) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
641 heap_free(full_name);
642 return hr;
645 HRESULT __cdecl SchRpcRename(const WCHAR *path, const WCHAR *name, DWORD flags)
647 FIXME("%s,%s,%#x: stub\n", debugstr_w(path), debugstr_w(name), flags);
648 return E_NOTIMPL;
651 HRESULT __cdecl SchRpcScheduledRuntimes(const WCHAR *path, SYSTEMTIME *start, SYSTEMTIME *end, DWORD flags,
652 DWORD n_requested, DWORD *n_runtimes, SYSTEMTIME **runtimes)
654 FIXME("%s,%p,%p,%#x,%u,%p,%p: stub\n", debugstr_w(path), start, end, flags,
655 n_requested, n_runtimes, runtimes);
656 return E_NOTIMPL;
659 HRESULT __cdecl SchRpcGetLastRunInfo(const WCHAR *path, SYSTEMTIME *last_runtime, DWORD *last_return_code)
661 FIXME("%s,%p,%p: stub\n", debugstr_w(path), last_runtime, last_return_code);
662 return E_NOTIMPL;
665 HRESULT __cdecl SchRpcGetTaskInfo(const WCHAR *path, DWORD flags, DWORD *enabled, DWORD *task_state)
667 WCHAR *full_name, *xml;
668 HRESULT hr;
670 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path), flags, enabled, task_state);
672 full_name = get_full_name(path, NULL);
673 if (!full_name) return E_OUTOFMEMORY;
675 hr = read_xml(full_name, &xml);
676 heap_free(full_name);
677 if (hr != S_OK) return hr;
678 heap_free(xml);
680 *enabled = 0;
681 *task_state = (flags & SCH_FLAG_STATE) ? TASK_STATE_DISABLED : TASK_STATE_UNKNOWN;
682 return S_OK;
685 HRESULT __cdecl SchRpcGetNumberOfMissedRuns(const WCHAR *path, DWORD *runs)
687 FIXME("%s,%p: stub\n", debugstr_w(path), runs);
688 return E_NOTIMPL;
691 HRESULT __cdecl SchRpcEnableTask(const WCHAR *path, DWORD enabled)
693 FIXME("%s,%u: stub\n", debugstr_w(path), enabled);
694 return E_NOTIMPL;