<commit_before>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/component_updater/widevine_cdm_component_installer.h"

#include <string.h>

#include <vector>

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/widevine_cdm_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/pepper_plugin_info.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"

#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.

using content::BrowserThread;
using content::PluginService;

namespace {

// TODO(xhwang): Move duplicate code among all component installer
// implementations to some common place.

#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

// CRX hash. The extension id is: oimompecagnajdejgnnjijobebaeigek.
const uint8 kSha2Hash[] = { 0xe8, 0xce, 0xcf, 0x42, 0x06, 0xd0, 0x93, 0x49,
                            0x6d, 0xd9, 0x89, 0xe1, 0x41, 0x04, 0x86, 0x4a,
                            0x8f, 0xbd, 0x86, 0x12, 0xb9, 0x58, 0x9b, 0xfb,
                            0x4f, 0xbb, 0x1b, 0xa9, 0xd3, 0x85, 0x37, 0xef };

// File name of the Widevine CDM component manifest on different platforms.
const char kWidevineCdmManifestName[] = "WidevineCdm";

// Name of the Widevine CDM OS in the component manifest.
const char kWidevineCdmPlatform[] =
#if defined(OS_MACOSX)
    "mac";
#elif defined(OS_WIN)
    "win";
#else  // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android?
    "linux";
#endif

// Name of the Widevine CDM architecture in the component manifest.
const char kWidevineCdmArch[] =
#if defined(ARCH_CPU_X86)
    "x86";
#elif defined(ARCH_CPU_X86_64)
    "x64";
#else  // TODO(viettrungluu): Support an ARM check?
    "???";
#endif

// If we don't have a Widevine CDM component, this is the version we claim.
const char kNullVersion[] = "0.0.0.0";

// The base directory on Windows looks like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\.
base::FilePath GetWidevineCdmBaseDirectory() {
  base::FilePath result;
  PathService::Get(chrome::DIR_COMPONENT_WIDEVINE_CDM, &result);
  return result;
}

// Widevine CDM is packaged as a multi-CRX. Widevine CDM binaries are located in
// _platform_specific/<platform_arch> folder in the package. This function
// returns the platform-specific subdirectory that is part of that multi-CRX.
base::FilePath GetPlatformDirectory(const base::FilePath& base_path) {
  std::string platform_arch = kWidevineCdmPlatform;
  platform_arch += '_';
  platform_arch += kWidevineCdmArch;
  return base_path.AppendASCII("_platform_specific").AppendASCII(platform_arch);
}

// Widevine CDM has the version encoded in the path so we need to enumerate the
// directories to find the full path.
// On success, |latest_dir| returns something like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\10.3.44.555\.
// |latest_version| returns the corresponding version number. |older_dirs|
// returns directories of all older versions.
bool GetWidevineCdmDirectory(base::FilePath* latest_dir,
                             base::Version* latest_version,
                             std::vector<base::FilePath>* older_dirs) {
  base::FilePath base_dir = GetWidevineCdmBaseDirectory();
  bool found = false;
  base::FileEnumerator file_enumerator(
      base_dir, false, base::FileEnumerator::DIRECTORIES);
  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    base::Version version(path.BaseName().MaybeAsASCII());
    if (!version.IsValid())
      continue;
    if (found) {
      if (version.CompareTo(*latest_version) > 0) {
        older_dirs->push_back(*latest_dir);
        *latest_dir = path;
        *latest_version = version;
      } else {
        older_dirs->push_back(path);
      }
    } else {
      *latest_dir = path;
      *latest_version = version;
      found = true;
    }
  }
  return found;
}

bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
                               const base::Version& version,
                               content::PepperPluginInfo* plugin_info) {
  if (!version.IsValid() ||
      version.components().size() !=
          static_cast<size_t>(kWidevineCdmVersionNumComponents)) {
    return false;
  }

  plugin_info->is_internal = false;
  // Widevine CDM must run out of process.
  plugin_info->is_out_of_process = true;
  plugin_info->path = path;
  plugin_info->name = kWidevineCdmDisplayName;
  plugin_info->description = kWidevineCdmDescription;
  plugin_info->version = version.GetString();
  content::WebPluginMimeType widevine_cdm_mime_type(
      kWidevineCdmPluginMimeType,
      kWidevineCdmPluginExtension,
      kWidevineCdmPluginMimeTypeDescription);
  plugin_info->mime_types.push_back(widevine_cdm_mime_type);
  plugin_info->permissions = kWidevineCdmPluginPermissions;

  return true;
}

void RegisterWidevineCdmWithChrome(const base::FilePath& path,
                                   const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  content::PepperPluginInfo plugin_info;
  if (!MakeWidevineCdmPluginInfo(path, version, &plugin_info))
    return;

  PluginService::GetInstance()->RegisterInternalPlugin(
      plugin_info.ToWebPluginInfo(), true);
  PluginService::GetInstance()->RefreshPlugins();
}

// Returns true if this browser is compatible with the given Widevine CDM
// manifest, with the version specified in the manifest in |version_out|.
bool CheckWidevineCdmManifest(const base::DictionaryValue& manifest,
                              base::Version* version_out) {
  std::string name;
  manifest.GetStringASCII("name", &name);

  if (name != kWidevineCdmManifestName)
    return false;

  std::string proposed_version;
  manifest.GetStringASCII("version", &proposed_version);
  base::Version version(proposed_version.c_str());
  if (!version.IsValid())
    return false;

  *version_out = version;
  return true;
}

class WidevineCdmComponentInstaller : public ComponentInstaller {
 public:
  explicit WidevineCdmComponentInstaller(const base::Version& version);
  virtual ~WidevineCdmComponentInstaller() {}

  virtual void OnUpdateError(int error) OVERRIDE;
  virtual bool Install(const base::DictionaryValue& manifest,
                       const base::FilePath& unpack_path) OVERRIDE;

  virtual bool GetInstalledFile(const std::string& file,
                                base::FilePath* installed_file) OVERRIDE;

 private:
  base::Version current_version_;
};

WidevineCdmComponentInstaller::WidevineCdmComponentInstaller(
    const base::Version& version)
    : current_version_(version) {
  DCHECK(version.IsValid());
}

void WidevineCdmComponentInstaller::OnUpdateError(int error) {
  NOTREACHED() << "Widevine CDM update error: " << error;
}

bool WidevineCdmComponentInstaller::Install(
    const base::DictionaryValue& manifest,
    const base::FilePath& unpack_path) {
  base::Version version;
  if (!CheckWidevineCdmManifest(manifest, &version))
    return false;
  if (current_version_.CompareTo(version) > 0)
    return false;

  if (!base::PathExists(
      GetPlatformDirectory(unpack_path).AppendASCII(kWidevineCdmFileName))) {
    return false;
  }

  base::FilePath adapter_source_path;
  PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
  if (!base::PathExists(adapter_source_path))
    return false;

  // Passed the basic tests. Time to install it.
  base::FilePath install_path =
      GetWidevineCdmBaseDirectory().AppendASCII(version.GetString());
  if (base::PathExists(install_path))
    return false;
  if (!base::Move(unpack_path, install_path))
    return false;

  base::FilePath adapter_install_path = GetPlatformDirectory(install_path)
      .AppendASCII(kWidevineCdmAdapterFileName);
  if (!base::CopyFile(adapter_source_path, adapter_install_path))
    return false;

  // Installation is done. Now register the Widevine CDM with chrome.
  current_version_ = version;
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
      &RegisterWidevineCdmWithChrome, adapter_install_path, version));
  return true;
}

// Given |file|, a path like "_platform_specific/win_x86/widevinecdm.dll",
// returns the assumed install path. The path separator in |file| is '/'
// for all platforms. Caller is responsible for checking that the
// |installed_file| actually exists.
bool WidevineCdmComponentInstaller::GetInstalledFile(
    const std::string& file, base::FilePath* installed_file) {
  if (current_version_.Equals(base::Version(kNullVersion)))
    return false;  // No CDM has been installed yet.

  *installed_file = GetWidevineCdmBaseDirectory().AppendASCII(
      current_version_.GetString()).AppendASCII(file);
  return true;
}

void FinishWidevineCdmUpdateRegistration(ComponentUpdateService* cus,
                                         const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  CrxComponent widevine_cdm;
  widevine_cdm.name = "WidevineCdm";
  widevine_cdm.installer = new WidevineCdmComponentInstaller(version);
  widevine_cdm.version = version;
  widevine_cdm.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
  if (cus->RegisterComponent(widevine_cdm) != ComponentUpdateService::kOk) {
    NOTREACHED() << "Widevine CDM component registration failed.";
    return;
  }
}

void StartWidevineCdmUpdateRegistration(ComponentUpdateService* cus) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FilePath base_dir = GetWidevineCdmBaseDirectory();
  if (!base::PathExists(base_dir) &&
      !file_util::CreateDirectory(base_dir)) {
    NOTREACHED() << "Could not create Widevine CDM directory.";
    return;
  }

  base::FilePath latest_dir;
  base::Version version(kNullVersion);
  std::vector<base::FilePath> older_dirs;

  if (GetWidevineCdmDirectory(&latest_dir, &version, &older_dirs)) {
    base::FilePath latest_platform_dir = GetPlatformDirectory(latest_dir);
    base::FilePath adapter_path =
        latest_platform_dir.AppendASCII(kWidevineCdmAdapterFileName);
    base::FilePath cdm_path =
        latest_platform_dir.AppendASCII(kWidevineCdmFileName);

    if (base::PathExists(adapter_path) &&
        base::PathExists(cdm_path)) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          base::Bind(&RegisterWidevineCdmWithChrome, adapter_path, version));
    } else {
      base::DeleteFile(latest_dir, true);
      version = base::Version(kNullVersion);
    }
  }

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&FinishWidevineCdmUpdateRegistration, cus, version));

  // Remove older versions of Widevine CDM.
  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
       iter != older_dirs.end(); ++iter) {
    base::DeleteFile(*iter, true);
  }
}

#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

}  // namespace

void RegisterWidevineCdmComponent(ComponentUpdateService* cus) {
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
                          base::Bind(&StartWidevineCdmUpdateRegistration, cus));
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
}
<commit_msg>Refactor Widevine component installer to use DefaultComponentInstaller.<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/component_updater/widevine_cdm_component_installer.h"

#include <string.h>

#include <vector>

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/browser/component_updater/default_component_installer.h"
#include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/widevine_cdm_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/pepper_plugin_info.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"

#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.

using content::BrowserThread;
using content::PluginService;

#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

namespace {

// CRX hash. The extension id is: oimompecagnajdejgnnjijobebaeigek.
const uint8 kSha2Hash[] = { 0xe8, 0xce, 0xcf, 0x42, 0x06, 0xd0, 0x93, 0x49,
                            0x6d, 0xd9, 0x89, 0xe1, 0x41, 0x04, 0x86, 0x4a,
                            0x8f, 0xbd, 0x86, 0x12, 0xb9, 0x58, 0x9b, 0xfb,
                            0x4f, 0xbb, 0x1b, 0xa9, 0xd3, 0x85, 0x37, 0xef };

// File name of the Widevine CDM component manifest on different platforms.
const char kWidevineCdmManifestName[] = "WidevineCdm";

// Name of the Widevine CDM OS in the component manifest.
const char kWidevineCdmPlatform[] =
#if defined(OS_MACOSX)
    "mac";
#elif defined(OS_WIN)
    "win";
#else  // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android?
    "linux";
#endif

// Name of the Widevine CDM architecture in the component manifest.
const char kWidevineCdmArch[] =
#if defined(ARCH_CPU_X86)
    "x86";
#elif defined(ARCH_CPU_X86_64)
    "x64";
#else  // TODO(viettrungluu): Support an ARM check?
    "???";
#endif

// Widevine CDM is packaged as a multi-CRX. Widevine CDM binaries are located in
// _platform_specific/<platform_arch> folder in the package. This function
// returns the platform-specific subdirectory that is part of that multi-CRX.
base::FilePath GetPlatformDirectory(const base::FilePath& base_path) {
  std::string platform_arch = kWidevineCdmPlatform;
  platform_arch += '_';
  platform_arch += kWidevineCdmArch;
  return base_path.AppendASCII("_platform_specific").AppendASCII(platform_arch);
}

bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
                               const base::Version& version,
                               content::PepperPluginInfo* plugin_info) {
  if (!version.IsValid() ||
      version.components().size() !=
          static_cast<size_t>(kWidevineCdmVersionNumComponents)) {
    return false;
  }

  plugin_info->is_internal = false;
  // Widevine CDM must run out of process.
  plugin_info->is_out_of_process = true;
  plugin_info->path = path;
  plugin_info->name = kWidevineCdmDisplayName;
  plugin_info->description = kWidevineCdmDescription;
  plugin_info->version = version.GetString();
  content::WebPluginMimeType widevine_cdm_mime_type(
      kWidevineCdmPluginMimeType,
      kWidevineCdmPluginExtension,
      kWidevineCdmPluginMimeTypeDescription);
  plugin_info->mime_types.push_back(widevine_cdm_mime_type);
  plugin_info->permissions = kWidevineCdmPluginPermissions;

  return true;
}

void RegisterWidevineCdmWithChrome(const base::FilePath& path,
                                   const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  content::PepperPluginInfo plugin_info;
  if (!MakeWidevineCdmPluginInfo(path, version, &plugin_info))
    return;

  PluginService::GetInstance()->RegisterInternalPlugin(
      plugin_info.ToWebPluginInfo(), true);
  PluginService::GetInstance()->RefreshPlugins();
}

}  // namespace

class WidevineCdmComponentInstallerTraits : public ComponentInstallerTraits {
 public:
  WidevineCdmComponentInstallerTraits();
  virtual ~WidevineCdmComponentInstallerTraits() {}

 private:
  // The following methods override ComponentInstallerTraits.
  virtual bool CanAutoUpdate() const OVERRIDE;
  virtual bool OnCustomInstall(const base::DictionaryValue& manifest,
                               const base::FilePath& install_dir) OVERRIDE;
  virtual bool VerifyInstallation(
      const base::FilePath& install_dir) const OVERRIDE;
  virtual void ComponentReady(const base::Version& version,
                              const base::FilePath& path) OVERRIDE;
  virtual base::FilePath GetBaseDirectory() const OVERRIDE;
  virtual void GetHash(std::vector<uint8>* hash) const OVERRIDE;
  virtual std::string GetName() const OVERRIDE;
  DISALLOW_COPY_AND_ASSIGN(WidevineCdmComponentInstallerTraits);
};

WidevineCdmComponentInstallerTraits::WidevineCdmComponentInstallerTraits() {
}

bool WidevineCdmComponentInstallerTraits::CanAutoUpdate() const {
  return true;
}

// The adapter is copied into the install directory as part of the installation.
bool WidevineCdmComponentInstallerTraits::OnCustomInstall(
    const base::DictionaryValue& manifest,
    const base::FilePath& install_path) {
  base::FilePath adapter_install_path = GetPlatformDirectory(install_path)
      .AppendASCII(kWidevineCdmAdapterFileName);
  base::FilePath adapter_source_path;
  PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
  return base::CopyFile(adapter_source_path, adapter_install_path);
}

// Once the component is installed, register the new version with Chrome.
void WidevineCdmComponentInstallerTraits::ComponentReady(
    const base::Version& version,
    const base::FilePath& path) {
  base::FilePath adapter_install_path = GetPlatformDirectory(path)
      .AppendASCII(kWidevineCdmAdapterFileName);
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
      &RegisterWidevineCdmWithChrome, adapter_install_path, version));
}

bool WidevineCdmComponentInstallerTraits::VerifyInstallation(
    const base::FilePath& install_dir) const {
  return base::PathExists(GetPlatformDirectory(install_dir)
          .AppendASCII(kWidevineCdmFileName))
      && base::PathExists(GetPlatformDirectory(install_dir)
          .AppendASCII(kWidevineCdmAdapterFileName));
}

// The base directory on Windows looks like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\.
base::FilePath WidevineCdmComponentInstallerTraits::GetBaseDirectory() const {
  base::FilePath result;
  PathService::Get(chrome::DIR_COMPONENT_WIDEVINE_CDM, &result);
  return result;
}

void WidevineCdmComponentInstallerTraits::GetHash(
    std::vector<uint8>* hash) const {
  hash->assign(kSha2Hash, kSha2Hash + arraysize(kSha2Hash));
}

std::string WidevineCdmComponentInstallerTraits::GetName() const {
  return kWidevineCdmManifestName;
}

#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

void RegisterWidevineCdmComponent(ComponentUpdateService* cus) {
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
  base::FilePath adapter_source_path;
  PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
  if (!base::PathExists(adapter_source_path))
    return;
  scoped_ptr<ComponentInstallerTraits> traits(
      new WidevineCdmComponentInstallerTraits);
  // |cus| will take ownership of |installer| during installer->Register(cus).
  DefaultComponentInstaller* installer
      = new DefaultComponentInstaller(traits.Pass());
  installer->Register(cus);
#else
  return;
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
}
<|endoftext|>


<commit_before>/*
 * Handler for raw TCP connections.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_tcp.hxx"
#include "filtered_socket.hxx"
#include "address_list.hxx"
#include "client_balancer.hxx"
#include "address_sticky.h"
#include "async.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"

#include <unistd.h>
#include <errno.h>

struct LbTcpConnection final : ConnectSocketHandler {
    struct pool *pool;
    Stock *pipe_stock;

    const LbTcpConnectionHandler *handler;
    void *handler_ctx;

    FilteredSocket inbound;

    BufferedSocket outbound;

    struct async_operation_ref connect;

    bool got_inbound_data, got_outbound_data;

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;
    void OnSocketConnectTimeout() override;
    void OnSocketConnectError(GError *error) override;
};

static constexpr timeval write_timeout = { 30, 0 };

static void
lb_tcp_destroy_inbound(LbTcpConnection *tcp)
{
    if (tcp->inbound.IsConnected())
        tcp->inbound.Close();

    tcp->inbound.Destroy();
}

static void
lb_tcp_destroy_outbound(LbTcpConnection *tcp)
{
    if (tcp->outbound.IsConnected())
        tcp->outbound.Close();

    tcp->outbound.Destroy();
}

/*
 * inbound BufferedSocketHandler
 *
 */

static BufferedResult
inbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = true;

    if (tcp->connect.IsDefined())
        /* outbound is not yet connected */
        return BufferedResult::BLOCKING;

    if (!tcp->outbound.IsValid()) {
        lb_tcp_close(tcp);
        tcp->handler->error("Send error", "Broken socket", tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    ssize_t nbytes = tcp->outbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->inbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
inbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return false;
}

static bool
inbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = false;

    if (!tcp->outbound.Read(false))
        return false;

    if (!tcp->got_outbound_data)
        tcp->inbound.UnscheduleWrite();
    return true;
}

static bool
inbound_buffered_socket_drained(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    if (!tcp->outbound.IsValid()) {
        /* now that inbound's output buffers are drained, we can
           finally close the connection (postponed from
           outbound_buffered_socket_end()) */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return false;
    }

    return true;
}

static enum write_result
inbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
inbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler inbound_buffered_socket_handler = {
    inbound_buffered_socket_data,
    nullptr, // TODO: inbound_buffered_socket_direct,
    inbound_buffered_socket_closed,
    nullptr,
    nullptr,
    inbound_buffered_socket_write,
    inbound_buffered_socket_drained,
    nullptr,
    inbound_buffered_socket_broken,
    inbound_buffered_socket_error,
};

/*
 * outbound buffered_socket_handler
 *
 */

static BufferedResult
outbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = true;

    ssize_t nbytes = tcp->inbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->outbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
        int save_errno;

    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        save_errno = errno;
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", save_errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
outbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Close();
    return true;
}

static void
outbound_buffered_socket_end(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Destroy();

    tcp->inbound.UnscheduleWrite();

    if (tcp->inbound.IsDrained()) {
        /* all output buffers to "inbound" are drained; close the
           connection, because there's nothing left to do */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);

        /* nothing will be done if the buffers are not yet drained;
           we're waiting for inbound_buffered_socket_drained() to be
           called */
    }
}

static bool
outbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = false;

    if (!tcp->inbound.Read(false))
        return false;

    if (!tcp->got_inbound_data)
        tcp->outbound.UnscheduleWrite();
    return true;
}

static enum write_result
outbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
outbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler outbound_buffered_socket_handler = {
    outbound_buffered_socket_data,
    nullptr, // TODO: outbound_buffered_socket_direct,
    outbound_buffered_socket_closed,
    nullptr,
    outbound_buffered_socket_end,
    outbound_buffered_socket_write,
    nullptr,
    nullptr,
    outbound_buffered_socket_broken,
    outbound_buffered_socket_error,
};

/*
 * ConnectSocketHandler
 *
 */

void
LbTcpConnection::OnSocketConnectSuccess(SocketDescriptor &&fd)
{
    connect.Clear();

    outbound.Init(*pool,
                  fd.Steal(), FdType::FD_TCP,
                  nullptr, &write_timeout,
                  outbound_buffered_socket_handler, this);

    /* TODO
    outbound.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0 &&
        (istream_direct_mask_to(inbound.base.base.fd_type) & FdType::FD_PIPE) != 0;
    */

    if (inbound.Read(false))
        outbound.Read(false);
}

void
LbTcpConnection::OnSocketConnectTimeout()
{
    lb_tcp_destroy_inbound(this);
    handler->error("Connect error", "Timeout", handler_ctx);
}

void
LbTcpConnection::OnSocketConnectError(GError *error)
{
    lb_tcp_destroy_inbound(this);
    handler->gerror("Connect error", error, handler_ctx);
}

/*
 * constructor
 *
 */

gcc_pure
static unsigned
lb_tcp_sticky(const AddressList &address_list,
              const struct sockaddr *remote_address)
{
    switch (address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        break;

    case StickyMode::SOURCE_IP:
        return socket_address_sticky(remote_address);

    case StickyMode::SESSION_MODULO:
    case StickyMode::COOKIE:
    case StickyMode::JVM_ROUTE:
        /* not implemented here */
        break;
    }

    return 0;
}

void
lb_tcp_new(struct pool *pool, Stock *pipe_stock,
           SocketDescriptor &&fd, FdType fd_type,
           const SocketFilter *filter, void *filter_ctx,
           SocketAddress remote_address,
           bool transparent_source,
           const AddressList &address_list,
           struct balancer &balancer,
           const LbTcpConnectionHandler *handler, void *ctx,
           LbTcpConnection **tcp_r)
{
    auto *tcp = NewFromPool<LbTcpConnection>(*pool);
    tcp->pool = pool;
    tcp->pipe_stock = pipe_stock;
    tcp->handler = handler;
    tcp->handler_ctx = ctx;

    tcp->inbound.Init(*pool, fd.Steal(), fd_type,
                      nullptr, &write_timeout,
                      filter, filter_ctx,
                      inbound_buffered_socket_handler, tcp);
    /* TODO
    tcp->inbound.base.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_PIPE & fd_type) != 0 &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0;
    */

    unsigned session_sticky = lb_tcp_sticky(address_list,
                                            remote_address.GetAddress());

    SocketAddress bind_address = SocketAddress::Null();

    if (transparent_source) {
        bind_address = remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetFamily() == AF_INET) {
            struct sockaddr_in *s_in = (struct sockaddr_in *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        } else if (bind_address.GetFamily() == AF_INET6) {
            struct sockaddr_in6 *s_in = (struct sockaddr_in6 *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin6_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        }
    }

    *tcp_r = tcp;

    client_balancer_connect(pool, &balancer,
                            transparent_source,
                            bind_address,
                            session_sticky,
                            &address_list,
                            20,
                            *tcp,
                            &tcp->connect);
}

void
lb_tcp_close(LbTcpConnection *tcp)
{
    if (tcp->inbound.IsValid())
        lb_tcp_destroy_inbound(tcp);

    if (tcp->connect.IsDefined())
        tcp->connect.Abort();
    else if (tcp->outbound.IsValid())
        lb_tcp_destroy_outbound(tcp);
}
<commit_msg>lb_tcp: move functions into the struct<commit_after>/*
 * Handler for raw TCP connections.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_tcp.hxx"
#include "filtered_socket.hxx"
#include "address_list.hxx"
#include "client_balancer.hxx"
#include "address_sticky.h"
#include "async.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"

#include <unistd.h>
#include <errno.h>

struct LbTcpConnection final : ConnectSocketHandler {
    struct pool *pool;
    Stock *pipe_stock;

    const LbTcpConnectionHandler *handler;
    void *handler_ctx;

    FilteredSocket inbound;

    BufferedSocket outbound;

    struct async_operation_ref connect;

    bool got_inbound_data, got_outbound_data;

    void DestroyInbound();
    void DestroyOutbound();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;
    void OnSocketConnectTimeout() override;
    void OnSocketConnectError(GError *error) override;
};

static constexpr timeval write_timeout = { 30, 0 };

void
LbTcpConnection::DestroyInbound()
{
    if (inbound.IsConnected())
        inbound.Close();

    inbound.Destroy();
}

void
LbTcpConnection::DestroyOutbound()
{
    if (outbound.IsConnected())
        outbound.Close();

    outbound.Destroy();
}

/*
 * inbound BufferedSocketHandler
 *
 */

static BufferedResult
inbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = true;

    if (tcp->connect.IsDefined())
        /* outbound is not yet connected */
        return BufferedResult::BLOCKING;

    if (!tcp->outbound.IsValid()) {
        lb_tcp_close(tcp);
        tcp->handler->error("Send error", "Broken socket", tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    ssize_t nbytes = tcp->outbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->inbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
inbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return false;
}

static bool
inbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = false;

    if (!tcp->outbound.Read(false))
        return false;

    if (!tcp->got_outbound_data)
        tcp->inbound.UnscheduleWrite();
    return true;
}

static bool
inbound_buffered_socket_drained(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    if (!tcp->outbound.IsValid()) {
        /* now that inbound's output buffers are drained, we can
           finally close the connection (postponed from
           outbound_buffered_socket_end()) */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return false;
    }

    return true;
}

static enum write_result
inbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
inbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler inbound_buffered_socket_handler = {
    inbound_buffered_socket_data,
    nullptr, // TODO: inbound_buffered_socket_direct,
    inbound_buffered_socket_closed,
    nullptr,
    nullptr,
    inbound_buffered_socket_write,
    inbound_buffered_socket_drained,
    nullptr,
    inbound_buffered_socket_broken,
    inbound_buffered_socket_error,
};

/*
 * outbound buffered_socket_handler
 *
 */

static BufferedResult
outbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = true;

    ssize_t nbytes = tcp->inbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->outbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
        int save_errno;

    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        save_errno = errno;
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", save_errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
outbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Close();
    return true;
}

static void
outbound_buffered_socket_end(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Destroy();

    tcp->inbound.UnscheduleWrite();

    if (tcp->inbound.IsDrained()) {
        /* all output buffers to "inbound" are drained; close the
           connection, because there's nothing left to do */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);

        /* nothing will be done if the buffers are not yet drained;
           we're waiting for inbound_buffered_socket_drained() to be
           called */
    }
}

static bool
outbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = false;

    if (!tcp->inbound.Read(false))
        return false;

    if (!tcp->got_inbound_data)
        tcp->outbound.UnscheduleWrite();
    return true;
}

static enum write_result
outbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
outbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler outbound_buffered_socket_handler = {
    outbound_buffered_socket_data,
    nullptr, // TODO: outbound_buffered_socket_direct,
    outbound_buffered_socket_closed,
    nullptr,
    outbound_buffered_socket_end,
    outbound_buffered_socket_write,
    nullptr,
    nullptr,
    outbound_buffered_socket_broken,
    outbound_buffered_socket_error,
};

/*
 * ConnectSocketHandler
 *
 */

void
LbTcpConnection::OnSocketConnectSuccess(SocketDescriptor &&fd)
{
    connect.Clear();

    outbound.Init(*pool,
                  fd.Steal(), FdType::FD_TCP,
                  nullptr, &write_timeout,
                  outbound_buffered_socket_handler, this);

    /* TODO
    outbound.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0 &&
        (istream_direct_mask_to(inbound.base.base.fd_type) & FdType::FD_PIPE) != 0;
    */

    if (inbound.Read(false))
        outbound.Read(false);
}

void
LbTcpConnection::OnSocketConnectTimeout()
{
    DestroyInbound();
    handler->error("Connect error", "Timeout", handler_ctx);
}

void
LbTcpConnection::OnSocketConnectError(GError *error)
{
    DestroyInbound();
    handler->gerror("Connect error", error, handler_ctx);
}

/*
 * constructor
 *
 */

gcc_pure
static unsigned
lb_tcp_sticky(const AddressList &address_list,
              const struct sockaddr *remote_address)
{
    switch (address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        break;

    case StickyMode::SOURCE_IP:
        return socket_address_sticky(remote_address);

    case StickyMode::SESSION_MODULO:
    case StickyMode::COOKIE:
    case StickyMode::JVM_ROUTE:
        /* not implemented here */
        break;
    }

    return 0;
}

void
lb_tcp_new(struct pool *pool, Stock *pipe_stock,
           SocketDescriptor &&fd, FdType fd_type,
           const SocketFilter *filter, void *filter_ctx,
           SocketAddress remote_address,
           bool transparent_source,
           const AddressList &address_list,
           struct balancer &balancer,
           const LbTcpConnectionHandler *handler, void *ctx,
           LbTcpConnection **tcp_r)
{
    auto *tcp = NewFromPool<LbTcpConnection>(*pool);
    tcp->pool = pool;
    tcp->pipe_stock = pipe_stock;
    tcp->handler = handler;
    tcp->handler_ctx = ctx;

    tcp->inbound.Init(*pool, fd.Steal(), fd_type,
                      nullptr, &write_timeout,
                      filter, filter_ctx,
                      inbound_buffered_socket_handler, tcp);
    /* TODO
    tcp->inbound.base.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_PIPE & fd_type) != 0 &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0;
    */

    unsigned session_sticky = lb_tcp_sticky(address_list,
                                            remote_address.GetAddress());

    SocketAddress bind_address = SocketAddress::Null();

    if (transparent_source) {
        bind_address = remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetFamily() == AF_INET) {
            struct sockaddr_in *s_in = (struct sockaddr_in *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        } else if (bind_address.GetFamily() == AF_INET6) {
            struct sockaddr_in6 *s_in = (struct sockaddr_in6 *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin6_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        }
    }

    *tcp_r = tcp;

    client_balancer_connect(pool, &balancer,
                            transparent_source,
                            bind_address,
                            session_sticky,
                            &address_list,
                            20,
                            *tcp,
                            &tcp->connect);
}

void
lb_tcp_close(LbTcpConnection *tcp)
{
    if (tcp->inbound.IsValid())
        tcp->DestroyInbound();

    if (tcp->connect.IsDefined())
        tcp->connect.Abort();
    else if (tcp->outbound.IsValid())
        tcp->DestroyOutbound();
}
<|endoftext|>


<commit_before>/*
 * Handler for raw TCP connections.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_tcp.hxx"
#include "filtered_socket.hxx"
#include "address_list.hxx"
#include "client_balancer.hxx"
#include "address_sticky.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "async.hxx"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"

#include <unistd.h>
#include <errno.h>

struct LbTcpConnection final : ConnectSocketHandler {
    struct pool *pool;
    Stock *pipe_stock;

    const LbTcpConnectionHandler *handler;
    void *handler_ctx;

    FilteredSocket inbound;

    BufferedSocket outbound;

    struct async_operation_ref connect;

    bool got_inbound_data, got_outbound_data;

    explicit LbTcpConnection(EventLoop &event_loop)
        :inbound(event_loop), outbound(event_loop) {}

    void DestroyInbound();
    void DestroyOutbound();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;
    void OnSocketConnectTimeout() override;
    void OnSocketConnectError(GError *error) override;
};

static constexpr timeval write_timeout = { 30, 0 };

void
LbTcpConnection::DestroyInbound()
{
    if (inbound.IsConnected())
        inbound.Close();

    inbound.Destroy();
}

void
LbTcpConnection::DestroyOutbound()
{
    if (outbound.IsConnected())
        outbound.Close();

    outbound.Destroy();
}

/*
 * inbound BufferedSocketHandler
 *
 */

static BufferedResult
inbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = true;

    if (tcp->connect.IsDefined())
        /* outbound is not yet connected */
        return BufferedResult::BLOCKING;

    if (!tcp->outbound.IsValid()) {
        lb_tcp_close(tcp);
        tcp->handler->error("Send error", "Broken socket", tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    ssize_t nbytes = tcp->outbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->inbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
inbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return false;
}

static bool
inbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = false;

    if (!tcp->outbound.Read(false))
        return false;

    if (!tcp->got_outbound_data)
        tcp->inbound.UnscheduleWrite();
    return true;
}

static bool
inbound_buffered_socket_drained(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    if (!tcp->outbound.IsValid()) {
        /* now that inbound's output buffers are drained, we can
           finally close the connection (postponed from
           outbound_buffered_socket_end()) */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return false;
    }

    return true;
}

static enum write_result
inbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
inbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler inbound_buffered_socket_handler = {
    inbound_buffered_socket_data,
    nullptr, // TODO: inbound_buffered_socket_direct,
    inbound_buffered_socket_closed,
    nullptr,
    nullptr,
    inbound_buffered_socket_write,
    inbound_buffered_socket_drained,
    nullptr,
    inbound_buffered_socket_broken,
    inbound_buffered_socket_error,
};

/*
 * outbound buffered_socket_handler
 *
 */

static BufferedResult
outbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = true;

    ssize_t nbytes = tcp->inbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->outbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
        int save_errno;

    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        save_errno = errno;
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", save_errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
outbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Close();
    return true;
}

static void
outbound_buffered_socket_end(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Destroy();

    tcp->inbound.UnscheduleWrite();

    if (tcp->inbound.IsDrained()) {
        /* all output buffers to "inbound" are drained; close the
           connection, because there's nothing left to do */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);

        /* nothing will be done if the buffers are not yet drained;
           we're waiting for inbound_buffered_socket_drained() to be
           called */
    }
}

static bool
outbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = false;

    if (!tcp->inbound.Read(false))
        return false;

    if (!tcp->got_inbound_data)
        tcp->outbound.UnscheduleWrite();
    return true;
}

static enum write_result
outbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
outbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler outbound_buffered_socket_handler = {
    outbound_buffered_socket_data,
    nullptr, // TODO: outbound_buffered_socket_direct,
    outbound_buffered_socket_closed,
    nullptr,
    outbound_buffered_socket_end,
    outbound_buffered_socket_write,
    nullptr,
    nullptr,
    outbound_buffered_socket_broken,
    outbound_buffered_socket_error,
};

/*
 * ConnectSocketHandler
 *
 */

void
LbTcpConnection::OnSocketConnectSuccess(SocketDescriptor &&fd)
{
    connect.Clear();

    outbound.Init(fd.Steal(), FdType::FD_TCP,
                  nullptr, &write_timeout,
                  outbound_buffered_socket_handler, this);

    /* TODO
    outbound.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0 &&
        (istream_direct_mask_to(inbound.base.base.fd_type) & FdType::FD_PIPE) != 0;
    */

    if (inbound.Read(false))
        outbound.Read(false);
}

void
LbTcpConnection::OnSocketConnectTimeout()
{
    DestroyInbound();
    handler->error("Connect error", "Timeout", handler_ctx);
}

void
LbTcpConnection::OnSocketConnectError(GError *error)
{
    DestroyInbound();
    handler->gerror("Connect error", error, handler_ctx);
}

/*
 * constructor
 *
 */

gcc_pure
static unsigned
lb_tcp_sticky(const AddressList &address_list,
              SocketAddress remote_address)
{
    switch (address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        break;

    case StickyMode::SOURCE_IP:
        return socket_address_sticky(remote_address);

    case StickyMode::SESSION_MODULO:
    case StickyMode::COOKIE:
    case StickyMode::JVM_ROUTE:
        /* not implemented here */
        break;
    }

    return 0;
}

void
lb_tcp_new(struct pool *pool, EventLoop &event_loop, Stock *pipe_stock,
           SocketDescriptor &&fd, FdType fd_type,
           const SocketFilter *filter, void *filter_ctx,
           SocketAddress remote_address,
           bool transparent_source,
           const AddressList &address_list,
           Balancer &balancer,
           const LbTcpConnectionHandler *handler, void *ctx,
           LbTcpConnection **tcp_r)
{
    auto *tcp = NewFromPool<LbTcpConnection>(*pool, event_loop);
    tcp->pool = pool;
    tcp->pipe_stock = pipe_stock;
    tcp->handler = handler;
    tcp->handler_ctx = ctx;

    tcp->inbound.Init(fd.Steal(), fd_type,
                      nullptr, &write_timeout,
                      filter, filter_ctx,
                      inbound_buffered_socket_handler, tcp);
    /* TODO
    tcp->inbound.base.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_PIPE & fd_type) != 0 &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0;
    */

    unsigned session_sticky = lb_tcp_sticky(address_list,
                                            remote_address);

    SocketAddress bind_address = SocketAddress::Null();

    if (transparent_source) {
        bind_address = remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetFamily() == AF_INET) {
            struct sockaddr_in *s_in = (struct sockaddr_in *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        } else if (bind_address.GetFamily() == AF_INET6) {
            struct sockaddr_in6 *s_in = (struct sockaddr_in6 *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin6_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        }
    }

    *tcp_r = tcp;

    client_balancer_connect(event_loop, *pool, balancer,
                            transparent_source,
                            bind_address,
                            session_sticky,
                            &address_list,
                            20,
                            *tcp,
                            tcp->connect);
}

void
lb_tcp_close(LbTcpConnection *tcp)
{
    if (tcp->inbound.IsValid())
        tcp->DestroyInbound();

    if (tcp->connect.IsDefined())
        tcp->connect.Abort();
    else if (tcp->outbound.IsValid())
        tcp->DestroyOutbound();
}
<commit_msg>lb_tcp: migrate to CancellablePointer<commit_after>/*
 * Handler for raw TCP connections.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_tcp.hxx"
#include "filtered_socket.hxx"
#include "address_list.hxx"
#include "client_balancer.hxx"
#include "address_sticky.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"
#include "util/Cancellable.hxx"

#include <unistd.h>
#include <errno.h>

struct LbTcpConnection final : ConnectSocketHandler {
    struct pool *pool;
    Stock *pipe_stock;

    const LbTcpConnectionHandler *handler;
    void *handler_ctx;

    FilteredSocket inbound;

    BufferedSocket outbound;

    CancellablePointer cancel_connect;

    bool got_inbound_data, got_outbound_data;

    explicit LbTcpConnection(EventLoop &event_loop)
        :inbound(event_loop), outbound(event_loop) {}

    void DestroyInbound();
    void DestroyOutbound();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;
    void OnSocketConnectTimeout() override;
    void OnSocketConnectError(GError *error) override;
};

static constexpr timeval write_timeout = { 30, 0 };

void
LbTcpConnection::DestroyInbound()
{
    if (inbound.IsConnected())
        inbound.Close();

    inbound.Destroy();
}

void
LbTcpConnection::DestroyOutbound()
{
    if (outbound.IsConnected())
        outbound.Close();

    outbound.Destroy();
}

/*
 * inbound BufferedSocketHandler
 *
 */

static BufferedResult
inbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = true;

    if (tcp->cancel_connect)
        /* outbound is not yet connected */
        return BufferedResult::BLOCKING;

    if (!tcp->outbound.IsValid()) {
        lb_tcp_close(tcp);
        tcp->handler->error("Send error", "Broken socket", tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    ssize_t nbytes = tcp->outbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->inbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
inbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return false;
}

static bool
inbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = false;

    if (!tcp->outbound.Read(false))
        return false;

    if (!tcp->got_outbound_data)
        tcp->inbound.UnscheduleWrite();
    return true;
}

static bool
inbound_buffered_socket_drained(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    if (!tcp->outbound.IsValid()) {
        /* now that inbound's output buffers are drained, we can
           finally close the connection (postponed from
           outbound_buffered_socket_end()) */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return false;
    }

    return true;
}

static enum write_result
inbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
inbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler inbound_buffered_socket_handler = {
    inbound_buffered_socket_data,
    nullptr, // TODO: inbound_buffered_socket_direct,
    inbound_buffered_socket_closed,
    nullptr,
    nullptr,
    inbound_buffered_socket_write,
    inbound_buffered_socket_drained,
    nullptr,
    inbound_buffered_socket_broken,
    inbound_buffered_socket_error,
};

/*
 * outbound buffered_socket_handler
 *
 */

static BufferedResult
outbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = true;

    ssize_t nbytes = tcp->inbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->outbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
        int save_errno;

    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        save_errno = errno;
        lb_tcp_close(tcp);
        tcp->handler->_errno("Send failed", save_errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
outbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Close();
    return true;
}

static void
outbound_buffered_socket_end(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Destroy();

    tcp->inbound.UnscheduleWrite();

    if (tcp->inbound.IsDrained()) {
        /* all output buffers to "inbound" are drained; close the
           connection, because there's nothing left to do */
        lb_tcp_close(tcp);
        tcp->handler->eof(tcp->handler_ctx);

        /* nothing will be done if the buffers are not yet drained;
           we're waiting for inbound_buffered_socket_drained() to be
           called */
    }
}

static bool
outbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = false;

    if (!tcp->inbound.Read(false))
        return false;

    if (!tcp->got_inbound_data)
        tcp->outbound.UnscheduleWrite();
    return true;
}

static enum write_result
outbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
outbound_buffered_socket_error(GError *error, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    lb_tcp_close(tcp);
    tcp->handler->gerror("Error", error, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler outbound_buffered_socket_handler = {
    outbound_buffered_socket_data,
    nullptr, // TODO: outbound_buffered_socket_direct,
    outbound_buffered_socket_closed,
    nullptr,
    outbound_buffered_socket_end,
    outbound_buffered_socket_write,
    nullptr,
    nullptr,
    outbound_buffered_socket_broken,
    outbound_buffered_socket_error,
};

/*
 * ConnectSocketHandler
 *
 */

void
LbTcpConnection::OnSocketConnectSuccess(SocketDescriptor &&fd)
{
    cancel_connect = nullptr;

    outbound.Init(fd.Steal(), FdType::FD_TCP,
                  nullptr, &write_timeout,
                  outbound_buffered_socket_handler, this);

    /* TODO
    outbound.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0 &&
        (istream_direct_mask_to(inbound.base.base.fd_type) & FdType::FD_PIPE) != 0;
    */

    if (inbound.Read(false))
        outbound.Read(false);
}

void
LbTcpConnection::OnSocketConnectTimeout()
{
    DestroyInbound();
    handler->error("Connect error", "Timeout", handler_ctx);
}

void
LbTcpConnection::OnSocketConnectError(GError *error)
{
    DestroyInbound();
    handler->gerror("Connect error", error, handler_ctx);
}

/*
 * constructor
 *
 */

gcc_pure
static unsigned
lb_tcp_sticky(const AddressList &address_list,
              SocketAddress remote_address)
{
    switch (address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        break;

    case StickyMode::SOURCE_IP:
        return socket_address_sticky(remote_address);

    case StickyMode::SESSION_MODULO:
    case StickyMode::COOKIE:
    case StickyMode::JVM_ROUTE:
        /* not implemented here */
        break;
    }

    return 0;
}

void
lb_tcp_new(struct pool *pool, EventLoop &event_loop, Stock *pipe_stock,
           SocketDescriptor &&fd, FdType fd_type,
           const SocketFilter *filter, void *filter_ctx,
           SocketAddress remote_address,
           bool transparent_source,
           const AddressList &address_list,
           Balancer &balancer,
           const LbTcpConnectionHandler *handler, void *ctx,
           LbTcpConnection **tcp_r)
{
    auto *tcp = NewFromPool<LbTcpConnection>(*pool, event_loop);
    tcp->pool = pool;
    tcp->pipe_stock = pipe_stock;
    tcp->handler = handler;
    tcp->handler_ctx = ctx;

    tcp->inbound.Init(fd.Steal(), fd_type,
                      nullptr, &write_timeout,
                      filter, filter_ctx,
                      inbound_buffered_socket_handler, tcp);
    /* TODO
    tcp->inbound.base.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_PIPE & fd_type) != 0 &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0;
    */

    unsigned session_sticky = lb_tcp_sticky(address_list,
                                            remote_address);

    SocketAddress bind_address = SocketAddress::Null();

    if (transparent_source) {
        bind_address = remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetFamily() == AF_INET) {
            struct sockaddr_in *s_in = (struct sockaddr_in *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        } else if (bind_address.GetFamily() == AF_INET6) {
            struct sockaddr_in6 *s_in = (struct sockaddr_in6 *)
                p_memdup(pool, bind_address.GetAddress(),
                         bind_address.GetSize());
            s_in->sin6_port = 0;
            bind_address = SocketAddress((const struct sockaddr *)s_in,
                                         bind_address.GetSize());
        }
    }

    *tcp_r = tcp;

    client_balancer_connect(event_loop, *pool, balancer,
                            transparent_source,
                            bind_address,
                            session_sticky,
                            &address_list,
                            20,
                            *tcp,
                            tcp->cancel_connect);
}

void
lb_tcp_close(LbTcpConnection *tcp)
{
    if (tcp->inbound.IsValid())
        tcp->DestroyInbound();

    if (tcp->cancel_connect)
        tcp->cancel_connect.Cancel();
    else if (tcp->outbound.IsValid())
        tcp->DestroyOutbound();
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// chromeos::MediaDeviceNotifications implementation.

#include "chrome/browser/system_monitor/media_device_notifications_chromeos.h"

#include "base/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/system_monitor/media_device_notifications_utils.h"
#include "chrome/browser/system_monitor/media_storage_util.h"
#include "content/public/browser/browser_thread.h"

namespace chromeos {

namespace {

bool GetDeviceInfo(const std::string& source_path, std::string* unique_id,
                   string16* device_label) {
  // Get the media device uuid and label if exists.
  const disks::DiskMountManager::Disk* disk =
      disks::DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path);
  if (!disk)
    return false;

  *unique_id = disk->fs_uuid();

  // TODO(kmadhusu): If device label is empty, extract vendor and model details
  // and use them as device_label.
  *device_label = UTF8ToUTF16(disk->device_label().empty() ?
                              FilePath(source_path).BaseName().value() :
                              disk->device_label());
  return true;
}

}  // namespace

using content::BrowserThread;

MediaDeviceNotifications::MediaDeviceNotifications() {
  DCHECK(disks::DiskMountManager::GetInstance());
  disks::DiskMountManager::GetInstance()->AddObserver(this);
  CheckExistingMountPointsOnUIThread();
}

MediaDeviceNotifications::~MediaDeviceNotifications() {
  disks::DiskMountManager* manager = disks::DiskMountManager::GetInstance();
  if (manager) {
    manager->RemoveObserver(this);
  }
}

void MediaDeviceNotifications::CheckExistingMountPointsOnUIThread() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  const disks::DiskMountManager::MountPointMap& mount_point_map =
      disks::DiskMountManager::GetInstance()->mount_points();
  for (disks::DiskMountManager::MountPointMap::const_iterator it =
           mount_point_map.begin(); it != mount_point_map.end(); ++it) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        base::Bind(&MediaDeviceNotifications::CheckMountedPathOnFileThread,
                   this, it->second));
  }
}

void MediaDeviceNotifications::DiskChanged(
    disks::DiskMountManagerEventType event,
    const disks::DiskMountManager::Disk* disk) {
}

void MediaDeviceNotifications::DeviceChanged(
    disks::DiskMountManagerEventType event,
    const std::string& device_path) {
}

void MediaDeviceNotifications::MountCompleted(
    disks::DiskMountManager::MountEvent event_type,
    MountError error_code,
    const disks::DiskMountManager::MountPointInfo& mount_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Ignore mount points that are not devices.
  if (mount_info.mount_type != MOUNT_TYPE_DEVICE)
    return;
  // Ignore errors.
  if (error_code != MOUNT_ERROR_NONE)
    return;
  if (mount_info.mount_condition != disks::MOUNT_CONDITION_NONE)
    return;

  switch (event_type) {
    case disks::DiskMountManager::MOUNTING: {
      if (ContainsKey(mount_map_, mount_info.mount_path)) {
        NOTREACHED();
        return;
      }

      BrowserThread::PostTask(
          BrowserThread::FILE, FROM_HERE,
          base::Bind(&MediaDeviceNotifications::CheckMountedPathOnFileThread,
                     this, mount_info));
      break;
    }
    case disks::DiskMountManager::UNMOUNTING: {
      MountMap::iterator it = mount_map_.find(mount_info.mount_path);
      if (it == mount_map_.end())
        return;
      base::SystemMonitor::Get()->ProcessRemovableStorageDetached(it->second);
      mount_map_.erase(it);
      break;
    }
  }
}

void MediaDeviceNotifications::CheckMountedPathOnFileThread(
    const disks::DiskMountManager::MountPointInfo& mount_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  if (!chrome::IsMediaDevice(mount_info.mount_path))
    return;

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&MediaDeviceNotifications::AddMountedPathOnUIThread,
                 this, mount_info));
}

void MediaDeviceNotifications::AddMountedPathOnUIThread(
    const disks::DiskMountManager::MountPointInfo& mount_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (ContainsKey(mount_map_, mount_info.mount_path)) {
    NOTREACHED();
    return;
  }

  // Get the media device uuid and label if exists.
  std::string unique_id;
  string16 device_label;
  if (!GetDeviceInfo(mount_info.source_path, &unique_id, &device_label))
    return;

  // Keep track of device uuid, to see how often we receive empty uuid values.
  UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.device_uuid_available",
                        !unique_id.empty());
  if (unique_id.empty())
    return;

  std::string device_id = chrome::MediaStorageUtil::MakeDeviceId(
      chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM, unique_id);
  mount_map_.insert(std::make_pair(mount_info.mount_path, device_id));
  base::SystemMonitor::Get()->ProcessRemovableStorageAttached(
      device_id,
      device_label,
      mount_info.mount_path);
}

}  // namespace chrome
<commit_msg>Fix MediaDeviceNotification histogram name.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// chromeos::MediaDeviceNotifications implementation.

#include "chrome/browser/system_monitor/media_device_notifications_chromeos.h"

#include "base/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/system_monitor/media_device_notifications_utils.h"
#include "chrome/browser/system_monitor/media_storage_util.h"
#include "content/public/browser/browser_thread.h"

namespace chromeos {

namespace {

bool GetDeviceInfo(const std::string& source_path, std::string* unique_id,
                   string16* device_label) {
  // Get the media device uuid and label if exists.
  const disks::DiskMountManager::Disk* disk =
      disks::DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path);
  if (!disk)
    return false;

  *unique_id = disk->fs_uuid();

  // TODO(kmadhusu): If device label is empty, extract vendor and model details
  // and use them as device_label.
  *device_label = UTF8ToUTF16(disk->device_label().empty() ?
                              FilePath(source_path).BaseName().value() :
                              disk->device_label());
  return true;
}

}  // namespace

using content::BrowserThread;

MediaDeviceNotifications::MediaDeviceNotifications() {
  DCHECK(disks::DiskMountManager::GetInstance());
  disks::DiskMountManager::GetInstance()->AddObserver(this);
  CheckExistingMountPointsOnUIThread();
}

MediaDeviceNotifications::~MediaDeviceNotifications() {
  disks::DiskMountManager* manager = disks::DiskMountManager::GetInstance();
  if (manager) {
    manager->RemoveObserver(this);
  }
}

void MediaDeviceNotifications::CheckExistingMountPointsOnUIThread() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  const disks::DiskMountManager::MountPointMap& mount_point_map =
      disks::DiskMountManager::GetInstance()->mount_points();
  for (disks::DiskMountManager::MountPointMap::const_iterator it =
           mount_point_map.begin(); it != mount_point_map.end(); ++it) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        base::Bind(&MediaDeviceNotifications::CheckMountedPathOnFileThread,
                   this, it->second));
  }
}

void MediaDeviceNotifications::DiskChanged(
    disks::DiskMountManagerEventType event,
    const disks::DiskMountManager::Disk* disk) {
}

void MediaDeviceNotifications::DeviceChanged(
    disks::DiskMountManagerEventType event,
    const std::string& device_path) {
}

void MediaDeviceNotifications::MountCompleted(
    disks::DiskMountManager::MountEvent event_type,
    MountError error_code,
    const disks::DiskMountManager::MountPointInfo& mount_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Ignore mount points that are not devices.
  if (mount_info.mount_type != MOUNT_TYPE_DEVICE)
    return;
  // Ignore errors.
  if (error_code != MOUNT_ERROR_NONE)
    return;
  if (mount_info.mount_condition != disks::MOUNT_CONDITION_NONE)
    return;

  switch (event_type) {
    case disks::DiskMountManager::MOUNTING: {
      if (ContainsKey(mount_map_, mount_info.mount_path)) {
        NOTREACHED();
        return;
      }

      BrowserThread::PostTask(
          BrowserThread::FILE, FROM_HERE,
          base::Bind(&MediaDeviceNotifications::CheckMountedPathOnFileThread,
                     this, mount_info));
      break;
    }
    case disks::DiskMountManager::UNMOUNTING: {
      MountMap::iterator it = mount_map_.find(mount_info.mount_path);
      if (it == mount_map_.end())
        return;
      base::SystemMonitor::Get()->ProcessRemovableStorageDetached(it->second);
      mount_map_.erase(it);
      break;
    }
  }
}

void MediaDeviceNotifications::CheckMountedPathOnFileThread(
    const disks::DiskMountManager::MountPointInfo& mount_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  if (!chrome::IsMediaDevice(mount_info.mount_path))
    return;

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&MediaDeviceNotifications::AddMountedPathOnUIThread,
                 this, mount_info));
}

void MediaDeviceNotifications::AddMountedPathOnUIThread(
    const disks::DiskMountManager::MountPointInfo& mount_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (ContainsKey(mount_map_, mount_info.mount_path)) {
    NOTREACHED();
    return;
  }

  // Get the media device uuid and label if exists.
  std::string unique_id;
  string16 device_label;
  if (!GetDeviceInfo(mount_info.source_path, &unique_id, &device_label))
    return;

  // Keep track of device uuid, to see how often we receive empty uuid values.
  UMA_HISTOGRAM_BOOLEAN("MediaDeviceNotification.DeviceUUIDAvailable",
                        !unique_id.empty());
  if (unique_id.empty())
    return;

  std::string device_id = chrome::MediaStorageUtil::MakeDeviceId(
      chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM, unique_id);
  mount_map_.insert(std::make_pair(mount_info.mount_path, device_id));
  base::SystemMonitor::Get()->ProcessRemovableStorageAttached(
      device_id,
      device_label,
      mount_info.mount_path);
}

}  // namespace chrome
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2013 The Communi Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*/

#include "messagestackview.h"
#include "settingsmodel.h"
#include "application.h"
#include "zncmanager.h"
#include "completer.h"
#include "session.h"
#include <ircbuffer.h>
#include <irccommand.h>
#include <ircbuffermodel.h>

class BufferModel : public IrcBufferModel
{
public:
    BufferModel(QObject* parent = 0) : IrcBufferModel(parent) { }

protected:
    void destroyBuffer(IrcBuffer* buffer) { Q_UNUSED(buffer); }
};

MessageStackView::MessageStackView(IrcSession* session, QWidget* parent) : QStackedWidget(parent)
{
    d.session = session;

    d.bufferModel = new BufferModel(session);
    connect(d.bufferModel, SIGNAL(bufferAdded(IrcBuffer*)), this, SLOT(setBuffer(IrcBuffer*)));
    connect(d.bufferModel, SIGNAL(messageIgnored(IrcMessage*)), &d.handler, SLOT(handleMessage(IrcMessage*)));
    connect(d.bufferModel, SIGNAL(channelsChanged(QStringList)), &d.parser, SLOT(setChannels(QStringList)));

    session->installMessageFilter(&d.handler);
    session->installMessageFilter(qobject_cast<Session*>(session)); // TODO
    d.handler.znc()->setModel(d.bufferModel);

    connect(this, SIGNAL(currentChanged(int)), this, SLOT(activateView(int)));

    connect(&d.handler, SIGNAL(viewToBeAdded(QString)), this, SLOT(addView(QString)));
    connect(&d.handler, SIGNAL(viewToBeRemoved(QString)), this, SLOT(removeView(QString)));
    connect(&d.handler, SIGNAL(viewToBeRenamed(QString, QString)), this, SLOT(renameView(QString, QString)));

    MessageView* view = addView(session->host());
    d.handler.setDefaultView(view);
    d.handler.setCurrentView(view);
    setCurrentWidget(view);

    applySettings();
    connect(Application::settings(), SIGNAL(changed()), this, SLOT(applySettings()));
}

IrcSession* MessageStackView::session() const
{
    return d.session;
}

CommandParser* MessageStackView::parser() const
{
    return &const_cast<MessageStackView*>(this)->d.parser;
}

QStringListModel* MessageStackView::commandModel() const
{
    return &const_cast<MessageStackView*>(this)->d.commandModel;
}

MessageView* MessageStackView::currentView() const
{
    return qobject_cast<MessageView*>(currentWidget());
}

MessageView* MessageStackView::viewAt(int index) const
{
    return qobject_cast<MessageView*>(widget(index));
}

MessageView* MessageStackView::addView(const QString& receiver)
{
    MessageView* view = d.views.value(receiver.toLower());
    bool channel = !receiver.isEmpty() && IrcSessionInfo(d.session).channelTypes().contains(receiver.at(0));
    if (!view) {
        ViewInfo::Type type = ViewInfo::Server;
        if (!d.views.isEmpty())
            type = channel ? ViewInfo::Channel : ViewInfo::Query;
        view = createView(type, receiver);
    }
    if (channel && !view->isActive())
        openView(receiver);
    return view;
}

void MessageStackView::restoreView(const ViewInfo& view)
{
    createView(static_cast<ViewInfo::Type>(view.type), view.name);
}

MessageView* MessageStackView::createView(ViewInfo::Type type, const QString& receiver)
{
    MessageView* view = new MessageView(type, static_cast<Session*>(d.session), this); // TODO
    // TODO:
    if (IrcSessionInfo(session()).isValid())
        view->completer()->setChannelPrefixes(IrcSessionInfo(session()).channelTypes().join(""));
    view->completer()->setChannelModel(&d.viewModel);
    view->setReceiver(receiver);
    connect(view, SIGNAL(queried(QString)), this, SLOT(addView(QString)));
    connect(view, SIGNAL(queried(QString)), this, SLOT(openView(QString)));
    connect(view, SIGNAL(messaged(QString,QString)), this, SLOT(sendMessage(QString,QString)));

    d.handler.addView(receiver, view);
    d.views.insert(receiver.toLower(), view);
    addWidget(view);
    d.viewModel.setStringList(d.viewModel.stringList() << receiver);
    emit viewAdded(view);
    return view;
}

void MessageStackView::openView(const QString& receiver)
{
    MessageView* view = d.views.value(receiver.toLower());
    if (view)
        setCurrentWidget(view);
}

void MessageStackView::removeView(const QString& receiver)
{
    MessageView* view = d.views.take(receiver.toLower());
    if (view) {
        view->deleteLater();
        QStringList views = d.viewModel.stringList();
        if (views.removeOne(receiver))
            d.viewModel.setStringList(views);
        emit viewRemoved(view);
        d.handler.removeView(view->receiver());
    }
}

void MessageStackView::closeView(int index)
{
    MessageView* view = viewAt(index);
    if (view) {
        if (view->isActive()) {
            if (indexOf(view) == 0)
                static_cast<Session*>(session())->quit(); // TODO
            else if (view->viewType() == ViewInfo::Channel)
                d.session->sendCommand(IrcCommand::createPart(view->receiver()));
        }
        d.handler.removeView(view->receiver());
    }
}

void MessageStackView::renameView(const QString& from, const QString& to)
{
    if (!d.views.contains(to.toLower())) {
        MessageView* view = d.views.take(from.toLower());
        if (view) {
            view->setReceiver(to);
            d.views.insert(to.toLower(), view);
            emit viewRenamed(view);
        }
    } else if (currentView() == d.views.value(from.toLower())) {
        setCurrentWidget(d.views.value(to.toLower()));
    }
}

void MessageStackView::sendMessage(const QString& receiver, const QString& message)
{
    MessageView* view = addView(receiver);
    if (view) {
        setCurrentWidget(view);
        view->sendMessage(message);
    }
}

void MessageStackView::applySettings()
{
    SettingsModel* settings = Application::settings();
    d.handler.znc()->setTimeStampFormat(settings->value("formatting.timeStamp").toString());

    QMap<QString,QString> aliases;
    QVariantMap values = settings->values("aliases.*");
    QMapIterator<QString,QVariant> it(values);
    while (it.hasNext()) {
        it.next();
        aliases[it.key().mid(8).toUpper()] = it.value().toString();
    }
    d.parser.setAliases(aliases);

    QStringList commands;
    foreach (const QString& command, d.parser.availableCommands())
        commands += d.parser.prefix() + command;
    d.commandModel.setStringList(commands);
}

void MessageStackView::activateView(int index)
{
    MessageView* view = viewAt(index);
    if (view && isVisible()) {
        d.handler.setCurrentView(view);
        d.parser.setCurrentTarget(view->receiver());
        view->setFocus();
        emit viewActivated(view);
    }
}

void MessageStackView::setBuffer(IrcBuffer* buffer)
{
    MessageView* view = addView(buffer->title().toLower());
    if (view)
        view->setBuffer(buffer);
}
<commit_msg>Fix MessageStackView::setBuffer() not to force lower case title<commit_after>/*
* Copyright (C) 2008-2013 The Communi Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*/

#include "messagestackview.h"
#include "settingsmodel.h"
#include "application.h"
#include "zncmanager.h"
#include "completer.h"
#include "session.h"
#include <ircbuffer.h>
#include <irccommand.h>
#include <ircbuffermodel.h>

class BufferModel : public IrcBufferModel
{
public:
    BufferModel(QObject* parent = 0) : IrcBufferModel(parent) { }

protected:
    void destroyBuffer(IrcBuffer* buffer) { Q_UNUSED(buffer); }
};

MessageStackView::MessageStackView(IrcSession* session, QWidget* parent) : QStackedWidget(parent)
{
    d.session = session;

    d.bufferModel = new BufferModel(session);
    connect(d.bufferModel, SIGNAL(bufferAdded(IrcBuffer*)), this, SLOT(setBuffer(IrcBuffer*)));
    connect(d.bufferModel, SIGNAL(messageIgnored(IrcMessage*)), &d.handler, SLOT(handleMessage(IrcMessage*)));
    connect(d.bufferModel, SIGNAL(channelsChanged(QStringList)), &d.parser, SLOT(setChannels(QStringList)));

    session->installMessageFilter(&d.handler);
    session->installMessageFilter(qobject_cast<Session*>(session)); // TODO
    d.handler.znc()->setModel(d.bufferModel);

    connect(this, SIGNAL(currentChanged(int)), this, SLOT(activateView(int)));

    connect(&d.handler, SIGNAL(viewToBeAdded(QString)), this, SLOT(addView(QString)));
    connect(&d.handler, SIGNAL(viewToBeRemoved(QString)), this, SLOT(removeView(QString)));
    connect(&d.handler, SIGNAL(viewToBeRenamed(QString, QString)), this, SLOT(renameView(QString, QString)));

    MessageView* view = addView(session->host());
    d.handler.setDefaultView(view);
    d.handler.setCurrentView(view);
    setCurrentWidget(view);

    applySettings();
    connect(Application::settings(), SIGNAL(changed()), this, SLOT(applySettings()));
}

IrcSession* MessageStackView::session() const
{
    return d.session;
}

CommandParser* MessageStackView::parser() const
{
    return &const_cast<MessageStackView*>(this)->d.parser;
}

QStringListModel* MessageStackView::commandModel() const
{
    return &const_cast<MessageStackView*>(this)->d.commandModel;
}

MessageView* MessageStackView::currentView() const
{
    return qobject_cast<MessageView*>(currentWidget());
}

MessageView* MessageStackView::viewAt(int index) const
{
    return qobject_cast<MessageView*>(widget(index));
}

MessageView* MessageStackView::addView(const QString& receiver)
{
    MessageView* view = d.views.value(receiver.toLower());
    bool channel = !receiver.isEmpty() && IrcSessionInfo(d.session).channelTypes().contains(receiver.at(0));
    if (!view) {
        ViewInfo::Type type = ViewInfo::Server;
        if (!d.views.isEmpty())
            type = channel ? ViewInfo::Channel : ViewInfo::Query;
        view = createView(type, receiver);
    }
    if (channel && !view->isActive())
        openView(receiver);
    return view;
}

void MessageStackView::restoreView(const ViewInfo& view)
{
    createView(static_cast<ViewInfo::Type>(view.type), view.name);
}

MessageView* MessageStackView::createView(ViewInfo::Type type, const QString& receiver)
{
    MessageView* view = new MessageView(type, static_cast<Session*>(d.session), this); // TODO
    // TODO:
    if (IrcSessionInfo(session()).isValid())
        view->completer()->setChannelPrefixes(IrcSessionInfo(session()).channelTypes().join(""));
    view->completer()->setChannelModel(&d.viewModel);
    view->setReceiver(receiver);
    connect(view, SIGNAL(queried(QString)), this, SLOT(addView(QString)));
    connect(view, SIGNAL(queried(QString)), this, SLOT(openView(QString)));
    connect(view, SIGNAL(messaged(QString,QString)), this, SLOT(sendMessage(QString,QString)));

    d.handler.addView(receiver, view);
    d.views.insert(receiver.toLower(), view);
    addWidget(view);
    d.viewModel.setStringList(d.viewModel.stringList() << receiver);
    emit viewAdded(view);
    return view;
}

void MessageStackView::openView(const QString& receiver)
{
    MessageView* view = d.views.value(receiver.toLower());
    if (view)
        setCurrentWidget(view);
}

void MessageStackView::removeView(const QString& receiver)
{
    MessageView* view = d.views.take(receiver.toLower());
    if (view) {
        view->deleteLater();
        QStringList views = d.viewModel.stringList();
        if (views.removeOne(receiver))
            d.viewModel.setStringList(views);
        emit viewRemoved(view);
        d.handler.removeView(view->receiver());
    }
}

void MessageStackView::closeView(int index)
{
    MessageView* view = viewAt(index);
    if (view) {
        if (view->isActive()) {
            if (indexOf(view) == 0)
                static_cast<Session*>(session())->quit(); // TODO
            else if (view->viewType() == ViewInfo::Channel)
                d.session->sendCommand(IrcCommand::createPart(view->receiver()));
        }
        d.handler.removeView(view->receiver());
    }
}

void MessageStackView::renameView(const QString& from, const QString& to)
{
    if (!d.views.contains(to.toLower())) {
        MessageView* view = d.views.take(from.toLower());
        if (view) {
            view->setReceiver(to);
            d.views.insert(to.toLower(), view);
            emit viewRenamed(view);
        }
    } else if (currentView() == d.views.value(from.toLower())) {
        setCurrentWidget(d.views.value(to.toLower()));
    }
}

void MessageStackView::sendMessage(const QString& receiver, const QString& message)
{
    MessageView* view = addView(receiver);
    if (view) {
        setCurrentWidget(view);
        view->sendMessage(message);
    }
}

void MessageStackView::applySettings()
{
    SettingsModel* settings = Application::settings();
    d.handler.znc()->setTimeStampFormat(settings->value("formatting.timeStamp").toString());

    QMap<QString,QString> aliases;
    QVariantMap values = settings->values("aliases.*");
    QMapIterator<QString,QVariant> it(values);
    while (it.hasNext()) {
        it.next();
        aliases[it.key().mid(8).toUpper()] = it.value().toString();
    }
    d.parser.setAliases(aliases);

    QStringList commands;
    foreach (const QString& command, d.parser.availableCommands())
        commands += d.parser.prefix() + command;
    d.commandModel.setStringList(commands);
}

void MessageStackView::activateView(int index)
{
    MessageView* view = viewAt(index);
    if (view && isVisible()) {
        d.handler.setCurrentView(view);
        d.parser.setCurrentTarget(view->receiver());
        view->setFocus();
        emit viewActivated(view);
    }
}

void MessageStackView::setBuffer(IrcBuffer* buffer)
{
    MessageView* view = addView(buffer->title());
    if (view)
        view->setBuffer(buffer);
}
<|endoftext|>