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
+= lstrlenW(tasksW
) + lstrlenW(path
);
51 target
= heap_alloc(len
* sizeof(WCHAR
));
54 GetSystemDirectoryW(target
, len
);
55 lstrcatW(target
, tasksW
);
57 *relative_path
= target
+ lstrlenW(target
) - 1;
58 while (*path
== '\\') path
++;
59 lstrcatW(target
, path
);
65 * Recursively create all directories in the path.
67 static HRESULT
create_directory(const WCHAR
*path
)
73 new_path
= heap_alloc((lstrlenW(path
) + 1) * sizeof(WCHAR
));
74 if (!new_path
) return E_OUTOFMEMORY
;
76 lstrcpyW(new_path
, path
);
78 len
= lstrlenW(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
= wcsrchr(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 (wcschr(path
, '\\') || wcschr(path
, '/'))
189 WCHAR
*p
= wcsrchr(full_name
, '/');
190 if (!p
) p
= wcsrchr(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
)
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 buff
= 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
));
305 MultiByteToWideChar(cp
, 0, src
, -1, *xml
, size
);
313 HRESULT __cdecl
SchRpcRetrieveTask(const WCHAR
*path
, const WCHAR
*languages
, ULONG
*n_languages
, WCHAR
**xml
)
318 TRACE("%s,%s,%p,%p\n", debugstr_w(path
), debugstr_w(languages
), n_languages
, xml
);
320 full_name
= get_full_name(path
, NULL
);
321 if (!full_name
) return E_OUTOFMEMORY
;
323 hr
= read_xml(full_name
, xml
);
324 if (hr
!= S_OK
) *xml
= NULL
;
326 heap_free(full_name
);
330 HRESULT __cdecl
SchRpcCreateFolder(const WCHAR
*path
, const WCHAR
*sddl
, DWORD flags
)
335 TRACE("%s,%s,%#x\n", debugstr_w(path
), debugstr_w(sddl
), flags
);
337 if (flags
) return E_INVALIDARG
;
339 full_name
= get_full_name(path
, NULL
);
340 if (!full_name
) return E_OUTOFMEMORY
;
342 hr
= create_directory(full_name
);
344 heap_free(full_name
);
348 HRESULT __cdecl
SchRpcSetSecurity(const WCHAR
*path
, const WCHAR
*sddl
, DWORD flags
)
350 FIXME("%s,%s,%#x: stub\n", debugstr_w(path
), debugstr_w(sddl
), flags
);
354 HRESULT __cdecl
SchRpcGetSecurity(const WCHAR
*path
, DWORD flags
, WCHAR
**sddl
)
356 FIXME("%s,%#x,%p: stub\n", debugstr_w(path
), flags
, sddl
);
360 static void free_list(TASK_NAMES list
, LONG count
)
364 for (i
= 0; i
< count
; i
++)
370 static inline BOOL
is_directory(const WIN32_FIND_DATAW
*data
)
372 if (data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
374 if (data
->cFileName
[0] == '.')
376 if (!data
->cFileName
[1] || (data
->cFileName
[1] == '.' && !data
->cFileName
[2]))
384 static inline BOOL
is_file(const WIN32_FIND_DATAW
*data
)
386 return !(data
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
389 HRESULT __cdecl
SchRpcEnumFolders(const WCHAR
*path
, DWORD flags
, DWORD
*start_index
, DWORD n_requested
,
390 DWORD
*n_names
, TASK_NAMES
*names
)
392 static const WCHAR allW
[] = {'\\','*',0};
395 WCHAR pathW
[MAX_PATH
];
396 WIN32_FIND_DATAW data
;
398 DWORD allocated
, count
, index
;
401 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
406 if (flags
& ~TASK_ENUM_HIDDEN
) return E_INVALIDARG
;
408 if (!n_requested
) n_requested
= ~0u;
410 full_name
= get_full_name(path
, NULL
);
411 if (!full_name
) return E_OUTOFMEMORY
;
413 if (lstrlenW(full_name
) + 2 > MAX_PATH
)
415 heap_free(full_name
);
416 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
419 lstrcpyW(pathW
, full_name
);
420 lstrcatW(pathW
, allW
);
422 heap_free(full_name
);
425 list
= heap_alloc(allocated
* sizeof(list
[0]));
426 if (!list
) return E_OUTOFMEMORY
;
430 handle
= FindFirstFileW(pathW
, &data
);
431 if (handle
== INVALID_HANDLE_VALUE
)
434 if (GetLastError() == ERROR_PATH_NOT_FOUND
)
435 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
436 return HRESULT_FROM_WIN32(GetLastError());
441 if (is_directory(&data
) && index
++ >= *start_index
)
443 if (count
>= allocated
)
447 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
456 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
458 list
[count
] = heap_strdupW(data
.cFileName
);
467 if (count
>= n_requested
)
473 } while (FindNextFileW(handle
, &data
));
479 free_list(list
, count
);
488 *start_index
= index
;
494 return *start_index
? S_FALSE
: S_OK
;
497 HRESULT __cdecl
SchRpcEnumTasks(const WCHAR
*path
, DWORD flags
, DWORD
*start_index
, DWORD n_requested
,
498 DWORD
*n_names
, TASK_NAMES
*names
)
500 static const WCHAR allW
[] = {'\\','*',0};
503 WCHAR pathW
[MAX_PATH
];
504 WIN32_FIND_DATAW data
;
506 DWORD allocated
, count
, index
;
509 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
514 if (flags
& ~TASK_ENUM_HIDDEN
) return E_INVALIDARG
;
516 if (!n_requested
) n_requested
= ~0u;
518 full_name
= get_full_name(path
, NULL
);
519 if (!full_name
) return E_OUTOFMEMORY
;
521 if (lstrlenW(full_name
) + 2 > MAX_PATH
)
523 heap_free(full_name
);
524 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
527 lstrcpyW(pathW
, full_name
);
528 lstrcatW(pathW
, allW
);
530 heap_free(full_name
);
533 list
= heap_alloc(allocated
* sizeof(list
[0]));
534 if (!list
) return E_OUTOFMEMORY
;
538 handle
= FindFirstFileW(pathW
, &data
);
539 if (handle
== INVALID_HANDLE_VALUE
)
542 if (GetLastError() == ERROR_PATH_NOT_FOUND
)
543 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
544 return HRESULT_FROM_WIN32(GetLastError());
549 if (is_file(&data
) && index
++ >= *start_index
)
551 if (count
>= allocated
)
555 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
564 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
566 list
[count
] = heap_strdupW(data
.cFileName
);
575 if (count
>= n_requested
)
581 } while (FindNextFileW(handle
, &data
));
587 free_list(list
, count
);
596 *start_index
= index
;
602 return *start_index
? S_FALSE
: S_OK
;
605 HRESULT __cdecl
SchRpcEnumInstances(const WCHAR
*path
, DWORD flags
, DWORD
*n_guids
, GUID
**guids
)
607 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path
), flags
, n_guids
, guids
);
611 HRESULT __cdecl
SchRpcGetInstanceInfo(GUID guid
, WCHAR
**path
, DWORD
*task_state
, WCHAR
**action
,
612 WCHAR
**info
, DWORD
*n_instances
, GUID
**instances
, DWORD
*pid
)
614 FIXME("%s,%p,%p,%p,%p,%p,%p,%p: stub\n", wine_dbgstr_guid(&guid
), path
, task_state
, action
,
615 info
, n_instances
, instances
, pid
);
619 HRESULT __cdecl
SchRpcStopInstance(GUID guid
, DWORD flags
)
621 FIXME("%s,%#x: stub\n", wine_dbgstr_guid(&guid
), flags
);
625 HRESULT __cdecl
SchRpcStop(const WCHAR
*path
, DWORD flags
)
627 FIXME("%s,%#x: stub\n", debugstr_w(path
), flags
);
631 HRESULT __cdecl
SchRpcRun(const WCHAR
*path
, DWORD n_args
, const WCHAR
**args
, DWORD flags
,
632 DWORD session_id
, const WCHAR
*user
, GUID
*guid
)
634 FIXME("%s,%u,%p,%#x,%#x,%s,%p: stub\n", debugstr_w(path
), n_args
, args
, flags
,
635 session_id
, debugstr_w(user
), guid
);
639 HRESULT __cdecl
SchRpcDelete(const WCHAR
*path
, DWORD flags
)
644 TRACE("%s,%#x\n", debugstr_w(path
), flags
);
646 if (flags
) return E_INVALIDARG
;
648 while (*path
== '\\' || *path
== '/') path
++;
649 if (!*path
) return E_ACCESSDENIED
;
651 full_name
= get_full_name(path
, NULL
);
652 if (!full_name
) return E_OUTOFMEMORY
;
654 if (!RemoveDirectoryW(full_name
))
656 hr
= HRESULT_FROM_WIN32(GetLastError());
657 if (hr
== HRESULT_FROM_WIN32(ERROR_DIRECTORY
))
658 hr
= DeleteFileW(full_name
) ? S_OK
: HRESULT_FROM_WIN32(GetLastError());
661 heap_free(full_name
);
665 HRESULT __cdecl
SchRpcRename(const WCHAR
*path
, const WCHAR
*name
, DWORD flags
)
667 FIXME("%s,%s,%#x: stub\n", debugstr_w(path
), debugstr_w(name
), flags
);
671 HRESULT __cdecl
SchRpcScheduledRuntimes(const WCHAR
*path
, SYSTEMTIME
*start
, SYSTEMTIME
*end
, DWORD flags
,
672 DWORD n_requested
, DWORD
*n_runtimes
, SYSTEMTIME
**runtimes
)
674 FIXME("%s,%p,%p,%#x,%u,%p,%p: stub\n", debugstr_w(path
), start
, end
, flags
,
675 n_requested
, n_runtimes
, runtimes
);
679 HRESULT __cdecl
SchRpcGetLastRunInfo(const WCHAR
*path
, SYSTEMTIME
*last_runtime
, DWORD
*last_return_code
)
681 FIXME("%s,%p,%p: stub\n", debugstr_w(path
), last_runtime
, last_return_code
);
685 HRESULT __cdecl
SchRpcGetTaskInfo(const WCHAR
*path
, DWORD flags
, DWORD
*enabled
, DWORD
*task_state
)
687 WCHAR
*full_name
, *xml
;
690 FIXME("%s,%#x,%p,%p: stub\n", debugstr_w(path
), flags
, enabled
, task_state
);
692 full_name
= get_full_name(path
, NULL
);
693 if (!full_name
) return E_OUTOFMEMORY
;
695 hr
= read_xml(full_name
, &xml
);
696 heap_free(full_name
);
697 if (hr
!= S_OK
) return hr
;
701 *task_state
= (flags
& SCH_FLAG_STATE
) ? TASK_STATE_DISABLED
: TASK_STATE_UNKNOWN
;
705 HRESULT __cdecl
SchRpcGetNumberOfMissedRuns(const WCHAR
*path
, DWORD
*runs
)
707 FIXME("%s,%p: stub\n", debugstr_w(path
), runs
);
711 HRESULT __cdecl
SchRpcEnableTask(const WCHAR
*path
, DWORD enabled
)
713 FIXME("%s,%u: stub\n", debugstr_w(path
), enabled
);