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
;
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
, '\\');
191 hr
= create_directory(full_name
);
192 if (hr
!= S_OK
&& hr
!= HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS
))
194 heap_free(full_name
);
200 switch (flags
& (TASK_CREATE
| TASK_UPDATE
))
204 disposition
= CREATE_NEW
;
208 disposition
= OPEN_EXISTING
;
211 case (TASK_CREATE
| TASK_UPDATE
):
212 disposition
= OPEN_ALWAYS
;
216 hr
= write_xml_utf8(full_name
, disposition
, xml
);
219 *actual_path
= heap_strdupW(relative_path
);
220 schedsvc_auto_start();
223 heap_free(full_name
);
227 static int detect_encoding(const void *buffer
, DWORD size
)
229 if (size
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
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
)
239 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
245 static HRESULT
read_xml(const WCHAR
*name
, WCHAR
**xml
)
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);
267 return E_OUTOFMEMORY
;
273 ReadFile(hfile
, src
, size
, &size
, NULL
);
276 cp
= detect_encoding(src
, size
);
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
);
293 HRESULT __cdecl
SchRpcRetrieveTask(const WCHAR
*path
, const WCHAR
*languages
, ULONG
*n_languages
, WCHAR
**xml
)
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
);
310 HRESULT __cdecl
SchRpcCreateFolder(const WCHAR
*path
, const WCHAR
*sddl
, DWORD flags
)
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
);
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
);
334 HRESULT __cdecl
SchRpcGetSecurity(const WCHAR
*path
, DWORD flags
, WCHAR
**sddl
)
336 FIXME("%s,%#x,%p: stub\n", debugstr_w(path
), flags
, sddl
);
340 static void free_list(TASK_NAMES list
, LONG count
)
344 for (i
= 0; i
< count
; i
++)
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]))
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};
375 WCHAR pathW
[MAX_PATH
];
376 WIN32_FIND_DATAW data
;
378 DWORD allocated
, count
, index
;
381 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
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
);
405 list
= heap_alloc(allocated
* sizeof(list
[0]));
406 if (!list
) return E_OUTOFMEMORY
;
410 handle
= FindFirstFileW(pathW
, &data
);
411 if (handle
== INVALID_HANDLE_VALUE
)
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
)
427 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
436 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
438 list
[count
] = heap_strdupW(data
.cFileName
);
447 if (count
>= n_requested
)
453 } while (FindNextFileW(handle
, &data
));
459 free_list(list
, count
);
468 *start_index
= index
;
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};
483 WCHAR pathW
[MAX_PATH
];
484 WIN32_FIND_DATAW data
;
486 DWORD allocated
, count
, index
;
489 TRACE("%s,%#x,%u,%u,%p,%p\n", debugstr_w(path
), flags
, *start_index
, n_requested
, n_names
, names
);
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
);
513 list
= heap_alloc(allocated
* sizeof(list
[0]));
514 if (!list
) return E_OUTOFMEMORY
;
518 handle
= FindFirstFileW(pathW
, &data
);
519 if (handle
== INVALID_HANDLE_VALUE
)
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
)
535 new_list
= heap_realloc(list
, allocated
* sizeof(list
[0]));
544 TRACE("adding %s\n", debugstr_w(data
.cFileName
));
546 list
[count
] = heap_strdupW(data
.cFileName
);
555 if (count
>= n_requested
)
561 } while (FindNextFileW(handle
, &data
));
567 free_list(list
, count
);
576 *start_index
= index
;
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
);
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
);
599 HRESULT __cdecl
SchRpcStopInstance(GUID guid
, DWORD flags
)
601 FIXME("%s,%#x: stub\n", wine_dbgstr_guid(&guid
), flags
);
605 HRESULT __cdecl
SchRpcStop(const WCHAR
*path
, DWORD flags
)
607 FIXME("%s,%#x: stub\n", debugstr_w(path
), flags
);
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
);
619 HRESULT __cdecl
SchRpcDelete(const WCHAR
*path
, DWORD flags
)
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
);
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
);
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
);
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
);
665 HRESULT __cdecl
SchRpcGetTaskInfo(const WCHAR
*path
, DWORD flags
, DWORD
*enabled
, DWORD
*task_state
)
667 WCHAR
*full_name
, *xml
;
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
;
681 *task_state
= (flags
& SCH_FLAG_STATE
) ? TASK_STATE_DISABLED
: TASK_STATE_UNKNOWN
;
685 HRESULT __cdecl
SchRpcGetNumberOfMissedRuns(const WCHAR
*path
, DWORD
*runs
)
687 FIXME("%s,%p: stub\n", debugstr_w(path
), runs
);
691 HRESULT __cdecl
SchRpcEnableTask(const WCHAR
*path
, DWORD enabled
)
693 FIXME("%s,%u: stub\n", debugstr_w(path
), enabled
);