// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/updater/mac/privileged_helper/server.h"

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/launch.h"
#include "base/sequence_checker.h"
#include "base/strings/sys_string_conversions.h"
#import "base/task/sequenced_task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/mac/privileged_helper/helper_branding.h"
#include "chrome/updater/updater_branding.h"

namespace updater {

namespace {
constexpr int kServerKeepAliveSeconds = 1;
}

PrivilegedHelperServer::PrivilegedHelperServer() = default;
PrivilegedHelperServer::~PrivilegedHelperServer() = default;

int PrivilegedHelperServer::Initialize() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  service_delegate_ = [[PrivilegedHelperServiceXPCDelegate alloc]
      initWithService:service_
               server:scoped_refptr<PrivilegedHelperServer>(this)];
  service_listener_ = [[NSXPCListener alloc]
      initWithMachServiceName:base::SysUTF8ToNSString(PRIVILEGED_HELPER_NAME)];
  service_listener_.delegate = service_delegate_;
  [service_listener_ resume];
  return kErrorOk;
}

void PrivilegedHelperServer::FirstTaskRun() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

void PrivilegedHelperServer::Uninitialize() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  service_delegate_ = nil;
  service_listener_ = nil;
  Uninstall();
}

void PrivilegedHelperServer::TaskStarted() {
  main_task_runner_->PostTask(
      FROM_HERE, BindOnce(&PrivilegedHelperServer::MarkTaskStarted, this));
}

void PrivilegedHelperServer::MarkTaskStarted() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  ++tasks_running_;
}

base::TimeDelta PrivilegedHelperServer::ServerKeepAlive() {
  int seconds = kServerKeepAliveSeconds;
  return base::Seconds(seconds);
}

void PrivilegedHelperServer::TaskCompleted() {
  main_task_runner_->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(&PrivilegedHelperServer::AcknowledgeTaskCompletion, this),
      ServerKeepAlive());
}

void PrivilegedHelperServer::Uninstall() {
  base::DeleteFile(base::FilePath("/Library/LaunchDaemons")
                       .Append(UPDATER_HELPER_BUNDLE_ID ".plist"));
  base::DeleteFile(base::FilePath("/Library/PrivilegedHelperTools")
                       .Append(UPDATER_HELPER_BUNDLE_ID));
  base::CommandLine launchctl(base::FilePath("/bin/launchctl"));
  launchctl.AppendArg("remove");
  launchctl.AppendArg(UPDATER_HELPER_BUNDLE_ID);
  base::LaunchProcess(launchctl, {});
}

void PrivilegedHelperServer::AcknowledgeTaskCompletion() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (--tasks_running_ < 1) {
    main_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&PrivilegedHelperServer::Shutdown, this, 0));
  }
}

scoped_refptr<App> PrivilegedHelperServerInstance() {
  return base::MakeRefCounted<PrivilegedHelperServer>();
}

}  // namespace updater
