/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ScheduledTaskRemove.h" #include #include #include #include "EventLog.h" #include "mozilla/RefPtr.h" namespace mozilla::default_agent { #define ENSURE(x) \ if (FAILED(hr = (x))) { \ LOG_ERROR(hr); \ return hr; \ } bool EndsWith(const wchar_t* string, const wchar_t* suffix) { size_t string_len = wcslen(string); size_t suffix_len = wcslen(suffix); if (suffix_len > string_len) { return false; } const wchar_t* substring = string + string_len - suffix_len; return wcscmp(substring, suffix) == 0; } HRESULT RemoveTasks(const wchar_t* uniqueToken, WhichTasks tasksToRemove) { if (!uniqueToken || wcslen(uniqueToken) == 0) { return E_INVALIDARG; } RefPtr scheduler; HRESULT hr = S_OK; ENSURE(CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, getter_AddRefs(scheduler))); ENSURE(scheduler->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{})); RefPtr taskFolder; BStrPtr folderBStr(SysAllocString(kTaskVendor)); hr = scheduler->GetFolder(folderBStr.get(), getter_AddRefs(taskFolder)); if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { // Don't return an error code if our folder doesn't exist, // because that just means it's been removed already. return S_OK; } else { return hr; } } RefPtr tasksInFolder; ENSURE(taskFolder->GetTasks(TASK_ENUM_HIDDEN, getter_AddRefs(tasksInFolder))); LONG numTasks = 0; ENSURE(tasksInFolder->get_Count(&numTasks)); std::wstring WdbaTaskName(kTaskName); WdbaTaskName += uniqueToken; // This will be set to the last error that we encounter while deleting tasks. // This allows us to keep attempting to remove the remaining tasks, even if // we encounter an error, while still preserving what error we encountered so // we can return it from this function. HRESULT deleteResult = S_OK; // Set to true if we intentionally skip any tasks. bool tasksSkipped = false; for (LONG i = 0; i < numTasks; ++i) { RefPtr task; // IRegisteredTaskCollection's are 1-indexed. hr = tasksInFolder->get_Item(_variant_t(i + 1), getter_AddRefs(task)); if (FAILED(hr)) { deleteResult = hr; continue; } BSTR taskName; hr = task->get_Name(&taskName); if (FAILED(hr)) { deleteResult = hr; continue; } // Automatically free taskName when we are done with it. BStrPtr uniqueTaskName(taskName); if (tasksToRemove == WhichTasks::WdbaTaskOnly) { if (WdbaTaskName.compare(taskName) != 0) { tasksSkipped = true; continue; } } else { // tasksToRemove == WhichTasks::AllTasksForInstallation if (!EndsWith(taskName, uniqueToken)) { tasksSkipped = true; continue; } } hr = taskFolder->DeleteTask(taskName, 0 /* flags */); if (FAILED(hr)) { deleteResult = hr; } } // If we successfully removed all the tasks, delete the folder too. if (!tasksSkipped && SUCCEEDED(deleteResult)) { RefPtr rootFolder; BStrPtr rootFolderBStr = BStrPtr(SysAllocString(L"\\")); ENSURE( scheduler->GetFolder(rootFolderBStr.get(), getter_AddRefs(rootFolder))); ENSURE(rootFolder->DeleteFolder(folderBStr.get(), 0)); } return deleteResult; } } // namespace mozilla::default_agent