mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			334 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
 | 
						|
/* 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 "nsWinTaskScheduler.h"
 | 
						|
 | 
						|
#include <windows.h>
 | 
						|
#include <comdef.h>
 | 
						|
#include <sddl.h>
 | 
						|
#include <securitybaseapi.h>
 | 
						|
#include <taskschd.h>
 | 
						|
 | 
						|
#include "nsString.h"
 | 
						|
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/ResultVariant.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
 | 
						|
struct SysFreeStringDeleter {
 | 
						|
  void operator()(BSTR aPtr) { ::SysFreeString(aPtr); }
 | 
						|
};
 | 
						|
using BStrPtr = mozilla::UniquePtr<OLECHAR, SysFreeStringDeleter>;
 | 
						|
 | 
						|
static nsresult ToNotFoundOrFailure(HRESULT hr) {
 | 
						|
  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
 | 
						|
    return NS_ERROR_FILE_NOT_FOUND;
 | 
						|
  } else {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static nsresult ToAlreadyExistsOrFailure(HRESULT hr) {
 | 
						|
  if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) {
 | 
						|
    return NS_ERROR_FILE_ALREADY_EXISTS;
 | 
						|
  } else {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] static Result<RefPtr<ITaskFolder>, HRESULT> GetTaskFolder(
 | 
						|
    const char16_t* aFolderName) {
 | 
						|
  HRESULT hr;
 | 
						|
  RefPtr<ITaskService> scheduler = nullptr;
 | 
						|
 | 
						|
  hr = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER,
 | 
						|
                        IID_ITaskService, getter_AddRefs(scheduler));
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return Err(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  // Connect to the local Task Scheduler.
 | 
						|
  hr = scheduler->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{});
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return Err(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrFolderName =
 | 
						|
      BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aFolderName)));
 | 
						|
 | 
						|
  RefPtr<ITaskFolder> folder = nullptr;
 | 
						|
  hr = scheduler->GetFolder(bstrFolderName.get(), getter_AddRefs(folder));
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return Err(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  return folder;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] static Result<RefPtr<IRegisteredTask>, HRESULT> GetRegisteredTask(
 | 
						|
    const char16_t* aFolderName, const char16_t* aTaskName) {
 | 
						|
  auto folder = GetTaskFolder(aFolderName);
 | 
						|
  if (!folder.isOk()) {
 | 
						|
    return Err(folder.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrTaskName =
 | 
						|
      BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName)));
 | 
						|
 | 
						|
  RefPtr<IRegisteredTask> task = nullptr;
 | 
						|
  HRESULT hr =
 | 
						|
      folder.unwrap()->GetTask(bstrTaskName.get(), getter_AddRefs(task));
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return Err(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  return task;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(nsWinTaskSchedulerService, nsIWinTaskSchedulerService)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::GetTaskXML(const char16_t* aFolderName,
 | 
						|
                                      const char16_t* aTaskName,
 | 
						|
                                      nsAString& aResult) {
 | 
						|
  if (!aFolderName || !aTaskName) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto task = GetRegisteredTask(aFolderName, aTaskName);
 | 
						|
  if (!task.isOk()) {
 | 
						|
    return ToNotFoundOrFailure(task.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    BSTR bstrXml = nullptr;
 | 
						|
    if (FAILED(task.unwrap()->get_Xml(&bstrXml))) {
 | 
						|
      return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    aResult.Assign(bstrXml, ::SysStringLen(bstrXml));
 | 
						|
    ::SysFreeString(bstrXml);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::GetCurrentUserSid(nsAString& aUserSid) {
 | 
						|
#ifndef XP_WIN
 | 
						|
  return NS_ERROR_NOT_IMPLEMENTED;
 | 
						|
#else  // !XP_WIN
 | 
						|
  DWORD tokenLen;
 | 
						|
  LPWSTR stringSid;
 | 
						|
  BYTE tokenBuf[TOKEN_USER_MAX_SIZE];
 | 
						|
  PTOKEN_USER tokenInfo = reinterpret_cast<PTOKEN_USER>(tokenBuf);
 | 
						|
  BOOL success = GetTokenInformation(GetCurrentProcessToken(), TokenUser,
 | 
						|
                                     tokenInfo, sizeof(tokenBuf), &tokenLen);
 | 
						|
  if (!success) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
  success = ConvertSidToStringSidW(tokenInfo->User.Sid, &stringSid);
 | 
						|
  if (!success) {
 | 
						|
    return NS_ERROR_ABORT;
 | 
						|
  }
 | 
						|
  aUserSid.Assign(stringSid);
 | 
						|
  LocalFree(stringSid);
 | 
						|
  return NS_OK;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::RegisterTask(const char16_t* aFolderName,
 | 
						|
                                        const char16_t* aTaskName,
 | 
						|
                                        const char16_t* aDefinitionXML,
 | 
						|
                                        bool aUpdateExisting) {
 | 
						|
  if (!aFolderName || !aTaskName || !aDefinitionXML) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto folder = GetTaskFolder(aFolderName);
 | 
						|
  if (!folder.isOk()) {
 | 
						|
    return ToNotFoundOrFailure(folder.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrTaskName =
 | 
						|
      BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName)));
 | 
						|
  BStrPtr bstrXml = BStrPtr(
 | 
						|
      ::SysAllocString(reinterpret_cast<const OLECHAR*>(aDefinitionXML)));
 | 
						|
  LONG flags = aUpdateExisting ? TASK_CREATE_OR_UPDATE : TASK_CREATE;
 | 
						|
  TASK_LOGON_TYPE logonType = TASK_LOGON_INTERACTIVE_TOKEN;
 | 
						|
 | 
						|
  // The outparam is not needed, but not documented as optional.
 | 
						|
  RefPtr<IRegisteredTask> unusedTaskOutput = nullptr;
 | 
						|
  HRESULT hr = folder.unwrap()->RegisterTask(
 | 
						|
      bstrTaskName.get(), bstrXml.get(), flags, VARIANT{} /* userId */,
 | 
						|
      VARIANT{} /* password */, logonType, VARIANT{} /* sddl */,
 | 
						|
      getter_AddRefs(unusedTaskOutput));
 | 
						|
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return ToAlreadyExistsOrFailure(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::ValidateTaskDefinition(
 | 
						|
    const char16_t* aDefinitionXML, int32_t* aResult) {
 | 
						|
  if (!aDefinitionXML) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto folder = GetTaskFolder(reinterpret_cast<const char16_t*>(L"\\"));
 | 
						|
  if (!folder.isOk()) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrXml = BStrPtr(
 | 
						|
      ::SysAllocString(reinterpret_cast<const OLECHAR*>(aDefinitionXML)));
 | 
						|
  LONG flags = TASK_VALIDATE_ONLY;
 | 
						|
  TASK_LOGON_TYPE logonType = TASK_LOGON_INTERACTIVE_TOKEN;
 | 
						|
 | 
						|
  // The outparam is not needed, but not documented as optional.
 | 
						|
  RefPtr<IRegisteredTask> unusedTaskOutput = nullptr;
 | 
						|
  HRESULT hr = folder.unwrap()->RegisterTask(
 | 
						|
      nullptr /* path */, bstrXml.get(), flags, VARIANT{} /* userId */,
 | 
						|
      VARIANT{} /* password */, logonType, VARIANT{} /* sddl */,
 | 
						|
      getter_AddRefs(unusedTaskOutput));
 | 
						|
 | 
						|
  if (aResult) {
 | 
						|
    *aResult = hr;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::DeleteTask(const char16_t* aFolderName,
 | 
						|
                                      const char16_t* aTaskName) {
 | 
						|
  if (!aFolderName || !aTaskName) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto folder = GetTaskFolder(aFolderName);
 | 
						|
  if (!folder.isOk()) {
 | 
						|
    return ToNotFoundOrFailure(folder.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrTaskName =
 | 
						|
      BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName)));
 | 
						|
 | 
						|
  HRESULT hr = folder.unwrap()->DeleteTask(bstrTaskName.get(), 0 /* flags */);
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return ToNotFoundOrFailure(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::GetFolderTasks(const char16_t* aFolderName,
 | 
						|
                                          nsTArray<nsString>& aResult) {
 | 
						|
  if (!aFolderName) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto folder = GetTaskFolder(aFolderName);
 | 
						|
  if (!folder.isOk()) {
 | 
						|
    return ToNotFoundOrFailure(folder.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<IRegisteredTaskCollection> taskCollection = nullptr;
 | 
						|
  if (FAILED(folder.unwrap()->GetTasks(TASK_ENUM_HIDDEN,
 | 
						|
                                       getter_AddRefs(taskCollection)))) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  LONG taskCount = 0;
 | 
						|
  if (FAILED(taskCollection->get_Count(&taskCount))) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  aResult.Clear();
 | 
						|
 | 
						|
  for (LONG i = 0; i < taskCount; ++i) {
 | 
						|
    RefPtr<IRegisteredTask> task = nullptr;
 | 
						|
 | 
						|
    // nb: Collections are indexed from 1.
 | 
						|
    if (FAILED(taskCollection->get_Item(_variant_t(i + 1),
 | 
						|
                                        getter_AddRefs(task)))) {
 | 
						|
      return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    BStrPtr bstrTaskName;
 | 
						|
    {
 | 
						|
      BSTR tempTaskName = nullptr;
 | 
						|
      if (FAILED(task->get_Name(&tempTaskName))) {
 | 
						|
        return NS_ERROR_FAILURE;
 | 
						|
      }
 | 
						|
      bstrTaskName = BStrPtr(tempTaskName);
 | 
						|
    }
 | 
						|
 | 
						|
    aResult.AppendElement(nsString(bstrTaskName.get()));
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::CreateFolder(const char16_t* aParentFolderName,
 | 
						|
                                        const char16_t* aSubFolderName) {
 | 
						|
  if (!aParentFolderName || !aSubFolderName) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto parentFolder = GetTaskFolder(aParentFolderName);
 | 
						|
  if (!parentFolder.isOk()) {
 | 
						|
    return ToNotFoundOrFailure(parentFolder.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrSubFolderName = BStrPtr(
 | 
						|
      ::SysAllocString(reinterpret_cast<const OLECHAR*>(aSubFolderName)));
 | 
						|
 | 
						|
  HRESULT hr = parentFolder.unwrap()->CreateFolder(bstrSubFolderName.get(),
 | 
						|
                                                   VARIANT{},  // sddl
 | 
						|
                                                   nullptr);   // ppFolder
 | 
						|
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    return ToAlreadyExistsOrFailure(hr);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsWinTaskSchedulerService::DeleteFolder(const char16_t* aParentFolderName,
 | 
						|
                                        const char16_t* aSubFolderName) {
 | 
						|
  if (!aParentFolderName || !aSubFolderName) {
 | 
						|
    return NS_ERROR_NULL_POINTER;
 | 
						|
  }
 | 
						|
 | 
						|
  auto parentFolder = GetTaskFolder(aParentFolderName);
 | 
						|
  if (!parentFolder.isOk()) {
 | 
						|
    return ToNotFoundOrFailure(parentFolder.unwrapErr());
 | 
						|
  }
 | 
						|
 | 
						|
  BStrPtr bstrSubFolderName = BStrPtr(
 | 
						|
      ::SysAllocString(reinterpret_cast<const OLECHAR*>(aSubFolderName)));
 | 
						|
 | 
						|
  HRESULT hr = parentFolder.unwrap()->DeleteFolder(bstrSubFolderName.get(),
 | 
						|
                                                   0 /* flags */);
 | 
						|
 | 
						|
  if (FAILED(hr)) {
 | 
						|
    if (hr == HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY)) {
 | 
						|
      return NS_ERROR_FILE_DIR_NOT_EMPTY;
 | 
						|
    } else {
 | 
						|
      return ToNotFoundOrFailure(hr);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 |