secur32/tests: Use importlib for functions available since Windows XP.
[wine.git] / dlls / schedsvc / schedsvc.c
blob8be8c0dd7fc65741ec9979d33638f1889a4fa690
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_PATH_NOT_FOUND || !(slash = strrchrW(new_path, '\\')))
92 hr = HRESULT_FROM_WIN32(last_error);
93 break;
96 len = slash - new_path;
97 new_path[len] = 0;
98 hr = create_directory(new_path);
99 if (hr != S_OK) break;
100 new_path[len] = '\\';
103 heap_free(new_path);
104 return hr;
107 static HRESULT write_xml_utf8(const WCHAR *name, DWORD disposition, const WCHAR *xmlW)
109 static const char comment[] = "<!-- Task definition created by Wine -->\n";
110 HANDLE hfile;
111 DWORD size;
112 char *xml;
113 HRESULT hr = S_OK;
115 hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, disposition, 0, 0);
116 if (hfile == INVALID_HANDLE_VALUE)
118 if (GetLastError() == ERROR_FILE_EXISTS)
119 return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
121 return HRESULT_FROM_WIN32(GetLastError());
124 size = WideCharToMultiByte(CP_UTF8, 0, xmlW, -1, NULL, 0, NULL, NULL);
125 xml = heap_alloc(size);
126 if (!xml)
128 CloseHandle(hfile);
129 return E_OUTOFMEMORY;
131 WideCharToMultiByte(CP_UTF8, 0, xmlW, -1, xml, size, NULL, NULL);
133 if (!WriteFile(hfile, bom_utf8, sizeof(bom_utf8), &size, NULL))
135 hr = HRESULT_FROM_WIN32(GetLastError());
136 goto failed;
138 if (!WriteFile(hfile, comment, strlen(comment), &size, NULL))
140 hr = HRESULT_FROM_WIN32(GetLastError());
141 goto failed;
144 /* skip XML declaration with UTF-16 specifier */
145 if (!memcmp(xml, "<?xml", 5))
147 const char *p = strchr(xml, '>');
148 if (p++) while (isspace(*p)) p++;
149 else p = xml;
150 if (!WriteFile(hfile, p, strlen(p), &size, NULL))
151 hr = HRESULT_FROM_WIN32(GetLastError());
153 else
155 if (!WriteFile(hfile, xml, strlen(xml), &size, NULL))
156 hr = HRESULT_FROM_WIN32(GetLastError());
159 failed:
160 heap_free(xml);
161 CloseHandle(hfile);
162 return hr;
165 HRESULT __cdecl SchRpcRegisterTask(const WCHAR *path, const WCHAR *xml, DWORD flags, const WCHAR *sddl,
166 DWORD task_logon_type, DWORD n_creds, const TASK_USER_CRED *creds,
167 WCHAR **actual_path, TASK_XML_ERROR_INFO **xml_error_info)
169 WCHAR *full_name, *relative_path;
170 DWORD disposition;
171 HRESULT hr;
173 TRACE("%s,%s,%#x,%s,%u,%u,%p,%p,%p\n", debugstr_w(path), debugstr_w(xml), flags,
174 debugstr_w(sddl), task_logon_type, n_creds, creds, actual_path, xml_error_info);
176 *actual_path = NULL;
177 *xml_error_info = NULL;
179 /* FIXME: assume that validation is performed on the client side */
180 if (flags & TASK_VALIDATE_ONLY) return S_OK;
182 if (path)
184 full_name = get_full_name(path, &relative_path);
185 if (!full_name) return E_OUTOFMEMORY;
187 if (strchrW(path, '\\') || strchrW(path, '/'))
189 WCHAR *p = strrchrW(full_name, '/');
190 if (!p) p = strrchrW(full_name, '\\');
191 *p = 0;
192 hr = create_directory(full_name);
193 if (hr != S_OK && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
195 heap_free(full_name);
196 return hr;
198 *p = '\\';
201 else
203 IID iid;
204 WCHAR uuid_str[39];
206 UuidCreate(&iid);
207 StringFromGUID2(&iid, uuid_str, 39);
209 full_name = get_full_name(uuid_str, &relative_path);
210 if (!full_name) return E_OUTOFMEMORY;
211 /* skip leading '\' */
212 relative_path++;
215 switch (flags & (TASK_CREATE | TASK_UPDATE))
217 default:
218 case TASK_CREATE:
219 disposition = CREATE_NEW;
220 break;
222 case TASK_UPDATE:
223 disposition = OPEN_EXISTING;
224 break;
226 case (TASK_CREATE | TASK_UPDATE):
227 disposition = OPEN_ALWAYS;
228 break;
231 hr = write_xml_utf8(full_name, disposition, xml);
232 if (hr == S_OK)
234 *actual_path = heap_strdupW(relative_path);
235 schedsvc_auto_start();
238 heap_free(full_name);
239 return hr;
242 static int detect_encoding(const void *buffer, DWORD size)
244 if (size >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
245 return CP_UTF8;
246 else
248 int flags = IS_TEXT_UNICODE_SIGNATURE |
249 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
250 IS_TEXT_UNICODE_ODD_LENGTH;
251 IsTextUnicode(buffer, size, &flags);
252 if (flags & IS_TEXT_UNICODE_SIGNATURE)
253 return -1;
254 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
255 return -2;
256 return CP_ACP;
260 static HRESULT read_xml(const WCHAR *name, WCHAR **xml)
262 HANDLE hfile;
263 DWORD size, attrs;
264 char *src;
265 int cp;
267 attrs = GetFileAttributesW(name);
268 if (attrs == INVALID_FILE_ATTRIBUTES)
269 return HRESULT_FROM_WIN32(GetLastError());
270 if (attrs & FILE_ATTRIBUTE_DIRECTORY)
271 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
273 hfile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
274 if (hfile == INVALID_HANDLE_VALUE)
275 return HRESULT_FROM_WIN32(GetLastError());
277 size = GetFileSize(hfile, NULL);
278 src = heap_alloc(size + 2);
279 if (!src)
281 CloseHandle(hfile);
282 return E_OUTOFMEMORY;
285 src[size] = 0;
286 src[size + 1] = 0;
288 ReadFile(hfile, src, size, &size, NULL);
289 CloseHandle(hfile);
291 cp = detect_encoding(src, size);
292 if (cp < 0)
294 *xml = (WCHAR *)src;
295 return S_OK;
298 if (cp == CP_UTF8 && size >= sizeof(bom_utf8) && !memcmp(src, bom_utf8, sizeof(bom_utf8)))
299 src += sizeof(bom_utf8);
301 size = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
302 *xml = heap_alloc(size * sizeof(WCHAR));
303 if (!*xml) return E_OUTOFMEMORY;
304 MultiByteToWideChar(cp, 0, src, -1, *xml, size);
305 return S_OK;
308 HRESULT __cdecl SchRpcRetrieveTask(const WCHAR *path, const WCHAR *languages, ULONG *n_languages, WCHAR **xml)
310 WCHAR *full_name;
311 HRESULT hr;
313 TRACE("%s,%s,%p,%p\n", debugstr_w(path), debugstr_w(languages), n_languages, xml);
315 full_name = get_full_name(path, NULL);
316 if (!full_name) return E_OUTOFMEMORY;
318 hr = read_xml(full_name, xml);
319 if (hr != S_OK) *xml = NULL;
321 heap_free(full_name);
322 return hr;
325 HRESULT __cdecl SchRpcCreateFolder(const WCHAR *path, const WCHAR *sddl, DWORD flags)
327 WCHAR *full_name;
328 HRESULT hr;
330 TRACE("%s,%s,%#x\n", debugstr_w(path), debugstr_w(sddl), flags);
332 if (flags) return E_INVALIDARG;
334 full_name = get_full_name(path, NULL);
335 if (!full_name) return E_OUTOFMEMORY;
337 hr = create_directory(full_name);
339 heap_free(full_name);
340 return hr;
343 HRESULT __cdecl SchRpcSetSecurity(const WCHAR *path, const WCHAR *sddl, DWORD flags)
345 FIXME("%s,%s,%#x: stub\n", debugstr_w(path), debugstr_w(sddl), flags);
346 return E_NOTIMPL;
349 HRESULT __cdecl SchRpcGetSecurity(const WCHAR *path, DWORD flags, WCHAR **sddl)
351 FIXME("%s,%#x,%p: stub\n", debugstr_w(path), flags, sddl);
352 return E_NOTIMPL;
355 static void free_list(TASK_NAMES list, LONG count)
357 LONG i;
359 for (i = 0; i < count; i++)
360 heap_free(list[i]);
362 heap_free(list);
365 static inline BOOL is_directory(const WIN32_FIND_DATAW *data)
367 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
369 if (data->cFileName[0] == '.')
371 if (!data->cFileName[1] || (data->cFileName[1] == '.' && !data->cFileName[2]))
372 return FALSE;
374 return TRUE;
376 return FALSE;
379 static inline BOOL is_file(const WIN32_FIND_DATAW *data)
381 return !(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
384 HRESULT __cdecl SchRpcEnumFolders(const WCHAR *path, DWORD flags, DWORD *start_index, DWORD n_requested,
385 DWORD *n_names, TASK_NAMES *names)
387 static const WCHAR allW[] = {'\\','*',0};
388 HRESULT hr = S_OK;
389 WCHAR *full_name;
390 WCHAR pathW[MAX_PATH];
391 WIN32_FIND_DATAW data;
392 HANDLE handle;
393 DWORD allocated, count, index;
394 TASK_NAMES list;
396 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path), flags, *start_index, n_requested, n_names, names);
398 *n_names = 0;
399 *names = NULL;
401 if (flags & ~TASK_ENUM_HIDDEN) return E_INVALIDARG;
403 if (!n_requested) n_requested = ~0u;
405 full_name = get_full_name(path, NULL);
406 if (!full_name) return E_OUTOFMEMORY;
408 if (strlenW(full_name) + 2 > MAX_PATH)
410 heap_free(full_name);
411 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
414 strcpyW(pathW, full_name);
415 strcatW(pathW, allW);
417 heap_free(full_name);
419 allocated = 64;
420 list = heap_alloc(allocated * sizeof(list[0]));
421 if (!list) return E_OUTOFMEMORY;
423 index = count = 0;
425 handle = FindFirstFileW(pathW, &data);
426 if (handle == INVALID_HANDLE_VALUE)
428 heap_free(list);
429 if (GetLastError() == ERROR_PATH_NOT_FOUND)
430 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
431 return HRESULT_FROM_WIN32(GetLastError());
436 if (is_directory(&data) && index++ >= *start_index)
438 if (count >= allocated)
440 TASK_NAMES new_list;
441 allocated *= 2;
442 new_list = heap_realloc(list, allocated * sizeof(list[0]));
443 if (!new_list)
445 hr = E_OUTOFMEMORY;
446 break;
448 list = new_list;
451 TRACE("adding %s\n", debugstr_w(data.cFileName));
453 list[count] = heap_strdupW(data.cFileName);
454 if (!list[count])
456 hr = E_OUTOFMEMORY;
457 break;
460 count++;
462 if (count >= n_requested)
464 hr = S_FALSE;
465 break;
468 } while (FindNextFileW(handle, &data));
470 FindClose(handle);
472 if (FAILED(hr))
474 free_list(list, count);
475 return hr;
478 *n_names = count;
480 if (count)
482 *names = list;
483 *start_index = index;
484 return hr;
487 heap_free(list);
488 *names = NULL;
489 return *start_index ? S_FALSE : S_OK;
492 HRESULT __cdecl SchRpcEnumTasks(const WCHAR *path, DWORD flags, DWORD *start_index, DWORD n_requested,
493 DWORD *n_names, TASK_NAMES *names)
495 static const WCHAR allW[] = {'\\','*',0};
496 HRESULT hr = S_OK;
497 WCHAR *full_name;
498 WCHAR pathW[MAX_PATH];
499 WIN32_FIND_DATAW data;
500 HANDLE handle;
501 DWORD allocated, count, index;
502 TASK_NAMES list;
504 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path), flags, *start_index, n_requested, n_names, names);
506 *n_names = 0;
507 *names = NULL;
509 if (flags & ~TASK_ENUM_HIDDEN) return E_INVALIDARG;
511 if (!n_requested) n_requested = ~0u;
513 full_name = get_full_name(path, NULL);
514 if (!full_name) return E_OUTOFMEMORY;
516 if (strlenW(full_name) + 2 > MAX_PATH)
518 heap_free(full_name);
519 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
522 strcpyW(pathW, full_name);
523 strcatW(pathW, allW);
525 heap_free(full_name);
527 allocated = 64;
528 list = heap_alloc(allocated * sizeof(list[0]));
529 if (!list) return E_OUTOFMEMORY;
531 index = count = 0;
533 handle = FindFirstFileW(pathW, &data);
534 if (handle == INVALID_HANDLE_VALUE)
536 heap_free(list);
537 if (GetLastError() == ERROR_PATH_NOT_FOUND)
538 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
539 return HRESULT_FROM_WIN32(GetLastError());
544 if (is_file(&data) && index++ >= *start_index)
546 if (count >= allocated)
548 TASK_NAMES new_list;
549 allocated *= 2;
550 new_list = heap_realloc(list, allocated * sizeof(list[0]));
551 if (!new_list)
553 hr = E_OUTOFMEMORY;
554 break;
556 list = new_list;
559 TRACE("adding %s\n", debugstr_w(data.cFileName));
561 list[count] = heap_strdupW(data.cFileName);
562 if (!list[count])
564 hr = E_OUTOFMEMORY;
565 break;
568 count++;
570 if (count >= n_requested)
572 hr = S_FALSE;
573 break;
576 } while (FindNextFileW(handle, &data));
578 FindClose(handle);
580 if (FAILED(hr))
582 free_list(list, count);
583 return hr;
586 *n_names = count;
588 if (count)
590 *names = list;
591 *start_index = index;
592 return hr;
595 heap_free(list);
596 *names = NULL;
597 return *start_index ? S_FALSE : S_OK;
600 HRESULT __cdecl SchRpcEnumInstances(const WCHAR *path, DWORD flags, DWORD *n_guids, GUID **guids)
602 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path), flags, n_guids, guids);
603 return E_NOTIMPL;
606 HRESULT __cdecl SchRpcGetInstanceInfo(GUID guid, WCHAR **path, DWORD *task_state, WCHAR **action,
607 WCHAR **info, DWORD *n_instances, GUID **instances, DWORD *pid)
609 FIXME("%s,%p,%p,%p,%p,%p,%p,%p: stub\n", wine_dbgstr_guid(&guid), path, task_state, action,
610 info, n_instances, instances, pid);
611 return E_NOTIMPL;
614 HRESULT __cdecl SchRpcStopInstance(GUID guid, DWORD flags)
616 FIXME("%s,%#x: stub\n", wine_dbgstr_guid(&guid), flags);
617 return E_NOTIMPL;
620 HRESULT __cdecl SchRpcStop(const WCHAR *path, DWORD flags)
622 FIXME("%s,%#x: stub\n", debugstr_w(path), flags);
623 return E_NOTIMPL;
626 HRESULT __cdecl SchRpcRun(const WCHAR *path, DWORD n_args, const WCHAR **args, DWORD flags,
627 DWORD session_id, const WCHAR *user, GUID *guid)
629 FIXME("%s,%u,%p,%#x,%#x,%s,%p: stub\n", debugstr_w(path), n_args, args, flags,
630 session_id, debugstr_w(user), guid);
631 return E_NOTIMPL;
634 HRESULT __cdecl SchRpcDelete(const WCHAR *path, DWORD flags)
636 WCHAR *full_name;
637 HRESULT hr = S_OK;
639 TRACE("%s,%#x\n", debugstr_w(path), flags);
641 if (flags) return E_INVALIDARG;
643 while (*path == '\\' || *path == '/') path++;
644 if (!*path) return E_ACCESSDENIED;
646 full_name = get_full_name(path, NULL);
647 if (!full_name) return E_OUTOFMEMORY;
649 if (!RemoveDirectoryW(full_name))
651 hr = HRESULT_FROM_WIN32(GetLastError());
652 if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY))
653 hr = DeleteFileW(full_name) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
656 heap_free(full_name);
657 return hr;
660 HRESULT __cdecl SchRpcRename(const WCHAR *path, const WCHAR *name, DWORD flags)
662 FIXME("%s,%s,%#x: stub\n", debugstr_w(path), debugstr_w(name), flags);
663 return E_NOTIMPL;
666 HRESULT __cdecl SchRpcScheduledRuntimes(const WCHAR *path, SYSTEMTIME *start, SYSTEMTIME *end, DWORD flags,
667 DWORD n_requested, DWORD *n_runtimes, SYSTEMTIME **runtimes)
669 FIXME("%s,%p,%p,%#x,%u,%p,%p: stub\n", debugstr_w(path), start, end, flags,
670 n_requested, n_runtimes, runtimes);
671 return E_NOTIMPL;
674 HRESULT __cdecl SchRpcGetLastRunInfo(const WCHAR *path, SYSTEMTIME *last_runtime, DWORD *last_return_code)
676 FIXME("%s,%p,%p: stub\n", debugstr_w(path), last_runtime, last_return_code);
677 return E_NOTIMPL;
680 HRESULT __cdecl SchRpcGetTaskInfo(const WCHAR *path, DWORD flags, DWORD *enabled, DWORD *task_state)
682 WCHAR *full_name, *xml;
683 HRESULT hr;
685 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path), flags, enabled, task_state);
687 full_name = get_full_name(path, NULL);
688 if (!full_name) return E_OUTOFMEMORY;
690 hr = read_xml(full_name, &xml);
691 heap_free(full_name);
692 if (hr != S_OK) return hr;
693 heap_free(xml);
695 *enabled = 0;
696 *task_state = (flags & SCH_FLAG_STATE) ? TASK_STATE_DISABLED : TASK_STATE_UNKNOWN;
697 return S_OK;
700 HRESULT __cdecl SchRpcGetNumberOfMissedRuns(const WCHAR *path, DWORD *runs)
702 FIXME("%s,%p: stub\n", debugstr_w(path), runs);
703 return E_NOTIMPL;
706 HRESULT __cdecl SchRpcEnableTask(const WCHAR *path, DWORD enabled)
708 FIXME("%s,%u: stub\n", debugstr_w(path), enabled);
709 return E_NOTIMPL;