// Copyright 2010-2021, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// skip all unless OS_WIN
#ifdef OS_WIN

#include <Sddl.h>
#include <Windows.h>

#include <algorithm>
#include <string>

#include "base/const.h"
#include "base/cpu_stats.h"
#include "base/logging.h"
#include "base/scoped_handle.h"
#include "base/singleton.h"
#include "base/system_util.h"
#include "base/thread.h"
#include "base/util.h"
#include "base/win_sandbox.h"
#include "base/win_util.h"
#include "ipc/ipc.h"
#include "ipc/ipc_path_manager.h"

namespace mozc {
namespace {

const bool kReadTypeACK = true;
const bool kReadTypeData = false;
const bool kSendTypeData = false;
const int kMaxSuccessiveConnectionFailureCount = 5;

size_t GetNumberOfProcessors() {
  // thread-safety is not required.
  static size_t num = CPUStats().GetNumberOfProcessors();
  return std::max(num, static_cast<size_t>(1));
}

// Least significant bit of OVERLAPPED::hEvent can be used for special
// purpose against GetQueuedCompletionStatus API.
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364986.aspx
// This function provides a safe way to retrieve the actual event handle
// even in this situation.
HANDLE GetEventHandleFromOverlapped(const OVERLAPPED *overlapped) {
  return reinterpret_cast<HANDLE>(
      reinterpret_cast<DWORD_PTR>(overlapped->hEvent) & ~1);
}

// Returns true if the given |overlapped| is initialized in successful.
bool InitOverlapped(OVERLAPPED *overlapped, HANDLE wait_handle) {
  if (wait_handle == 0 || wait_handle == INVALID_HANDLE_VALUE) {
    LOG(ERROR) << "wait_handle is invalid.";
    return false;
  }
  ::ZeroMemory(overlapped, sizeof(OVERLAPPED));
  if (::ResetEvent(wait_handle) == FALSE) {
    const DWORD last_error = ::GetLastError();
    LOG(ERROR) << "::ResetEvent failed. error: " << last_error;
    return false;
  }
  overlapped->hEvent = wait_handle;
  return true;
}

class IPCClientMutexBase {
 public:
  explicit IPCClientMutexBase(const std::string &ipc_channel_name) {
    // Make a kernel mutex object so that multiple ipc connections are
    // serialized here. In Windows, there is no useful way to serialize
    // the multiple connections to the single-thread named pipe server.
    // WaitForNamedPipe doesn't work for this propose as it just lets
    // clients know that the connection becomes "available" right now.
    // It doesn't mean that connection is available for the current
    // thread. The "available" notification is sent to all waiting ipc
    // clients at the same time and only one client gets the connection.
    // This causes redundant and wasteful CreateFile calles.
    std::string mutex_name = kMutexPathPrefix;
    mutex_name += SystemUtil::GetUserSidAsString();
    mutex_name += ".";
    mutex_name += ipc_channel_name;
    mutex_name += ".ipc";
    std::wstring wmutex_name;
    Util::UTF8ToWide(mutex_name, &wmutex_name);

    LPSECURITY_ATTRIBUTES security_attributes_ptr = nullptr;
    SECURITY_ATTRIBUTES security_attributes;
    if (!WinSandbox::MakeSecurityAttributes(WinSandbox::kSharableMutex,
                                            &security_attributes)) {
      LOG(ERROR) << "Cannot make SecurityAttributes";
    } else {
      security_attributes_ptr = &security_attributes;
    }

    // http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx:
    // Two or more processes can call CreateMutex to create the same named
    // mutex. The first process actually creates the mutex, and subsequent
    // processes with sufficient access rights simply open a handle to
    // the existing mutex. This enables multiple processes to get handles
    // of the same mutex, while relieving the user of the responsibility
    // of ensuring that the creating process is started first.
    // When using this technique, you should set the
    // bInitialOwner flag to FALSE; otherwise, it can be difficult to be
    // certain which process has initial ownership.
    ipc_mutex_.reset(
        ::CreateMutex(security_attributes_ptr, FALSE, wmutex_name.c_str()));
    if (security_attributes_ptr != nullptr) {
      ::LocalFree(security_attributes_ptr->lpSecurityDescriptor);
    }

    const DWORD create_mutex_error = ::GetLastError();
    if (ipc_mutex_.get() == nullptr) {
      LOG(ERROR) << "CreateMutex failed: " << create_mutex_error;
      return;
    }
  }

  virtual ~IPCClientMutexBase() {}

  HANDLE get() const { return ipc_mutex_.get(); }

 private:
  ScopedHandle ipc_mutex_;
};

class ConverterClientMutex : public IPCClientMutexBase {
 public:
  ConverterClientMutex() : IPCClientMutexBase("converter") {}

 private:
  DISALLOW_COPY_AND_ASSIGN(ConverterClientMutex);
};

class RendererClientMutex : public IPCClientMutexBase {
 public:
  RendererClientMutex() : IPCClientMutexBase("renderer") {}

 private:
  DISALLOW_COPY_AND_ASSIGN(RendererClientMutex);
};

class FallbackClientMutex : public IPCClientMutexBase {
 public:
  FallbackClientMutex() : IPCClientMutexBase("fallback") {}

 private:
  DISALLOW_COPY_AND_ASSIGN(FallbackClientMutex);
};

// In Mozc client, we should support different IPC channels (client-converter
// and client-renderer) so we need to have different global mutexes to
// serialize each client. Currently |ipc_name| starts with "session" and
// "renderer" are expected.
HANDLE GetClientMutex(const std::string &ipc_name) {
  if (Util::StartsWith(ipc_name, "session")) {
    return Singleton<ConverterClientMutex>::get()->get();
  }
  if (Util::StartsWith(ipc_name, "renderer")) {
    return Singleton<RendererClientMutex>::get()->get();
  }
  LOG(WARNING) << "unexpected IPC name: " << ipc_name;
  return Singleton<FallbackClientMutex>::get()->get();
}

// RAII class for calling ReleaseMutex in destructor.
class ScopedReleaseMutex {
 public:
  explicit ScopedReleaseMutex(HANDLE handle) : pipe_handle_(handle) {}

  virtual ~ScopedReleaseMutex() {
    if (nullptr != pipe_handle_) {
      ::ReleaseMutex(pipe_handle_);
    }
    pipe_handle_ = nullptr;
  }

  HANDLE get() const { return pipe_handle_; }

 private:
  HANDLE pipe_handle_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedReleaseMutex);
};

uint32 GetServerProcessIdImpl(HANDLE handle) {
  ULONG pid = 0;
  if (::GetNamedPipeServerProcessId(handle, &pid) == 0) {
    const DWORD get_named_pipe_server_process_id_error = ::GetLastError();
    LOG(ERROR) << "GetNamedPipeServerProcessId failed: "
               << get_named_pipe_server_process_id_error;
    return static_cast<uint32>(-1);  // always deny the connection
  }

  VLOG(1) << "Got server ProcessID: " << pid;

  return static_cast<uint32>(pid);
}

void SafeCancelIO(HANDLE device_handle, OVERLAPPED *overlapped) {
  if (::CancelIo(device_handle) == FALSE) {
    const DWORD cancel_error = ::GetLastError();
    LOG(ERROR) << "Failed to CancelIo: " << cancel_error;
  }

  // Wait for the completion of the on-going request forever. This is not
  // _safe_ and should be fixed anyway.
  // TODO(yukawa): Avoid INFINITE if possible.
  ::WaitForSingleObject(GetEventHandleFromOverlapped(overlapped), INFINITE);
}

bool WaitForQuitOrIOImpl(HANDLE device_handle, HANDLE quit_event, DWORD timeout,
                         OVERLAPPED *overlapped, IPCErrorType *last_ipc_error) {
  const HANDLE events[] = {quit_event,
                           GetEventHandleFromOverlapped(overlapped)};
  const DWORD wait_result =
      ::WaitForMultipleObjects(ARRAYSIZE(events), events, FALSE, timeout);
  const DWORD wait_error = ::GetLastError();
  // Clear the I/O operation if still exists.
  if (!HasOverlappedIoCompleted(overlapped)) {
    // This is not safe because this operation may be blocked forever.
    // TODO(yukawa): Implement safer cancelation mechanism.
    SafeCancelIO(device_handle, overlapped);
  }
  if (wait_result == WAIT_TIMEOUT) {
    LOG(WARNING) << "Timeout: " << timeout;
    *last_ipc_error = IPC_TIMEOUT_ERROR;
    return false;
  }
  if (wait_result == WAIT_OBJECT_0) {
    // Should be quit immediately
    *last_ipc_error = IPC_QUIT_EVENT_SIGNALED;
    return false;
  }
  if (wait_result != (WAIT_OBJECT_0 + 1)) {
    LOG(WARNING) << "Unknown result: " << wait_result
                 << ", Error: " << wait_error;
    *last_ipc_error = IPC_UNKNOWN_ERROR;
    return false;
  }
  return true;
}

bool WaitForIOImpl(HANDLE device_handle, DWORD timeout, OVERLAPPED *overlapped,
                   IPCErrorType *last_ipc_error) {
  const DWORD wait_result =
      ::WaitForSingleObject(GetEventHandleFromOverlapped(overlapped), timeout);
  // Clear the I/O operation if still exists.
  if (!HasOverlappedIoCompleted(overlapped)) {
    // This is not safe because this operation may be blocked forever.
    // TODO(yukawa): Implement safer cancelation mechanism.
    SafeCancelIO(device_handle, overlapped);
  }
  if (wait_result == WAIT_TIMEOUT) {
    LOG(WARNING) << "Timeout: " << timeout;
    *last_ipc_error = IPC_TIMEOUT_ERROR;
    return false;
  }
  if (wait_result != WAIT_OBJECT_0) {
    LOG(WARNING) << "Unknown result: " << wait_result;
    *last_ipc_error = IPC_UNKNOWN_ERROR;
    return false;
  }
  return true;
}

bool WaitForQuitOrIO(HANDLE device_handle, HANDLE quit_event, DWORD timeout,
                     OVERLAPPED *overlapped, IPCErrorType *last_ipc_error) {
  if (quit_event != nullptr) {
    return WaitForQuitOrIOImpl(device_handle, quit_event, timeout, overlapped,
                               last_ipc_error);
  }
  return WaitForIOImpl(device_handle, timeout, overlapped, last_ipc_error);
}

// To work around a bug of GetOverlappedResult in Vista
// http://msdn.microsoft.com/en-us/library/dd371711.aspx
bool SafeWaitOverlappedResult(HANDLE device_handle, HANDLE quit_event,
                              DWORD timeout, OVERLAPPED *overlapped,
                              DWORD *num_bytes_updated,
                              IPCErrorType *last_ipc_error, bool wait_ack) {
  DCHECK(overlapped);
  DCHECK(num_bytes_updated);
  DCHECK(last_ipc_error);
  if (!WaitForQuitOrIO(device_handle, quit_event, timeout, overlapped,
                       last_ipc_error)) {
    return false;
  }

  *num_bytes_updated = 0;
  const BOOL get_overlapped_result = ::GetOverlappedResult(
      device_handle, overlapped, num_bytes_updated, FALSE);
  if (get_overlapped_result == FALSE) {
    const DWORD get_overlapped_error = ::GetLastError();
    if (get_overlapped_error == ERROR_BROKEN_PIPE) {
      if (wait_ack) {
        // This is an expected behavior.
        return true;
      }
      LOG(ERROR) << "GetOverlappedResult() failed: ERROR_BROKEN_PIPE";
    } else {
      LOG(ERROR) << "GetOverlappedResult() failed: " << get_overlapped_error;
    }
    *last_ipc_error = IPC_UNKNOWN_ERROR;
    return false;
  }
  return true;
}

bool SendIPCMessage(HANDLE device_handle, HANDLE write_wait_handle,
                    const char *buf, size_t buf_length, int timeout,
                    IPCErrorType *last_ipc_error) {
  if (buf_length == 0) {
    LOG(WARNING) << "buf length is 0";
    *last_ipc_error = IPC_UNKNOWN_ERROR;
    return false;
  }

  DWORD num_bytes_written = 0;
  OVERLAPPED overlapped;
  if (!InitOverlapped(&overlapped, write_wait_handle)) {
    *last_ipc_error = IPC_WRITE_ERROR;
    return false;
  }

  const bool write_file_result =
      (::WriteFile(device_handle, buf, static_cast<DWORD>(buf_length),
                   &num_bytes_written, &overlapped) != FALSE);
  const DWORD write_file_error = ::GetLastError();
  if (write_file_result) {
    // ::WriteFile is done as sync operation.
  } else {
    if (write_file_error != ERROR_IO_PENDING) {
      LOG(ERROR) << "WriteFile() failed: " << write_file_error;
      *last_ipc_error = IPC_WRITE_ERROR;
      return false;
    }
    if (!SafeWaitOverlappedResult(device_handle, nullptr, timeout, &overlapped,
                                  &num_bytes_written, last_ipc_error,
                                  kSendTypeData)) {
      return false;
    }
  }

  // As we use message-type namedpipe, all the data should be written in one
  // shot. Otherwise, single message will be split into multiple packets.
  if (num_bytes_written != buf_length) {
    LOG(ERROR) << "Data truncated. buf_length: " << buf_length
               << ", num_bytes_written: " << num_bytes_written;
    *last_ipc_error = IPC_UNKNOWN_ERROR;
    return false;
  }
  return true;
}

bool RecvIPCMessage(HANDLE device_handle, HANDLE read_wait_handle, char *buf,
                    size_t *buf_length, int timeout, bool read_type_ack,
                    IPCErrorType *last_ipc_error) {
  if (*buf_length == 0) {
    LOG(WARNING) << "buf length is 0";
    *last_ipc_error = IPC_UNKNOWN_ERROR;
    return false;
  }

  OVERLAPPED overlapped;
  if (!InitOverlapped(&overlapped, read_wait_handle)) {
    *last_ipc_error = IPC_READ_ERROR;
    return false;
  }

  DWORD num_bytes_read = 0;
  const bool read_file_result =
      (::ReadFile(device_handle, buf, static_cast<DWORD>(*buf_length),
                  &num_bytes_read, &overlapped) != FALSE);
  const DWORD read_file_error = ::GetLastError();
  if (read_file_result) {
    // ::ReadFile is done as sync operation.
  } else {
    if (read_type_ack && (read_file_error == ERROR_BROKEN_PIPE)) {
      // The client has already disconnected this pipe. This is an expected
      // behavior and do not treat as an error.
      return true;
    }
    if (read_file_error != ERROR_IO_PENDING) {
      LOG(ERROR) << "ReadFile() failed: " << read_file_error;
      *last_ipc_error = IPC_READ_ERROR;
      return false;
    }
    // Actually this is an async operation. Let's wait for its completion.
    if (!SafeWaitOverlappedResult(device_handle, nullptr, timeout, &overlapped,
                                  &num_bytes_read, last_ipc_error,
                                  read_type_ack)) {
      return false;
    }
  }

  if (!read_type_ack && (num_bytes_read == 0)) {
    LOG(WARNING) << "Received 0 result.";
  }

  *buf_length = num_bytes_read;

  return true;
}

HANDLE CreateManualResetEvent() {
  return ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
}

// We do not care about the signaled state of the device handle itself.
// This slightly improves the performance.
// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538.aspx
void MaybeDisableFileCompletionNotification(HANDLE device_handle) {
  // This is not a mandatory task. Just ignore the actual error (if any).
  ::SetFileCompletionNotificationModes(device_handle,
                                       FILE_SKIP_SET_EVENT_ON_HANDLE);
}

}  // namespace

IPCServer::IPCServer(const std::string &name, int32 num_connections,
                     int32 timeout)
    : connected_(false),
      pipe_event_(CreateManualResetEvent()),
      quit_event_(CreateManualResetEvent()),
      timeout_(timeout) {
  IPCPathManager *manager = IPCPathManager::GetIPCPathManager(name);
  std::string server_address;

  if (!manager->CreateNewPathName() && !manager->LoadPathName()) {
    LOG(ERROR) << "Cannot prepare IPC path name";
    return;
  }

  if (!manager->GetPathName(&server_address)) {
    LOG(ERROR) << "Cannot make IPC path name";
    return;
  }
  DCHECK(!server_address.empty());

  SECURITY_ATTRIBUTES security_attributes;
  if (!WinSandbox::MakeSecurityAttributes(WinSandbox::kSharablePipe,
                                          &security_attributes)) {
    LOG(ERROR) << "Cannot make SecurityAttributes";
    return;
  }

  // Create a named pipe.
  std::wstring wserver_address;
  Util::UTF8ToWide(server_address, &wserver_address);
  HANDLE handle = ::CreateNamedPipe(
      wserver_address.c_str(),
      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
      (num_connections <= 0 ? PIPE_UNLIMITED_INSTANCES : num_connections),
      sizeof(request_), sizeof(response_), 0, &security_attributes);
  const DWORD create_named_pipe_error = ::GetLastError();
  ::LocalFree(security_attributes.lpSecurityDescriptor);

  if (INVALID_HANDLE_VALUE == handle) {
    LOG(FATAL) << "CreateNamedPipe failed" << create_named_pipe_error;
    return;
  }

  pipe_handle_.reset(handle);

  MaybeDisableFileCompletionNotification(pipe_handle_.get());

  if (!manager->SavePathName()) {
    LOG(ERROR) << "Cannot save IPC path name";
    return;
  }

  connected_ = true;
}

IPCServer::~IPCServer() { Terminate(); }

bool IPCServer::Connected() const { return connected_; }

void IPCServer::Terminate() {
  if (server_thread_.get() == nullptr) {
    return;
  }

  if (!server_thread_->IsRunning()) {
    return;
  }

  if (!::SetEvent(quit_event_.get())) {
    LOG(ERROR) << "SetEvent failed";
  }

  // Close the named pipe.
  // This is a workaround for killing child thread
  if (server_thread_.get() != nullptr) {
    server_thread_->Join();
    server_thread_->Terminate();
  }

  connected_ = false;
}

void IPCServer::Loop() {
  IPCErrorType last_ipc_error = IPC_NO_ERROR;

  int successive_connection_failure_count = 0;
  while (connected_) {
    OVERLAPPED overlapped;
    if (!InitOverlapped(&overlapped, pipe_event_.get())) {
      connected_ = false;
      return;
    }

    const BOOL result = ::ConnectNamedPipe(pipe_handle_.get(), &overlapped);
    const DWORD connect_named_pipe_error = ::GetLastError();
    if (result == FALSE) {
      if (connect_named_pipe_error == ERROR_PIPE_CONNECTED) {
        // Already connected. Nothing to do.
      } else if (connect_named_pipe_error == ERROR_NO_DATA) {
        // client already closes the connection
        ::DisconnectNamedPipe(pipe_handle_.get());
        continue;
      } else if (connect_named_pipe_error == ERROR_IO_PENDING) {
        // Actually this is async operation.
        DWORD ignored = 0;
        IPCErrorType ipc_error = IPC_NO_ERROR;
        if (!SafeWaitOverlappedResult(pipe_handle_.get(), quit_event_.get(),
                                      INFINITE, &overlapped, &ignored,
                                      &ipc_error, kReadTypeData)) {
          if (ipc_error == IPC_QUIT_EVENT_SIGNALED) {
            VLOG(1) << "Received Conrol event from other thread";
            connected_ = false;
            return;
          }
          ++successive_connection_failure_count;
          if (successive_connection_failure_count >=
              kMaxSuccessiveConnectionFailureCount) {
            LOG(ERROR) << "Give up to connect named pipe.";
            connected_ = false;
            return;
          }
          ::DisconnectNamedPipe(pipe_handle_.get());
          continue;
        }
      } else {
        LOG(FATAL) << "Unexpected error: " << connect_named_pipe_error;
      }
    }

    successive_connection_failure_count = 0;
    // Retrieve an incoming message.
    size_t request_size = sizeof(request_);
    if (RecvIPCMessage(pipe_handle_.get(), pipe_event_.get(), &request_[0],
                       &request_size, timeout_, kReadTypeData,
                       &last_ipc_error)) {
      size_t response_size = sizeof(response_);
      if (!Process(&request_[0], request_size, &response_[0], &response_size)) {
        connected_ = false;
      }

      // When Process() returns 0 result, force to call DisconnectNamedPipe()
      // instead of checking ACK message
      if (response_size == 0) {
        LOG(WARNING) << "Process() return 0 result";
        ::DisconnectNamedPipe(pipe_handle_.get());
        continue;
      }

      // Send a response
      SendIPCMessage(pipe_handle_.get(), pipe_event_.get(), &response_[0],
                     response_size, timeout_, &last_ipc_error);
    }

    // Special treatment for Windows per discussion with thatanaka:
    // It's hard to know that client has processed the server's response.
    // We will be able to call ::FlushFileHandles() here, but
    // FlushFileHandles() is blocked if client doesn't call ReadFile(). That
    // means that a malicious user can easily block the server not by calling
    // ReadFile. In order to know the transaction completes successfully,
    // client needs to send an ACK message to the server.

    // Wait ACK-like signal from client for 0.1 second. If we detect the pipe
    // disconnect event, so far so good. If we receive more data, we assume it
    // is an ACK signal (the IPC client of Mozc 1.5.x or earlier does this).
    char ack_request[1] = {0};
    size_t ack_request_size = 1;
    static const int kAckTimeout = 100;
    if (!RecvIPCMessage(pipe_handle_.get(), pipe_event_.get(), ack_request,
                        &ack_request_size, kAckTimeout, kReadTypeACK,
                        &last_ipc_error)) {
      // This case happens when the client did not receive the server's response
      // within timeout. Anyway we will close the connection so that the server
      // will not be blocked.
      LOG(WARNING) << "Client didn't respond within " << kAckTimeout
                   << " msec.";
    }
    ::DisconnectNamedPipe(pipe_handle_.get());
  }

  connected_ = false;
}

// old interface
IPCClient::IPCClient(const std::string &name)
    : pipe_event_(CreateManualResetEvent()),
      connected_(false),
      ipc_path_manager_(nullptr),
      last_ipc_error_(IPC_NO_ERROR) {
  Init(name, "");
}

IPCClient::IPCClient(const std::string &name, const std::string &server_path)
    : pipe_event_(CreateManualResetEvent()),
      connected_(false),
      ipc_path_manager_(nullptr),
      last_ipc_error_(IPC_NO_ERROR) {
  Init(name, server_path);
}

void IPCClient::Init(const std::string &name, const std::string &server_path) {
  last_ipc_error_ = IPC_NO_CONNECTION;

  // We should change the mutex based on which IPC server we will talk with.
  ScopedReleaseMutex ipc_mutex(GetClientMutex(name));

  if (ipc_mutex.get() == nullptr) {
    LOG(ERROR) << "IPC mutex is not available";
  } else {
    const int kMutexTimeout = 10 * 1000;  // wait at most 10sec.
    switch (::WaitForSingleObject(ipc_mutex.get(), kMutexTimeout)) {
      case WAIT_TIMEOUT:
        // TODO(taku): with suspend/resume, WaitForSingleObject may
        // return WAIT_TIMEOUT. We have to consider the case
        // in the future.
        LOG(ERROR) << "IPC client was not available even after "
                   << kMutexTimeout << " msec.";
        break;
      case WAIT_ABANDONED:
        DLOG(INFO) << "mutex object was removed";
        break;
      case WAIT_OBJECT_0:
        break;
      default:
        break;
    }
  }

  IPCPathManager *manager = IPCPathManager::GetIPCPathManager(name);
  if (manager == nullptr) {
    LOG(ERROR) << "IPCPathManager::GetIPCPathManager failed";
    return;
  }

  ipc_path_manager_ = manager;

  // TODO(taku): enable them on Mac/Linux
#ifdef DEBUG
  const size_t kMaxTrial = 256;
#else
  const size_t kMaxTrial = 2;
#endif

  for (size_t trial = 0; trial < kMaxTrial; ++trial) {
    std::string server_address;
    if (!manager->LoadPathName() || !manager->GetPathName(&server_address)) {
      continue;
    }
    std::wstring wserver_address;
    Util::UTF8ToWide(server_address, &wserver_address);

    if (GetNumberOfProcessors() == 1) {
      // When the code is running in single processor environment, sometimes
      // IPC server has not finished the clean-up tasks for the previous IPC
      // session here. So we intentionally call WaitNamedPipe API so that IPC
      // server has a chance to complete clean-up tasks if necessary.
      // NOTE: We cannot set 0 for the wait time because 0 has a special meaning
      // as |NMPWAIT_USE_DEFAULT_WAIT|.
      const DWORD kMinWaitTimeForWaitNamedPipe = 1;
      ::WaitNamedPipe(wserver_address.c_str(), kMinWaitTimeForWaitNamedPipe);
    }

    ScopedHandle new_handle(
        ::CreateFile(wserver_address.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
                     nullptr, OPEN_EXISTING,
                     FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT |
                         SECURITY_IDENTIFICATION | SECURITY_EFFECTIVE_ONLY,
                     nullptr));
    const DWORD create_file_error = ::GetLastError();
    // ScopedHandle returns nullptr even when it received INVALID_HANDLE_VALUE.
    if (new_handle.get() != nullptr) {
      pipe_handle_.reset(new_handle.take());
      MaybeDisableFileCompletionNotification(pipe_handle_.get());
      if (!manager->IsValidServer(GetServerProcessIdImpl(pipe_handle_.get()),
                                  server_path)) {
        LOG(ERROR) << "Connecting to invalid server";
        last_ipc_error_ = IPC_INVALID_SERVER;
        return;
      }

      last_ipc_error_ = IPC_NO_ERROR;
      connected_ = true;
      return;
    }

    if (ERROR_PIPE_BUSY != create_file_error) {
      LOG(ERROR) << "Server is not running: " << create_file_error;
      manager->Clear();
      continue;
    }

    // wait for 10 second until server is ready
    // TODO(taku): control the timeout via flag.
#ifdef DEBUG
    const int kNamedPipeTimeout = 100000;  // 100 sec
#else
    const int kNamedPipeTimeout = 10000;  // 10 sec
#endif
    DLOG(ERROR) << "Server is busy. waiting for " << kNamedPipeTimeout
                << " msec";
    if (!::WaitNamedPipe(wserver_address.c_str(), kNamedPipeTimeout)) {
      const DWORD wait_named_pipe_error = ::GetLastError();
      LOG(ERROR) << "WaitNamedPipe failed: " << wait_named_pipe_error;
      if ((trial + 1) == kMaxTrial) {
        last_ipc_error_ = IPC_TIMEOUT_ERROR;
        return;
      }
      continue;  // go 2nd trial
    }
  }
}

IPCClient::~IPCClient() {}

bool IPCClient::Connected() const { return connected_; }

bool IPCClient::Call(const char *request, size_t request_size, char *response,
                     size_t *response_size, int32 timeout) {
  last_ipc_error_ = IPC_NO_ERROR;
  if (!connected_) {
    LOG(ERROR) << "IPCClient is not connected";
    last_ipc_error_ = IPC_NO_CONNECTION;
    return false;
  }

  if (!SendIPCMessage(pipe_handle_.get(), pipe_event_.get(), request,
                      request_size, timeout, &last_ipc_error_)) {
    LOG(ERROR) << "SendIPCMessage() failed";
    return false;
  }

  if (!RecvIPCMessage(pipe_handle_.get(), pipe_event_.get(), response,
                      response_size, timeout, kReadTypeData,
                      &last_ipc_error_)) {
    LOG(ERROR) << "RecvIPCMessage() failed";
    return false;
  }

  // Instead of sending ACK message to Server, we simply disconnect the named
  // pile to notify that client can read the message successfully.
  connected_ = false;
  pipe_handle_.reset(INVALID_HANDLE_VALUE);

  return true;
}

}  // namespace mozc

#endif  // OS_WIN
