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
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);
42 static WCHAR
*get_full_name(const WCHAR
*path
, WCHAR
**relative_path
)
44 static const WCHAR tasksW
[] = { '\\','t','a','s','k','s','\\',0 };
48 len
= GetSystemDirectoryW(NULL
, 0);
49 len
+= strlenW(tasksW
) + strlenW(path
);
51 target
= heap_alloc(len
* sizeof(WCHAR
));
54 GetSystemDirectoryW(target
, len
);
55 strcatW(target
, tasksW
);
57 *relative_path
= target
+ strlenW(target
) - 1;
58 while (*path
== '\\') path
++;
59 strcatW(target
, path
);
65 * Recursively create all directories in the path.
67 static HRESULT
create_directory(const WCHAR
*path
)
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;
85 while (!CreateDirectoryW(new_path
, NULL
))
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
);
97 len
= slash
- new_path
;
99 hr
= create_directory(new_path
);
100 if (hr
!= S_OK
) break;
101 new_path
[len
] = '\\';
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";
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
);
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());
139 if (!WriteFile(hfile
, comment
, strlen(comment
), &size
, NULL
))
141 hr
= HRESULT_FROM_WIN32(GetLastError());
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
++;
151 if (!WriteFile(hfile
, p
, strlen(p
), &size
, NULL
))
152 hr
= HRESULT_FROM_WIN32(GetLastError());
156 if (!WriteFile(hfile
, xml
, strlen(xml
), &size
, NULL
))
157 hr
= HRESULT_FROM_WIN32(GetLastError());
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
;
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
);
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
;
185 full_name
= get_full_name(path
, &relative_path
);
186 if (!full_name
) return E_OUTOFMEMORY
;
188 if (strchrW(path
, '\\') || strchrW(path
, '/'))
190 WCHAR
*p
= strrchrW(full_name
, '/');
191 if (!p
) p
= strrchrW(full_name
, '\\');
193 hr
= create_directory(full_name
);
194 if (hr
!= S_OK
&& hr
!= HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS
))
196 heap_free(full_name
);
208 StringFromGUID2(&iid
, uuid_str
, 39);
210 full_name
= get_full_name(uuid_str
, &relative_path
);
211 if (!full_name
) return E_OUTOFMEMORY
;
212 /* skip leading '\' */
216 switch (flags
& (TASK_CREATE
| TASK_UPDATE
))
220 disposition
= CREATE_NEW
;
224 disposition
= OPEN_EXISTING
;
227 case (TASK_CREATE
| TASK_UPDATE
):
228 disposition
= OPEN_ALWAYS
;
232 hr
= write_xml_utf8(full_name
, disposition
, xml
);
235 *actual_path
= heap_strdupW(relative_path
);
236 schedsvc_auto_start();
239 heap_free(full_name
);
243 static int detect_encoding(const void *buffer
, DWORD size
)
245 if (size
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
249 int flags
= IS_TEXT_UNICODE_SIGNATURE
|
250 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
251 IS_TEXT_UNICODE_ODD_LENGTH
;
252 IsTextUnicode(buffer
, size
, &flags
);
253 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
255 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
261 static HRESULT
read_xml(const WCHAR
*name
, WCHAR
**xml
)
268 attrs
= GetFileAttributesW(name
);
269 if (attrs
== INVALID_FILE_ATTRIBUTES
)
270 return HRESULT_FROM_WIN32(GetLastError());
271 if (attrs
& FILE_ATTRIBUTE_DIRECTORY
)
272 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
);
274 hfile
= CreateFileW(name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
275 if (hfile
== INVALID_HANDLE_VALUE
)
276 return HRESULT_FROM_WIN32(GetLastError());
278 size
= GetFileSize(hfile
, NULL
);
279 src
= heap_alloc(size
+ 2);
283 return E_OUTOFMEMORY
;
289 ReadFile(hfile
, src
, size
, &size
, NULL
);
292 cp
= detect_encoding(src
, size
);
299 if (cp
== CP_UTF8
&& size
>= sizeof(bom_utf8
) && !memcmp(src
, bom_utf8
, sizeof(bom_utf8
)))
300 src
+= sizeof(bom_utf8
);
302 size
= MultiByteToWideChar(cp
, 0, src
, -1, NULL
, 0);
303 *xml
= heap_alloc(size
* sizeof(WCHAR
));
304 if (!*xml
) return E_OUTOFMEMORY
;
305 MultiByteToWideChar(cp
, 0, src
, -1, *xml
, size
);
309 HRESULT __cdecl
SchRpcRetrieveTask(const WCHAR
*path
, const WCHAR
*languages
, ULONG
*n_languages
, WCHAR
**xml
)
314 TRACE("%s,%s,%p,%p\n", debugstr_w(path
), debugstr_w(languages
), n_languages
, xml
);
316 full_name
= get_full_name(path
, NULL
);
317 if (!full_name
) return E_OUTOFMEMORY
;
319 hr
= read_xml(full_name
, xml
);
320 if (hr
!= S_OK
) *xml
= NULL
;
322 heap_free(full_name
);
326 HRESULT __cdecl
SchRpcCreateFolder(const WCHAR
*path
, const WCHAR
*sddl
, DWORD flags
)
331 TRACE("%s,%s,%#x\n", debugstr_w(path
), debugstr_w(sddl
), flags
);
333 if (flags
) return E_INVALIDARG
;
335 full_name
= get_full_name(path
, NULL
);
336 if (!full_name
) return E_OUTOFMEMORY
;
338 hr
= create_directory(full_name
);
340 heap_free(full_name
);
344 HRESULT __cdecl
SchRpcSetSecurity(const WCHAR
*path
, const WCHAR
*sddl
, DWORD flags
)
346 FIXME("%s,%s,%#x: stub\n", debugstr_w(path
), debugstr_w(sddl
), flags
);
350 HRESULT __cdecl
SchRpcGetSecurity(const WCHAR
*path
, DWORD flags
, WCHAR
**sddl
)
352 FIXME("%s,%#x,%p: stub\n", debugstr_w(path
), flags
, sddl
);
356 static void free_list(TASK_NAMES list
, LONG count
)
360 for (i
= 0; i
< count
; i
++)
366 static inline BOOL
is_directory(const WIN32_FIND_DATAW
*data
)
368 if (data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
370 if (data
->cFileName
[0] == '.')
372 if (!data
->cFileName
[1] || (data
->cFileName
[1] == '.' && !data
->cFileName
[2]))
380 static inline BOOL
is_file(const WIN32_FIND_DATAW
*data
)
382 return !(data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
385 HRESULT __cdecl
SchRpcEnumFolders(const WCHAR
*path
, DWORD flags
, DWORD
*start_index
, DWORD n_requested
,
386 DWORD
*n_names
, TASK_NAMES
*names
)
388 static const WCHAR allW
[] = {'\\','*',0};
391 WCHAR pathW
[MAX_PATH
];
392 WIN32_FIND_DATAW data
;
394 DWORD allocated
, count
, index
;
397 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
402 if (flags
& ~TASK_ENUM_HIDDEN
) return E_INVALIDARG
;
404 if (!n_requested
) n_requested
= ~0u;
406 full_name
= get_full_name(path
, NULL
);
407 if (!full_name
) return E_OUTOFMEMORY
;
409 if (strlenW(full_name
) + 2 > MAX_PATH
)
411 heap_free(full_name
);
412 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
415 strcpyW(pathW
, full_name
);
416 strcatW(pathW
, allW
);
418 heap_free(full_name
);
421 list
= heap_alloc(allocated
* sizeof(list
[0]));
422 if (!list
) return E_OUTOFMEMORY
;
426 handle
= FindFirstFileW(pathW
, &data
);
427 if (handle
== INVALID_HANDLE_VALUE
)
430 if (GetLastError() == ERROR_PATH_NOT_FOUND
)
431 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
432 return HRESULT_FROM_WIN32(GetLastError());
437 if (is_directory(&data
) && index
++ >= *start_index
)
439 if (count
>= allocated
)
443 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
452 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
454 list
[count
] = heap_strdupW(data
.cFileName
);
463 if (count
>= n_requested
)
469 } while (FindNextFileW(handle
, &data
));
475 free_list(list
, count
);
484 *start_index
= index
;
490 return *start_index
? S_FALSE
: S_OK
;
493 HRESULT __cdecl
SchRpcEnumTasks(const WCHAR
*path
, DWORD flags
, DWORD
*start_index
, DWORD n_requested
,
494 DWORD
*n_names
, TASK_NAMES
*names
)
496 static const WCHAR allW
[] = {'\\','*',0};
499 WCHAR pathW
[MAX_PATH
];
500 WIN32_FIND_DATAW data
;
502 DWORD allocated
, count
, index
;
505 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
510 if (flags
& ~TASK_ENUM_HIDDEN
) return E_INVALIDARG
;
512 if (!n_requested
) n_requested
= ~0u;
514 full_name
= get_full_name(path
, NULL
);
515 if (!full_name
) return E_OUTOFMEMORY
;
517 if (strlenW(full_name
) + 2 > MAX_PATH
)
519 heap_free(full_name
);
520 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
523 strcpyW(pathW
, full_name
);
524 strcatW(pathW
, allW
);
526 heap_free(full_name
);
529 list
= heap_alloc(allocated
* sizeof(list
[0]));
530 if (!list
) return E_OUTOFMEMORY
;
534 handle
= FindFirstFileW(pathW
, &data
);
535 if (handle
== INVALID_HANDLE_VALUE
)
538 if (GetLastError() == ERROR_PATH_NOT_FOUND
)
539 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
540 return HRESULT_FROM_WIN32(GetLastError());
545 if (is_file(&data
) && index
++ >= *start_index
)
547 if (count
>= allocated
)
551 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
560 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
562 list
[count
] = heap_strdupW(data
.cFileName
);
571 if (count
>= n_requested
)
577 } while (FindNextFileW(handle
, &data
));
583 free_list(list
, count
);
592 *start_index
= index
;
598 return *start_index
? S_FALSE
: S_OK
;
601 HRESULT __cdecl
SchRpcEnumInstances(const WCHAR
*path
, DWORD flags
, DWORD
*n_guids
, GUID
**guids
)
603 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path
), flags
, n_guids
, guids
);
607 HRESULT __cdecl
SchRpcGetInstanceInfo(GUID guid
, WCHAR
**path
, DWORD
*task_state
, WCHAR
**action
,
608 WCHAR
**info
, DWORD
*n_instances
, GUID
**instances
, DWORD
*pid
)
610 FIXME("%s,%p,%p,%p,%p,%p,%p,%p: stub\n", wine_dbgstr_guid(&guid
), path
, task_state
, action
,
611 info
, n_instances
, instances
, pid
);
615 HRESULT __cdecl
SchRpcStopInstance(GUID guid
, DWORD flags
)
617 FIXME("%s,%#x: stub\n", wine_dbgstr_guid(&guid
), flags
);
621 HRESULT __cdecl
SchRpcStop(const WCHAR
*path
, DWORD flags
)
623 FIXME("%s,%#x: stub\n", debugstr_w(path
), flags
);
627 HRESULT __cdecl
SchRpcRun(const WCHAR
*path
, DWORD n_args
, const WCHAR
**args
, DWORD flags
,
628 DWORD session_id
, const WCHAR
*user
, GUID
*guid
)
630 FIXME("%s,%u,%p,%#x,%#x,%s,%p: stub\n", debugstr_w(path
), n_args
, args
, flags
,
631 session_id
, debugstr_w(user
), guid
);
635 HRESULT __cdecl
SchRpcDelete(const WCHAR
*path
, DWORD flags
)
640 TRACE("%s,%#x\n", debugstr_w(path
), flags
);
642 if (flags
) return E_INVALIDARG
;
644 while (*path
== '\\' || *path
== '/') path
++;
645 if (!*path
) return E_ACCESSDENIED
;
647 full_name
= get_full_name(path
, NULL
);
648 if (!full_name
) return E_OUTOFMEMORY
;
650 if (!RemoveDirectoryW(full_name
))
652 hr
= HRESULT_FROM_WIN32(GetLastError());
653 if (hr
== HRESULT_FROM_WIN32(ERROR_DIRECTORY
))
654 hr
= DeleteFileW(full_name
) ? S_OK
: HRESULT_FROM_WIN32(GetLastError());
657 heap_free(full_name
);
661 HRESULT __cdecl
SchRpcRename(const WCHAR
*path
, const WCHAR
*name
, DWORD flags
)
663 FIXME("%s,%s,%#x: stub\n", debugstr_w(path
), debugstr_w(name
), flags
);
667 HRESULT __cdecl
SchRpcScheduledRuntimes(const WCHAR
*path
, SYSTEMTIME
*start
, SYSTEMTIME
*end
, DWORD flags
,
668 DWORD n_requested
, DWORD
*n_runtimes
, SYSTEMTIME
**runtimes
)
670 FIXME("%s,%p,%p,%#x,%u,%p,%p: stub\n", debugstr_w(path
), start
, end
, flags
,
671 n_requested
, n_runtimes
, runtimes
);
675 HRESULT __cdecl
SchRpcGetLastRunInfo(const WCHAR
*path
, SYSTEMTIME
*last_runtime
, DWORD
*last_return_code
)
677 FIXME("%s,%p,%p: stub\n", debugstr_w(path
), last_runtime
, last_return_code
);
681 HRESULT __cdecl
SchRpcGetTaskInfo(const WCHAR
*path
, DWORD flags
, DWORD
*enabled
, DWORD
*task_state
)
683 WCHAR
*full_name
, *xml
;
686 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path
), flags
, enabled
, task_state
);
688 full_name
= get_full_name(path
, NULL
);
689 if (!full_name
) return E_OUTOFMEMORY
;
691 hr
= read_xml(full_name
, &xml
);
692 heap_free(full_name
);
693 if (hr
!= S_OK
) return hr
;
697 *task_state
= (flags
& SCH_FLAG_STATE
) ? TASK_STATE_DISABLED
: TASK_STATE_UNKNOWN
;
701 HRESULT __cdecl
SchRpcGetNumberOfMissedRuns(const WCHAR
*path
, DWORD
*runs
)
703 FIXME("%s,%p: stub\n", debugstr_w(path
), runs
);
707 HRESULT __cdecl
SchRpcEnableTask(const WCHAR
*path
, DWORD enabled
)
709 FIXME("%s,%u: stub\n", debugstr_w(path
), enabled
);