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_PATH_NOT_FOUND
|| !(slash
= strrchrW(new_path
, '\\')))
92 hr
= HRESULT_FROM_WIN32(last_error
);
96 len
= slash
- new_path
;
98 hr
= create_directory(new_path
);
99 if (hr
!= S_OK
) break;
100 new_path
[len
] = '\\';
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";
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
);
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());
138 if (!WriteFile(hfile
, comment
, strlen(comment
), &size
, NULL
))
140 hr
= HRESULT_FROM_WIN32(GetLastError());
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
++;
150 if (!WriteFile(hfile
, p
, strlen(p
), &size
, NULL
))
151 hr
= HRESULT_FROM_WIN32(GetLastError());
155 if (!WriteFile(hfile
, xml
, strlen(xml
), &size
, NULL
))
156 hr
= HRESULT_FROM_WIN32(GetLastError());
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
;
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
);
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
;
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
, '\\');
192 hr
= create_directory(full_name
);
193 if (hr
!= S_OK
&& hr
!= HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS
))
195 heap_free(full_name
);
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 '\' */
215 switch (flags
& (TASK_CREATE
| TASK_UPDATE
))
219 disposition
= CREATE_NEW
;
223 disposition
= OPEN_EXISTING
;
226 case (TASK_CREATE
| TASK_UPDATE
):
227 disposition
= OPEN_ALWAYS
;
231 hr
= write_xml_utf8(full_name
, disposition
, xml
);
234 *actual_path
= heap_strdupW(relative_path
);
235 schedsvc_auto_start();
238 heap_free(full_name
);
242 static int detect_encoding(const void *buffer
, DWORD size
)
244 if (size
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
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
)
254 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
260 static HRESULT
read_xml(const WCHAR
*name
, WCHAR
**xml
)
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);
282 return E_OUTOFMEMORY
;
288 ReadFile(hfile
, src
, size
, &size
, NULL
);
291 cp
= detect_encoding(src
, size
);
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
);
308 HRESULT __cdecl
SchRpcRetrieveTask(const WCHAR
*path
, const WCHAR
*languages
, ULONG
*n_languages
, WCHAR
**xml
)
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
);
325 HRESULT __cdecl
SchRpcCreateFolder(const WCHAR
*path
, const WCHAR
*sddl
, DWORD flags
)
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
);
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
);
349 HRESULT __cdecl
SchRpcGetSecurity(const WCHAR
*path
, DWORD flags
, WCHAR
**sddl
)
351 FIXME("%s,%#x,%p: stub\n", debugstr_w(path
), flags
, sddl
);
355 static void free_list(TASK_NAMES list
, LONG count
)
359 for (i
= 0; i
< count
; i
++)
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]))
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};
390 WCHAR pathW
[MAX_PATH
];
391 WIN32_FIND_DATAW data
;
393 DWORD allocated
, count
, index
;
396 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
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
);
420 list
= heap_alloc(allocated
* sizeof(list
[0]));
421 if (!list
) return E_OUTOFMEMORY
;
425 handle
= FindFirstFileW(pathW
, &data
);
426 if (handle
== INVALID_HANDLE_VALUE
)
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
)
442 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
451 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
453 list
[count
] = heap_strdupW(data
.cFileName
);
462 if (count
>= n_requested
)
468 } while (FindNextFileW(handle
, &data
));
474 free_list(list
, count
);
483 *start_index
= index
;
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};
498 WCHAR pathW
[MAX_PATH
];
499 WIN32_FIND_DATAW data
;
501 DWORD allocated
, count
, index
;
504 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
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
);
528 list
= heap_alloc(allocated
* sizeof(list
[0]));
529 if (!list
) return E_OUTOFMEMORY
;
533 handle
= FindFirstFileW(pathW
, &data
);
534 if (handle
== INVALID_HANDLE_VALUE
)
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
)
550 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
559 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
561 list
[count
] = heap_strdupW(data
.cFileName
);
570 if (count
>= n_requested
)
576 } while (FindNextFileW(handle
, &data
));
582 free_list(list
, count
);
591 *start_index
= index
;
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
);
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
);
614 HRESULT __cdecl
SchRpcStopInstance(GUID guid
, DWORD flags
)
616 FIXME("%s,%#x: stub\n", wine_dbgstr_guid(&guid
), flags
);
620 HRESULT __cdecl
SchRpcStop(const WCHAR
*path
, DWORD flags
)
622 FIXME("%s,%#x: stub\n", debugstr_w(path
), flags
);
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
);
634 HRESULT __cdecl
SchRpcDelete(const WCHAR
*path
, DWORD flags
)
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
);
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
);
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
);
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
);
680 HRESULT __cdecl
SchRpcGetTaskInfo(const WCHAR
*path
, DWORD flags
, DWORD
*enabled
, DWORD
*task_state
)
682 WCHAR
*full_name
, *xml
;
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
;
696 *task_state
= (flags
& SCH_FLAG_STATE
) ? TASK_STATE_DISABLED
: TASK_STATE_UNKNOWN
;
700 HRESULT __cdecl
SchRpcGetNumberOfMissedRuns(const WCHAR
*path
, DWORD
*runs
)
702 FIXME("%s,%p: stub\n", debugstr_w(path
), runs
);
706 HRESULT __cdecl
SchRpcEnableTask(const WCHAR
*path
, DWORD enabled
)
708 FIXME("%s,%u: stub\n", debugstr_w(path
), enabled
);