| /* |
| * Copyright 2014 Dmitry Timoshkov |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| #include "objbase.h" |
| #include "taskschd.h" |
| #include "schrpc.h" |
| #include "taskschd_private.h" |
| |
| #include "wine/unicode.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(taskschd); |
| |
| typedef struct |
| { |
| ITaskFolder ITaskFolder_iface; |
| LONG ref; |
| WCHAR *path; |
| } TaskFolder; |
| |
| static inline TaskFolder *impl_from_ITaskFolder(ITaskFolder *iface) |
| { |
| return CONTAINING_RECORD(iface, TaskFolder, ITaskFolder_iface); |
| } |
| |
| static ULONG WINAPI TaskFolder_AddRef(ITaskFolder *iface) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| return InterlockedIncrement(&folder->ref); |
| } |
| |
| static ULONG WINAPI TaskFolder_Release(ITaskFolder *iface) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| LONG ref = InterlockedDecrement(&folder->ref); |
| |
| if (!ref) |
| { |
| TRACE("destroying %p\n", iface); |
| heap_free(folder->path); |
| heap_free(folder); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI TaskFolder_QueryInterface(ITaskFolder *iface, REFIID riid, void **obj) |
| { |
| if (!riid || !obj) return E_INVALIDARG; |
| |
| TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), obj); |
| |
| if (IsEqualGUID(riid, &IID_ITaskFolder) || |
| IsEqualGUID(riid, &IID_IDispatch) || |
| IsEqualGUID(riid, &IID_IUnknown)) |
| { |
| ITaskFolder_AddRef(iface); |
| *obj = iface; |
| return S_OK; |
| } |
| |
| FIXME("interface %s is not implemented\n", debugstr_guid(riid)); |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetTypeInfoCount(ITaskFolder *iface, UINT *count) |
| { |
| FIXME("%p,%p: stub\n", iface, count); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetTypeInfo(ITaskFolder *iface, UINT index, LCID lcid, ITypeInfo **info) |
| { |
| FIXME("%p,%u,%u,%p: stub\n", iface, index, lcid, info); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetIDsOfNames(ITaskFolder *iface, REFIID riid, LPOLESTR *names, |
| UINT count, LCID lcid, DISPID *dispid) |
| { |
| FIXME("%p,%s,%p,%u,%u,%p: stub\n", iface, debugstr_guid(riid), names, count, lcid, dispid); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TaskFolder_Invoke(ITaskFolder *iface, DISPID dispid, REFIID riid, LCID lcid, WORD flags, |
| DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *argerr) |
| { |
| FIXME("%p,%d,%s,%04x,%04x,%p,%p,%p,%p: stub\n", iface, dispid, debugstr_guid(riid), lcid, flags, |
| params, result, excepinfo, argerr); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TaskFolder_get_Name(ITaskFolder *iface, BSTR *name) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| const WCHAR *p_name; |
| |
| TRACE("%p,%p\n", iface, name); |
| |
| if (!name) return E_POINTER; |
| |
| p_name = strrchrW(folder->path, '\\'); |
| if (!p_name) |
| p_name = folder->path; |
| else |
| if (p_name[1] != 0) p_name++; |
| |
| *name = SysAllocString(p_name); |
| if (!*name) return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TaskFolder_get_Path(ITaskFolder *iface, BSTR *path) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| |
| TRACE("%p,%p\n", iface, path); |
| |
| if (!path) return E_POINTER; |
| |
| *path = SysAllocString(folder->path); |
| if (!*path) return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetFolder(ITaskFolder *iface, BSTR path, ITaskFolder **new_folder) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| |
| TRACE("%p,%s,%p\n", iface, debugstr_w(path), folder); |
| |
| if (!path) return E_INVALIDARG; |
| if (!new_folder) return E_POINTER; |
| |
| return TaskFolder_create(folder->path, path, new_folder, FALSE); |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetFolders(ITaskFolder *iface, LONG flags, ITaskFolderCollection **folders) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| |
| TRACE("%p,%x,%p: stub\n", iface, flags, folders); |
| |
| if (!folders) return E_POINTER; |
| |
| if (flags) |
| FIXME("unsupported flags %x\n", flags); |
| |
| return TaskFolderCollection_create(folder->path, folders); |
| } |
| |
| static inline BOOL is_variant_null(const VARIANT *var) |
| { |
| return V_VT(var) == VT_EMPTY || V_VT(var) == VT_NULL || |
| (V_VT(var) == VT_BSTR && (V_BSTR(var) == NULL || !*V_BSTR(var))); |
| } |
| |
| static HRESULT WINAPI TaskFolder_CreateFolder(ITaskFolder *iface, BSTR path, VARIANT sddl, ITaskFolder **new_folder) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| ITaskFolder *tmp_folder = NULL; |
| HRESULT hr; |
| |
| TRACE("%p,%s,%s,%p\n", iface, debugstr_w(path), debugstr_variant(&sddl), folder); |
| |
| if (!path) return E_INVALIDARG; |
| |
| if (!new_folder) new_folder = &tmp_folder; |
| |
| if (!is_variant_null(&sddl)) |
| FIXME("security descriptor %s is ignored\n", debugstr_variant(&sddl)); |
| |
| hr = TaskFolder_create(folder->path, path, new_folder, TRUE); |
| if (tmp_folder) |
| ITaskFolder_Release(tmp_folder); |
| |
| return hr; |
| } |
| |
| WCHAR *get_full_path(const WCHAR *parent, const WCHAR *path) |
| { |
| static const WCHAR bslash[] = { '\\', 0 }; |
| WCHAR *folder_path; |
| int len = 0; |
| |
| if (path) len = strlenW(path); |
| |
| if (parent) len += strlenW(parent); |
| |
| /* +1 if parent is not '\' terminated */ |
| folder_path = heap_alloc((len + 2) * sizeof(WCHAR)); |
| if (!folder_path) return NULL; |
| |
| folder_path[0] = 0; |
| |
| if (parent) |
| strcpyW(folder_path, parent); |
| |
| if (path && *path) |
| { |
| len = strlenW(folder_path); |
| if (!len || folder_path[len - 1] != '\\') |
| strcatW(folder_path, bslash); |
| |
| while (*path == '\\') path++; |
| strcatW(folder_path, path); |
| } |
| |
| len = strlenW(folder_path); |
| if (!len) |
| strcatW(folder_path, bslash); |
| |
| return folder_path; |
| } |
| |
| static HRESULT WINAPI TaskFolder_DeleteFolder(ITaskFolder *iface, BSTR name, LONG flags) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| WCHAR *folder_path; |
| HRESULT hr; |
| |
| TRACE("%p,%s,%x\n", iface, debugstr_w(name), flags); |
| |
| if (!name || !*name) return E_ACCESSDENIED; |
| |
| if (flags) |
| FIXME("unsupported flags %x\n", flags); |
| |
| folder_path = get_full_path(folder->path, name); |
| if (!folder_path) return E_OUTOFMEMORY; |
| |
| hr = SchRpcDelete(folder_path, 0); |
| heap_free(folder_path); |
| return hr; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetTask(ITaskFolder *iface, BSTR name, IRegisteredTask **task) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| ITaskDefinition *taskdef; |
| HRESULT hr; |
| |
| TRACE("%p,%s,%p\n", iface, debugstr_w(name), task); |
| |
| if (!task) return E_POINTER; |
| |
| hr = TaskDefinition_create(&taskdef); |
| if (hr != S_OK) return hr; |
| |
| hr = RegisteredTask_create(folder->path, name, taskdef, 0, 0, task, FALSE); |
| if (hr != S_OK) |
| ITaskDefinition_Release(taskdef); |
| return hr; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetTasks(ITaskFolder *iface, LONG flags, IRegisteredTaskCollection **tasks) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| |
| TRACE("%p,%x,%p: stub\n", iface, flags, tasks); |
| |
| if (!tasks) return E_POINTER; |
| |
| return RegisteredTaskCollection_create(folder->path, tasks); |
| } |
| |
| static HRESULT WINAPI TaskFolder_DeleteTask(ITaskFolder *iface, BSTR name, LONG flags) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| WCHAR *folder_path; |
| HRESULT hr; |
| |
| TRACE("%p,%s,%x\n", iface, debugstr_w(name), flags); |
| |
| if (!name || !*name) return E_ACCESSDENIED; |
| |
| if (flags) |
| FIXME("unsupported flags %x\n", flags); |
| |
| folder_path = get_full_path(folder->path, name); |
| if (!folder_path) return E_OUTOFMEMORY; |
| |
| hr = SchRpcDelete(folder_path, 0); |
| heap_free(folder_path); |
| return hr; |
| } |
| |
| static HRESULT WINAPI TaskFolder_RegisterTask(ITaskFolder *iface, BSTR name, BSTR xml, LONG flags, |
| VARIANT user, VARIANT password, TASK_LOGON_TYPE logon, |
| VARIANT sddl, IRegisteredTask **task) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| IRegisteredTask *regtask = NULL; |
| ITaskDefinition *taskdef; |
| HRESULT hr; |
| |
| TRACE("%p,%s,%s,%x,%s,%s,%d,%s,%p\n", iface, debugstr_w(name), debugstr_w(xml), flags, |
| debugstr_variant(&user), debugstr_variant(&password), logon, debugstr_variant(&sddl), task); |
| |
| if (!xml) return HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER); |
| |
| if (!task) task = ®task; |
| |
| hr = TaskDefinition_create(&taskdef); |
| if (hr != S_OK) return hr; |
| |
| hr = ITaskDefinition_put_XmlText(taskdef, xml); |
| if (hr == S_OK) |
| hr = RegisteredTask_create(folder->path, name, taskdef, flags, logon, task, TRUE); |
| |
| if (hr != S_OK) |
| ITaskDefinition_Release(taskdef); |
| |
| if (regtask) |
| IRegisteredTask_Release(regtask); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI TaskFolder_RegisterTaskDefinition(ITaskFolder *iface, BSTR name, ITaskDefinition *definition, LONG flags, |
| VARIANT user, VARIANT password, TASK_LOGON_TYPE logon, |
| VARIANT sddl, IRegisteredTask **task) |
| { |
| TaskFolder *folder = impl_from_ITaskFolder(iface); |
| IRegisteredTask *regtask = NULL; |
| HRESULT hr; |
| |
| FIXME("%p,%s,%p,%x,%s,%s,%d,%s,%p: stub\n", iface, debugstr_w(name), definition, flags, |
| debugstr_variant(&user), debugstr_variant(&password), logon, debugstr_variant(&sddl), task); |
| |
| if (!is_variant_null(&sddl)) |
| FIXME("security descriptor %s is ignored\n", debugstr_variant(&sddl)); |
| |
| if (!is_variant_null(&user) || !is_variant_null(&password)) |
| FIXME("user/password are ignored\n"); |
| |
| if (!task) task = ®task; |
| |
| ITaskDefinition_AddRef(definition); |
| hr = RegisteredTask_create(folder->path, name, definition, flags, logon, task, TRUE); |
| if (hr != S_OK) |
| ITaskDefinition_Release(definition); |
| |
| if (regtask) |
| IRegisteredTask_Release(regtask); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI TaskFolder_GetSecurityDescriptor(ITaskFolder *iface, LONG info, BSTR *sddl) |
| { |
| FIXME("%p,%x,%p: stub\n", iface, info, sddl); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TaskFolder_SetSecurityDescriptor(ITaskFolder *iface, BSTR sddl, LONG flags) |
| { |
| FIXME("%p,%s,%x: stub\n", iface, debugstr_w(sddl), flags); |
| return E_NOTIMPL; |
| } |
| |
| static const ITaskFolderVtbl TaskFolder_vtbl = |
| { |
| TaskFolder_QueryInterface, |
| TaskFolder_AddRef, |
| TaskFolder_Release, |
| TaskFolder_GetTypeInfoCount, |
| TaskFolder_GetTypeInfo, |
| TaskFolder_GetIDsOfNames, |
| TaskFolder_Invoke, |
| TaskFolder_get_Name, |
| TaskFolder_get_Path, |
| TaskFolder_GetFolder, |
| TaskFolder_GetFolders, |
| TaskFolder_CreateFolder, |
| TaskFolder_DeleteFolder, |
| TaskFolder_GetTask, |
| TaskFolder_GetTasks, |
| TaskFolder_DeleteTask, |
| TaskFolder_RegisterTask, |
| TaskFolder_RegisterTaskDefinition, |
| TaskFolder_GetSecurityDescriptor, |
| TaskFolder_SetSecurityDescriptor |
| }; |
| |
| HRESULT TaskFolder_create(const WCHAR *parent, const WCHAR *path, ITaskFolder **obj, BOOL create) |
| { |
| TaskFolder *folder; |
| WCHAR *folder_path; |
| HRESULT hr; |
| |
| if (path) |
| { |
| int len = strlenW(path); |
| if (len && path[len - 1] == '\\') return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); |
| } |
| |
| folder_path = get_full_path(parent, path); |
| if (!folder_path) return E_OUTOFMEMORY; |
| |
| if (create) |
| { |
| hr = SchRpcCreateFolder(folder_path, NULL, 0); |
| } |
| else |
| { |
| DWORD start_index, count, i; |
| TASK_NAMES names; |
| |
| start_index = 0; |
| names = NULL; |
| hr = SchRpcEnumFolders(folder_path, 0, &start_index, 0, &count, &names); |
| if (hr == S_OK) |
| { |
| for (i = 0; i < count; i++) |
| MIDL_user_free(names[i]); |
| MIDL_user_free(names); |
| } |
| else |
| { |
| if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) |
| hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); |
| } |
| } |
| |
| if (FAILED(hr)) |
| { |
| heap_free(folder_path); |
| return hr; |
| } |
| |
| folder = heap_alloc(sizeof(*folder)); |
| if (!folder) |
| { |
| heap_free(folder_path); |
| return E_OUTOFMEMORY; |
| } |
| |
| folder->ITaskFolder_iface.lpVtbl = &TaskFolder_vtbl; |
| folder->ref = 1; |
| folder->path = folder_path; |
| *obj = &folder->ITaskFolder_iface; |
| |
| TRACE("created %p\n", *obj); |
| |
| return S_OK; |
| } |