<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/VFS.h"
#include "fnord-rpc/ServerGroup.h"
#include "fnord-rpc/RPC.h"
#include "fnord-rpc/RPCClient.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-json/json.h"
#include "fnord-json/jsonrpc.h"
#include "fnord-http/httprouter.h"
#include "fnord-http/httpserver.h"
#include "fnord-http/VFSFileServlet.h"
#include "fnord-feeds/FeedService.h"
#include "fnord-feeds/RemoteFeedFactory.h"
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord-base/stats/statsdagent.h"
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-eventdb/EventDBServlet.h"
#include "fnord-eventdb/TableRepository.h"
#include "fnord-eventdb/TableJanitor.h"
#include "fnord-eventdb/TableReplication.h"
#include "fnord-eventdb/ArtifactReplication.h"
#include "fnord-eventdb/NumericBoundsSummary.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "common.h"
#include "schemas.h"
#include "CustomerNamespace.h"
#include "FeatureSchema.h"
#include "JoinedQuery.h"
#include "analytics/AnalyticsServlet.h"
#include "analytics/CTRByPageServlet.h"
#include "analytics/CTRStatsServlet.h"
#include "analytics/CTRByPositionQuery.h"
#include "analytics/CTRByPageQuery.h"
#include "analytics/TopSearchQueriesQuery.h"
#include "analytics/DiscoveryKPIQuery.h"
#include "analytics/DiscoveryCategoryStatsQuery.h"
#include "analytics/AnalyticsQueryEngine.h"
#include "analytics/AnalyticsQueryEngine.h"

using namespace fnord;

std::atomic<bool> shutdown_sig;
fnord::thread::EventLoop ev;

void quit(int n) {
  shutdown_sig = true;
  fnord::logInfo("cm.chunkserver", "Shutting down...");
  // FIXPAUL: wait for http server stop...
  ev.shutdown();
}

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  /* shutdown hook */
  shutdown_sig = false;
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = quit;
  sigaction(SIGTERM, &sa, NULL);
  sigaction(SIGQUIT, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "http_port",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "8000",
      "Start the public http server on this port",
      "<port>");

  flags.defineFlag(
      "readonly",
      cli::FlagParser::T_SWITCH,
      false,
      NULL,
      NULL,
      "readonly",
      "readonly");

  flags.defineFlag(
      "replica",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "replica id",
      "<id>");

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "datadir path",
      "<path>");

  flags.defineFlag(
      "replicate_from",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "url",
      "<url>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* start http server */
  fnord::thread::ThreadPool tpool;
  fnord::http::HTTPRouter http_router;
  fnord::http::HTTPServer http_server(&http_router, &ev);
  http_server.listen(flags.getInt("http_port"));

  /* eventdb */
  auto dir = flags.getString("datadir");
  auto readonly = flags.isSet("readonly");
  auto replica = flags.getString("replica");

  Set<String> tbls  = { "dawanda_joined_sessions", "joined_sessions-dawanda" };
  http::HTTPConnectionPool http(&ev);
  eventdb::ArtifactIndex artifacts(dir, replica, readonly);
  eventdb::TableRepository table_repo(
      &artifacts,
      dir,
      replica,
      readonly,
      &tpool);

  auto joined_sessions_schema = joinedSessionsSchema();
  for (const auto& tbl : tbls) {
    table_repo.addTable(tbl, joined_sessions_schema);
  }

  if (!readonly) {
    for (const auto& tbl : tbls) {
      auto joined_sessions_table =table_repo.findTableWriter(tbl);

      joined_sessions_table->addSummary(
          [joined_sessions_schema] () -> RefPtr<eventdb::TableChunkSummaryBuilder> {
            return new eventdb::NumericBoundsSummaryBuilder(
                "queries.time-bounds",
                joined_sessions_schema.id("queries.time"));
          });
    }
  }

  eventdb::TableReplication table_replication(&http);
  eventdb::ArtifactReplication artifact_replication(
      &artifacts,
      &http,
      &tpool,
      8);

  for (const auto& rep : flags.getStrings("replicate_from")) {
    for (const auto& tbl : tbls) {
      table_replication.replicateTableFrom(
          table_repo.findTableWriter(tbl),
          URI(StringUtil::format("http://$0:7003/eventdb", rep)));
    }

    artifact_replication.addSource(
        URI(StringUtil::format("http://$0:7005/chunks", rep)));
  }

  eventdb::TableJanitor table_janitor(&table_repo);
  if (!readonly) {
    table_janitor.start();
    table_replication.start();
    artifact_replication.start();
  }

  eventdb::EventDBServlet eventdb_servlet(&table_repo);

  /* analytics */
  cm::AnalyticsQueryEngine analytics(32, dir, &table_repo);
  cm::AnalyticsServlet analytics_servlet(&analytics);
  http_router.addRouteByPrefixMatch("/analytics", &analytics_servlet, &tpool);
  http_router.addRouteByPrefixMatch("/eventdb", &eventdb_servlet, &tpool);

  analytics.registerQueryFactory("ctr_by_position", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPositionQuery(scan, segments);
  });

  analytics.registerQueryFactory("ctr_by_page", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPageQuery(scan, segments);
  });

  analytics.registerQueryFactory("discovery_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryKPIQuery(
        scan,
        segments,
        query.start_time,
        query.end_time);
  });

  analytics.registerQueryFactory("discovery_category0_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category1",
        "queries.category1",
        params);
  });

  analytics.registerQueryFactory("discovery_category1_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category1",
        "queries.category2",
        params);
  });

  analytics.registerQueryFactory("discovery_category2_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category2",
        "queries.category3",
        params);
  });

  analytics.registerQueryFactory("discovery_category3_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category3",
        "queries.category3",
        params);
  });

  analytics.registerQueryFactory("top_search_queries", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::TopSearchQueriesQuery(scan, segments, params);
  });

  ev.run();

  if (!readonly) {
    table_janitor.stop();
    table_janitor.check();
    table_replication.stop();
    artifact_replication.stop();
  }

  fnord::logInfo("cm.chunkserver", "Exiting...");

  exit(0);
}

<commit_msg>use FixedSizeThreadPool<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/FixedSizeThreadPool.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/VFS.h"
#include "fnord-rpc/ServerGroup.h"
#include "fnord-rpc/RPC.h"
#include "fnord-rpc/RPCClient.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-json/json.h"
#include "fnord-json/jsonrpc.h"
#include "fnord-http/httprouter.h"
#include "fnord-http/httpserver.h"
#include "fnord-http/VFSFileServlet.h"
#include "fnord-feeds/FeedService.h"
#include "fnord-feeds/RemoteFeedFactory.h"
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord-base/stats/statsdagent.h"
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-eventdb/EventDBServlet.h"
#include "fnord-eventdb/TableRepository.h"
#include "fnord-eventdb/TableJanitor.h"
#include "fnord-eventdb/TableReplication.h"
#include "fnord-eventdb/ArtifactReplication.h"
#include "fnord-eventdb/NumericBoundsSummary.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "common.h"
#include "schemas.h"
#include "CustomerNamespace.h"
#include "FeatureSchema.h"
#include "JoinedQuery.h"
#include "analytics/AnalyticsServlet.h"
#include "analytics/CTRByPageServlet.h"
#include "analytics/CTRStatsServlet.h"
#include "analytics/CTRByPositionQuery.h"
#include "analytics/CTRByPageQuery.h"
#include "analytics/TopSearchQueriesQuery.h"
#include "analytics/DiscoveryKPIQuery.h"
#include "analytics/DiscoveryCategoryStatsQuery.h"
#include "analytics/AnalyticsQueryEngine.h"
#include "analytics/AnalyticsQueryEngine.h"

using namespace fnord;

std::atomic<bool> shutdown_sig;
fnord::thread::EventLoop ev;

void quit(int n) {
  shutdown_sig = true;
  fnord::logInfo("cm.chunkserver", "Shutting down...");
  // FIXPAUL: wait for http server stop...
  ev.shutdown();
}

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  /* shutdown hook */
  shutdown_sig = false;
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = quit;
  sigaction(SIGTERM, &sa, NULL);
  sigaction(SIGQUIT, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "http_port",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "8000",
      "Start the public http server on this port",
      "<port>");

  flags.defineFlag(
      "readonly",
      cli::FlagParser::T_SWITCH,
      false,
      NULL,
      NULL,
      "readonly",
      "readonly");

  flags.defineFlag(
      "replica",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "replica id",
      "<id>");

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "datadir path",
      "<path>");

  flags.defineFlag(
      "replicate_from",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "url",
      "<url>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* start http server */
  fnord::thread::ThreadPool tpool;
  fnord::thread::FixedSizeThreadPool wpool(8);
  fnord::http::HTTPRouter http_router;
  fnord::http::HTTPServer http_server(&http_router, &ev);
  http_server.listen(flags.getInt("http_port"));

  /* eventdb */
  auto dir = flags.getString("datadir");
  auto readonly = flags.isSet("readonly");
  auto replica = flags.getString("replica");

  Set<String> tbls  = { "dawanda_joined_sessions", "joined_sessions-dawanda" };
  http::HTTPConnectionPool http(&ev);
  eventdb::ArtifactIndex artifacts(dir, replica, readonly);
  eventdb::TableRepository table_repo(
      &artifacts,
      dir,
      replica,
      readonly,
      &wpool);

  auto joined_sessions_schema = joinedSessionsSchema();
  for (const auto& tbl : tbls) {
    table_repo.addTable(tbl, joined_sessions_schema);
  }

  if (!readonly) {
    for (const auto& tbl : tbls) {
      auto joined_sessions_table =table_repo.findTableWriter(tbl);

      joined_sessions_table->addSummary(
          [joined_sessions_schema] () -> RefPtr<eventdb::TableChunkSummaryBuilder> {
            return new eventdb::NumericBoundsSummaryBuilder(
                "queries.time-bounds",
                joined_sessions_schema.id("queries.time"));
          });
    }
  }

  eventdb::TableReplication table_replication(&http);
  eventdb::ArtifactReplication artifact_replication(
      &artifacts,
      &http,
      &wpool,
      8);

  for (const auto& rep : flags.getStrings("replicate_from")) {
    for (const auto& tbl : tbls) {
      table_replication.replicateTableFrom(
          table_repo.findTableWriter(tbl),
          URI(StringUtil::format("http://$0:7003/eventdb", rep)));
    }

    artifact_replication.addSource(
        URI(StringUtil::format("http://$0:7005/chunks", rep)));
  }

  eventdb::TableJanitor table_janitor(&table_repo);
  if (!readonly) {
    table_janitor.start();
    table_replication.start();
    artifact_replication.start();
  }

  eventdb::EventDBServlet eventdb_servlet(&table_repo);

  /* analytics */
  cm::AnalyticsQueryEngine analytics(32, dir, &table_repo);
  cm::AnalyticsServlet analytics_servlet(&analytics);
  http_router.addRouteByPrefixMatch("/analytics", &analytics_servlet, &tpool);
  http_router.addRouteByPrefixMatch("/eventdb", &eventdb_servlet, &tpool);

  analytics.registerQueryFactory("ctr_by_position", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPositionQuery(scan, segments);
  });

  analytics.registerQueryFactory("ctr_by_page", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPageQuery(scan, segments);
  });

  analytics.registerQueryFactory("discovery_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryKPIQuery(
        scan,
        segments,
        query.start_time,
        query.end_time);
  });

  analytics.registerQueryFactory("discovery_category0_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category1",
        "queries.category1",
        params);
  });

  analytics.registerQueryFactory("discovery_category1_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category1",
        "queries.category2",
        params);
  });

  analytics.registerQueryFactory("discovery_category2_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category2",
        "queries.category3",
        params);
  });

  analytics.registerQueryFactory("discovery_category3_kpis", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "queries.category3",
        "queries.category3",
        params);
  });

  analytics.registerQueryFactory("top_search_queries", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::TopSearchQueriesQuery(scan, segments, params);
  });

  ev.run();

  if (!readonly) {
    table_janitor.stop();
    table_janitor.check();
    table_replication.stop();
    artifact_replication.stop();
  }

  fnord::logInfo("cm.chunkserver", "Exiting...");

  exit(0);
}

<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2018-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed in accordance with the terms specified in
 *  the LICENSE file found in the root directory of this source tree.
 */

#include <osquery/extensions.h>
#include <osquery/registry_factory.h>
#include <osquery/registry_interface.h>
#include <osquery/utils/conversions/split.h>

namespace osquery {
void RegistryInterface::remove(const std::string& item_name) {
  if (items_.count(item_name) > 0) {
    items_[item_name]->tearDown();
    items_.erase(item_name);
  }

  // Populate list of aliases to remove (those that mask item_name).
  std::vector<std::string> removed_aliases;
  for (const auto& alias : aliases_) {
    if (alias.second == item_name) {
      removed_aliases.push_back(alias.first);
    }
  }

  for (const auto& alias : removed_aliases) {
    aliases_.erase(alias);
  }
}

bool RegistryInterface::isInternal(const std::string& item_name) const {
  ReadLock lock(mutex_);

  return isInternal_(item_name);
}

std::map<std::string, RouteUUID> RegistryInterface::getExternal() const {
  ReadLock lock(mutex_);

  return external_;
}

std::string RegistryInterface::getActive() const {
  ReadLock lock(mutex_);

  return active_;
}

std::string RegistryInterface::getName() const {
  ReadLock lock(mutex_);

  return name_;
}

size_t RegistryInterface::count() const {
  ReadLock lock(mutex_);

  return items_.size();
}

Status RegistryInterface::setActive(const std::string& item_name) {
  UpgradeLock lock(mutex_);

  // Default support multiple active plugins.
  for (const auto& item : osquery::split(item_name, ",")) {
    if (items_.count(item) == 0 && external_.count(item) == 0) {
      return Status(1, "Unknown registry plugin: " + item);
    }
  }

  Status status;
  {
    WriteUpgradeLock wlock(lock);
    active_ = item_name;
  }

  // The active plugin is setup when initialized.
  for (const auto& item : osquery::split(item_name, ",")) {
    if (exists_(item, true)) {
      status = RegistryFactory::get().plugin(name_, item)->setUp();
    } else if (exists_(item, false) && !RegistryFactory::get().external()) {
      // If the active plugin is within an extension we must wait.
      // An extension will first broadcast the registry, then receive the list
      // of active plugins, active them if they are extension-local, and finally
      // start their extension socket.
      status = pingExtension(getExtensionSocket(external_.at(item)));
    }

    if (!status.ok()) {
      break;
    }
  }
  return status;
}

RegistryRoutes RegistryInterface::getRoutes() const {
  ReadLock lock(mutex_);

  RegistryRoutes route_table;
  for (const auto& item : items_) {
    if (isInternal_(item.first)) {
      // This is an internal plugin, do not include the route.
      continue;
    }

    bool has_alias = false;
    for (const auto& alias : aliases_) {
      if (alias.second == item.first) {
        // If the item name is masked by at least one alias, it will not
        // broadcast under the internal item name.
        route_table[alias.first] = item.second->routeInfo();
        has_alias = true;
      }
    }

    if (!has_alias) {
      route_table[item.first] = item.second->routeInfo();
    }
  }
  return route_table;
}

Status RegistryInterface::call(const std::string& item_name,
                               const PluginRequest& request,
                               PluginResponse& response) {
  PluginRef plugin;
  {
    ReadLock lock(mutex_);

    // Search local plugins (items) for the plugin.
    if (items_.count(item_name) > 0) {
      plugin = items_.at(item_name);
    }
  }
  if (plugin) {
    return plugin->call(request, response);
  }

  RouteUUID uuid;
  {
    ReadLock lock(mutex_);

    // Check if the item was broadcasted as a plugin within an extension.
    if (external_.count(item_name) > 0) {
      // The item is a registered extension, call the extension by UUID.
      uuid = external_.at(item_name);
    } else if (routes_.count(item_name) > 0) {
      // The item has a route, but no extension, pass in the route info.
      response = routes_.at(item_name);
      return Status(0, "Route only");
    } else if (RegistryFactory::get().external()) {
      // If this is an extension's registry forward unknown calls to the core.
      uuid = 0;
    } else {
      return Status(1, "Cannot call registry item: " + item_name);
    }
  }

  return callExtension(uuid, name_, item_name, request, response);
}

Status RegistryInterface::addAlias(const std::string& item_name,
                                   const std::string& alias) {
  WriteLock lock(mutex_);

  if (aliases_.count(alias) > 0) {
    return Status(1, "Duplicate alias: " + alias);
  }
  aliases_[alias] = item_name;
  return Status(0, "OK");
}

std::string RegistryInterface::getAlias(const std::string& alias) const {
  ReadLock lock(mutex_);

  if (aliases_.count(alias) == 0) {
    return alias;
  }
  return aliases_.at(alias);
}

Status RegistryInterface::addPlugin(const std::string& plugin_name,
                                    const PluginRef& plugin_item,
                                    bool internal) {
  WriteLock lock(mutex_);

  if (items_.count(plugin_name) > 0) {
    return Status(1, "Duplicate registry item exists: " + plugin_name);
  }

  plugin_item->setName(plugin_name);
  items_.emplace(std::make_pair(plugin_name, plugin_item));

  // The item can be listed as internal, meaning it does not broadcast.
  if (internal) {
    internal_.push_back(plugin_name);
  }

  return Status(0, "OK");
}

void RegistryInterface::setUp() {
  ReadLock lock(mutex_);

  // If this registry does not auto-setup do NOT setup the registry items.
  if (!auto_setup_) {
    return;
  }

  // If the registry is using a single 'active' plugin, setUp that plugin.
  // For config and logger, only setUp the selected plugin.
  if (active_.size() != 0 && exists_(active_, true)) {
    items_.at(active_)->setUp();
    return;
  }

  // Try to set up each of the registry items.
  // If they fail, remove them from the registry.
  std::vector<std::string> failed;
  for (auto& item : items_) {
    if (!item.second->setUp().ok()) {
      failed.push_back(item.first);
    }
  }

  for (const auto& failed_item : failed) {
    remove(failed_item);
  }
}

void RegistryInterface::configure() {
  ReadLock lock(mutex_);

  if (!active_.empty() && exists_(active_, true)) {
    items_.at(active_)->configure();
  } else {
    for (auto& item : items_) {
      item.second->configure();
    }
  }
}

Status RegistryInterface::addExternal(const RouteUUID& uuid,
                                      const RegistryRoutes& routes) {
  // Add each route name (item name) to the tracking.
  for (const auto& route : routes) {
    // Keep the routes info assigned to the registry.
    {
      WriteLock wlock(mutex_);
      routes_[route.first] = route.second;
    }

    auto status = addExternalPlugin(route.first, route.second);

    if (status.ok()) {
      WriteLock wlock(mutex_);
      external_[route.first] = uuid;
    } else {
      return status;
    }
  }

  return Status(0, "OK");
}

/// Remove all the routes for a given uuid.
void RegistryInterface::removeExternal(const RouteUUID& uuid) {
  std::vector<std::string> removed_items;

  {
    ReadLock rlock(mutex_);
    // Create list of items to remove by filtering uuid
    for (const auto& item : external_) {
      if (item.second == uuid) {
        removed_items.push_back(item.first);
      }
    }
  }

  for (const auto& item : removed_items) {
    removeExternalPlugin(item);
  }

  {
    WriteLock wlock(mutex_);
    // Remove items belonging to the external uuid.
    for (const auto& item : removed_items) {
      external_.erase(item);
      routes_.erase(item);
    }
  }
}

/// Facility method to check if a registry item exists.
bool RegistryInterface::exists(const std::string& item_name, bool local) const {
  ReadLock lock(mutex_);

  return exists_(item_name, local);
}

/// Facility method to list the registry item identifiers.
std::vector<std::string> RegistryInterface::names() const {
  ReadLock lock(mutex_);

  std::vector<std::string> names;
  for (const auto& item : items_) {
    names.push_back(item.first);
  }

  // Also add names of external plugins.
  for (const auto& item : external_) {
    names.push_back(item.first);
  }
  return names;
}

std::map<std::string, PluginRef> RegistryInterface::plugins() {
  ReadLock lock(mutex_);

  return items_;
}

void RegistryInterface::setname(const std::string& name) {
  WriteLock lock(mutex_);

  name_ = name;
}

bool RegistryInterface::isInternal_(const std::string& item_name) const {
  if (std::find(internal_.begin(), internal_.end(), item_name) ==
      internal_.end()) {
    return false;
  }
  return true;
}

bool RegistryInterface::exists_(const std::string& item_name,
                                bool local) const {
  bool has_local = (items_.count(item_name) > 0);
  bool has_external = (external_.count(item_name) > 0);
  bool has_route = (routes_.count(item_name) > 0);
  return (local) ? has_local : has_local || has_external || has_route;
}

AutoRegisterInterface::AutoRegisterInterface(const char* _type,
                                             const char* _name,
                                             bool optional)
    : type_(_type), name_(_name), optional_(optional) {}

AutoRegisterSet& AutoRegisterInterface::registries() {
  static AutoRegisterSet registries_;
  return registries_;
}

AutoRegisterSet& AutoRegisterInterface::plugins() {
  static AutoRegisterSet plugins_;
  return plugins_;
}

void AutoRegisterInterface::autoloadRegistry(
    std::unique_ptr<AutoRegisterInterface> ar_) {
  registries().push_back(std::move(ar_));
}

void AutoRegisterInterface::autoloadPlugin(
    std::unique_ptr<AutoRegisterInterface> ar_) {
  plugins().push_back(std::move(ar_));
}

void registryAndPluginInit() {
  for (const auto& it : AutoRegisterInterface::registries()) {
    it->run();
  }

  for (const auto& it : AutoRegisterInterface::plugins()) {
    it->run();
  }

  AutoRegisterSet().swap(AutoRegisterInterface::registries());
  AutoRegisterSet().swap(AutoRegisterInterface::plugins());
}

} // namespace osquery
<commit_msg>Add error message for when we don't send an item name to the registry lookup (#5464)<commit_after>/**
 *  Copyright (c) 2018-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed in accordance with the terms specified in
 *  the LICENSE file found in the root directory of this source tree.
 */

#include <osquery/extensions.h>
#include <osquery/registry_factory.h>
#include <osquery/registry_interface.h>
#include <osquery/utils/conversions/split.h>

namespace osquery {
void RegistryInterface::remove(const std::string& item_name) {
  if (items_.count(item_name) > 0) {
    items_[item_name]->tearDown();
    items_.erase(item_name);
  }

  // Populate list of aliases to remove (those that mask item_name).
  std::vector<std::string> removed_aliases;
  for (const auto& alias : aliases_) {
    if (alias.second == item_name) {
      removed_aliases.push_back(alias.first);
    }
  }

  for (const auto& alias : removed_aliases) {
    aliases_.erase(alias);
  }
}

bool RegistryInterface::isInternal(const std::string& item_name) const {
  ReadLock lock(mutex_);

  return isInternal_(item_name);
}

std::map<std::string, RouteUUID> RegistryInterface::getExternal() const {
  ReadLock lock(mutex_);

  return external_;
}

std::string RegistryInterface::getActive() const {
  ReadLock lock(mutex_);

  return active_;
}

std::string RegistryInterface::getName() const {
  ReadLock lock(mutex_);

  return name_;
}

size_t RegistryInterface::count() const {
  ReadLock lock(mutex_);

  return items_.size();
}

Status RegistryInterface::setActive(const std::string& item_name) {
  UpgradeLock lock(mutex_);

  // Default support multiple active plugins.
  for (const auto& item : osquery::split(item_name, ",")) {
    if (items_.count(item) == 0 && external_.count(item) == 0) {
      return Status::failure("Unknown registry plugin: " + item);
    }
  }

  Status status;
  {
    WriteUpgradeLock wlock(lock);
    active_ = item_name;
  }

  // The active plugin is setup when initialized.
  for (const auto& item : osquery::split(item_name, ",")) {
    if (exists_(item, true)) {
      status = RegistryFactory::get().plugin(name_, item)->setUp();
    } else if (exists_(item, false) && !RegistryFactory::get().external()) {
      // If the active plugin is within an extension we must wait.
      // An extension will first broadcast the registry, then receive the list
      // of active plugins, active them if they are extension-local, and finally
      // start their extension socket.
      status = pingExtension(getExtensionSocket(external_.at(item)));
    }

    if (!status.ok()) {
      break;
    }
  }
  return status;
}

RegistryRoutes RegistryInterface::getRoutes() const {
  ReadLock lock(mutex_);

  RegistryRoutes route_table;
  for (const auto& item : items_) {
    if (isInternal_(item.first)) {
      // This is an internal plugin, do not include the route.
      continue;
    }

    bool has_alias = false;
    for (const auto& alias : aliases_) {
      if (alias.second == item.first) {
        // If the item name is masked by at least one alias, it will not
        // broadcast under the internal item name.
        route_table[alias.first] = item.second->routeInfo();
        has_alias = true;
      }
    }

    if (!has_alias) {
      route_table[item.first] = item.second->routeInfo();
    }
  }
  return route_table;
}

Status RegistryInterface::call(const std::string& item_name,
                               const PluginRequest& request,
                               PluginResponse& response) {
  if (item_name.empty()) {
    return Status::failure("No registry item name specified");
  }
  PluginRef plugin;
  {
    ReadLock lock(mutex_);

    // Search local plugins (items) for the plugin.
    if (items_.count(item_name) > 0) {
      plugin = items_.at(item_name);
    }
  }
  if (plugin) {
    return plugin->call(request, response);
  }

  RouteUUID uuid;
  {
    ReadLock lock(mutex_);

    // Check if the item was broadcasted as a plugin within an extension.
    if (external_.count(item_name) > 0) {
      // The item is a registered extension, call the extension by UUID.
      uuid = external_.at(item_name);
    } else if (routes_.count(item_name) > 0) {
      // The item has a route, but no extension, pass in the route info.
      response = routes_.at(item_name);
      return Status::success();
    } else if (RegistryFactory::get().external()) {
      // If this is an extension's registry forward unknown calls to the core.
      uuid = 0;
    } else {
      return Status::failure("Cannot call registry item: " + item_name);
    }
  }

  return callExtension(uuid, name_, item_name, request, response);
}

Status RegistryInterface::addAlias(const std::string& item_name,
                                   const std::string& alias) {
  WriteLock lock(mutex_);

  if (aliases_.count(alias) > 0) {
    return Status::failure("Duplicate alias: " + alias);
  }
  aliases_[alias] = item_name;
  return Status::success();
}

std::string RegistryInterface::getAlias(const std::string& alias) const {
  ReadLock lock(mutex_);

  if (aliases_.count(alias) == 0) {
    return alias;
  }
  return aliases_.at(alias);
}

Status RegistryInterface::addPlugin(const std::string& plugin_name,
                                    const PluginRef& plugin_item,
                                    bool internal) {
  WriteLock lock(mutex_);

  if (items_.count(plugin_name) > 0) {
    return Status::failure("Duplicate registry item exists: " + plugin_name);
  }

  plugin_item->setName(plugin_name);
  items_.emplace(std::make_pair(plugin_name, plugin_item));

  // The item can be listed as internal, meaning it does not broadcast.
  if (internal) {
    internal_.push_back(plugin_name);
  }

  return Status::success();
}

void RegistryInterface::setUp() {
  ReadLock lock(mutex_);

  // If this registry does not auto-setup do NOT setup the registry items.
  if (!auto_setup_) {
    return;
  }

  // If the registry is using a single 'active' plugin, setUp that plugin.
  // For config and logger, only setUp the selected plugin.
  if (active_.size() != 0 && exists_(active_, true)) {
    items_.at(active_)->setUp();
    return;
  }

  // Try to set up each of the registry items.
  // If they fail, remove them from the registry.
  std::vector<std::string> failed;
  for (auto& item : items_) {
    if (!item.second->setUp().ok()) {
      failed.push_back(item.first);
    }
  }

  for (const auto& failed_item : failed) {
    remove(failed_item);
  }
}

void RegistryInterface::configure() {
  ReadLock lock(mutex_);

  if (!active_.empty() && exists_(active_, true)) {
    items_.at(active_)->configure();
  } else {
    for (auto& item : items_) {
      item.second->configure();
    }
  }
}

Status RegistryInterface::addExternal(const RouteUUID& uuid,
                                      const RegistryRoutes& routes) {
  // Add each route name (item name) to the tracking.
  for (const auto& route : routes) {
    // Keep the routes info assigned to the registry.
    {
      WriteLock wlock(mutex_);
      routes_[route.first] = route.second;
    }

    auto status = addExternalPlugin(route.first, route.second);

    if (status.ok()) {
      WriteLock wlock(mutex_);
      external_[route.first] = uuid;
    } else {
      return status;
    }
  }

  return Status(0, "OK");
}

/// Remove all the routes for a given uuid.
void RegistryInterface::removeExternal(const RouteUUID& uuid) {
  std::vector<std::string> removed_items;

  {
    ReadLock rlock(mutex_);
    // Create list of items to remove by filtering uuid
    for (const auto& item : external_) {
      if (item.second == uuid) {
        removed_items.push_back(item.first);
      }
    }
  }

  for (const auto& item : removed_items) {
    removeExternalPlugin(item);
  }

  {
    WriteLock wlock(mutex_);
    // Remove items belonging to the external uuid.
    for (const auto& item : removed_items) {
      external_.erase(item);
      routes_.erase(item);
    }
  }
}

/// Facility method to check if a registry item exists.
bool RegistryInterface::exists(const std::string& item_name, bool local) const {
  ReadLock lock(mutex_);

  return exists_(item_name, local);
}

/// Facility method to list the registry item identifiers.
std::vector<std::string> RegistryInterface::names() const {
  ReadLock lock(mutex_);

  std::vector<std::string> names;
  for (const auto& item : items_) {
    names.push_back(item.first);
  }

  // Also add names of external plugins.
  for (const auto& item : external_) {
    names.push_back(item.first);
  }
  return names;
}

std::map<std::string, PluginRef> RegistryInterface::plugins() {
  ReadLock lock(mutex_);

  return items_;
}

void RegistryInterface::setname(const std::string& name) {
  WriteLock lock(mutex_);

  name_ = name;
}

bool RegistryInterface::isInternal_(const std::string& item_name) const {
  if (std::find(internal_.begin(), internal_.end(), item_name) ==
      internal_.end()) {
    return false;
  }
  return true;
}

bool RegistryInterface::exists_(const std::string& item_name,
                                bool local) const {
  bool has_local = (items_.count(item_name) > 0);
  bool has_external = (external_.count(item_name) > 0);
  bool has_route = (routes_.count(item_name) > 0);
  return (local) ? has_local : has_local || has_external || has_route;
}

AutoRegisterInterface::AutoRegisterInterface(const char* _type,
                                             const char* _name,
                                             bool optional)
    : type_(_type), name_(_name), optional_(optional) {}

AutoRegisterSet& AutoRegisterInterface::registries() {
  static AutoRegisterSet registries_;
  return registries_;
}

AutoRegisterSet& AutoRegisterInterface::plugins() {
  static AutoRegisterSet plugins_;
  return plugins_;
}

void AutoRegisterInterface::autoloadRegistry(
    std::unique_ptr<AutoRegisterInterface> ar_) {
  registries().push_back(std::move(ar_));
}

void AutoRegisterInterface::autoloadPlugin(
    std::unique_ptr<AutoRegisterInterface> ar_) {
  plugins().push_back(std::move(ar_));
}

void registryAndPluginInit() {
  for (const auto& it : AutoRegisterInterface::registries()) {
    it->run();
  }

  for (const auto& it : AutoRegisterInterface::plugins()) {
    it->run();
  }

  AutoRegisterSet().swap(AutoRegisterInterface::registries());
  AutoRegisterSet().swap(AutoRegisterInterface::plugins());
}

} // namespace osquery
<|endoftext|>


<commit_before>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#include<atomic>
#include<mutex>
#include<sstream>
#include<string>

#include"ork/file_utils.hpp"
#include"ork/orientation.hpp"
#include"ork/string_utils.hpp"
#include"ork/tagger.hpp"

namespace ork {

class tagger_statics {
private:
	file::path _debug_root = { ORK("./debug") };//Global guarded by mutex
	std::mutex _mutex = { };
public:
	tagger_statics() {}
	ORK_NON_COPYABLE(tagger_statics)
public:
	file::path debug_root() {
		std::lock_guard<std::mutex> lock(_mutex);
		return _debug_root;
	}
	void set_debug_root(const file::path&directory) {
		std::lock_guard<std::mutex> lock(_mutex);
		_debug_root = directory;
	}
};
tagger_statics&g_tagger_statics() {
	static tagger_statics statics;
	return statics;
}

struct tagger::impl {
public:
	std::atomic<unsigned>count;//Local static
	const bool number_folder;//Local static constant
	const string tag;//Local static constant
public:
	impl(const string&tag)
		: count(0)
		, number_folder(true)
		, tag(tag)
	{}
	impl(const string&tag, const bool numbered_folders)
		: count(0)
		, number_folder(numbered_folders)
		, tag(tag)
	{}
	ORK_NON_COPYABLE(impl)
};

tagger::tagger(const string&tag) : _pimpl{ new impl(tag) } {}
tagger::tagger(const string&tag, bool numbered_folders) : _pimpl{ new impl(tag, numbered_folders) } {}
tagger::~tagger() {}

string tagger::sub_tag(const string&tag, size_t&index) {
	string_stream stream;
	stream << tag << index++ << ORK("_");
	return stream.str();
}

string tagger::operator()() {
	string_stream stream;
	file::path p(g_tagger_statics().debug_root());
	if(_pimpl->number_folder) {
		stream << _pimpl->tag << ORK("_") << _pimpl->count++ << ORK("/");
		p /= stream.str();
		return p.ORK_GEN_STR();
	}
	else {
		stream << _pimpl->tag << ORK("/");
		p /= stream.str();
		return p.ORK_GEN_STR() + to_string(_pimpl->count++) + ORK("_");
	}
}

unsigned tagger::count() {
	return _pimpl->count.load();
}

void tagger::set_debug_root(const string&directory) {
	g_tagger_statics().set_debug_root(directory);
}

void tagger::set_debug_root(const string&as_is_path, const string&to_be_path) {
	file::path directory(ORK("./debug/"));
	directory /= file::path(as_is_path).stem();
	directory /= file::path(to_be_path).stem();
	g_tagger_statics().set_debug_root(directory.ORK_GEN_STR() + ORK("/"));
}

struct setup_hierarchy::impl {
public:
	string setup_directory;
	bool cached;
	unsigned score;
	std::vector<orientation>setups;
public:
	impl()
		: setup_directory()
		, cached(false)
		, score(0)
		, setups()
	{}
};

setup_hierarchy::setup_hierarchy(const string&setup_root, const string&as_is_path, const string&to_be_path)
	: _pimpl{ new impl{ } }
{
	if(!file::ensure_directory(setup_root))ORK_THROW(ORK("Setup root could not be created: ") << setup_root);
	//if(!test_file(as_is_path))ORK_THROW(ORK("As-is file could not be found: ") << as_is_path);
	//if(!test_file(to_be_path))ORK_THROW(ORK("To-Be file could not be found: ") << to_be_path);

	const string as_is_name(file::path(as_is_path).stem().ORK_GEN_STR());
	const string to_be_name(file::path(to_be_path).stem().ORK_GEN_STR());

	file::path setup_path(setup_root);
	setup_path /= as_is_name + ORK("/");
	setup_path /= to_be_name + ORK("/");
	_pimpl->setup_directory = setup_path.ORK_GEN_STR();
}

file::path setup_hierarchy::get_subdirectory() {
	file::path setup_dir;
	if(!top_subdirectory(get_path(), setup_dir))ORK_THROW(ORK("Setup directory not found for root: ") << get_path());
	_pimpl->cached = false;//Dirty, in case the client fetches more than one setup
	return setup_dir /= ORK("/");
}

struct setup_match {
	string setups;
	file::path setup_dir;
	setup_match(const string&exact_setups) :setups(exact_setups), setup_dir() {}
	void operator()(const file::path&p) {
		if(!setup_dir.empty())return;//Already found (this could be a superstring, so find might work)
		if(!ext_file::is_directory(p))return;//Just in case
		const string dir_name(p.stem().ORK_GEN_STR());
		if(dir_name.find(setups) != string::npos)setup_dir = p;
	}
};

const string&setup_hierarchy::get_path() {
	return _pimpl->setup_directory;
}

file::path setup_hierarchy::get_subdirectory(const string&exact_setups) {
	setup_match match(exact_setups);
	iterate_directory<setup_match, flat_search, sorted>::run(get_path(), match);
	if(match.setup_dir.empty())ORK_THROW(ORK("Setup permutation not found: ") << exact_setups);
	_pimpl->cached = false;//Dirty, in case the client fetches more than one setup
	return match.setup_dir;
}

const std::vector<orientation>& setup_hierarchy::get_setups() {
	return do_get_setups(file::canonical_normalized_trimmed(get_subdirectory()).filename().ORK_GEN_STR());
}

const std::vector<orientation>& setup_hierarchy::get_setups(const string&exact_setups) {
	return do_get_setups(file::canonical_normalized_trimmed(get_subdirectory(exact_setups)).filename().ORK_GEN_STR());
}

const std::vector<orientation>&setup_hierarchy::do_get_setups(const string&setup_dir) {
	if(!_pimpl->cached) {
		//Directory format: score [orientation]+
		//TODO: abstract writing, reading setups into a utility (right now process_plan class does the writing, but uses ACIS)
		i_string_stream strm(setup_dir);
		strm >> _pimpl->score;

		string setup;
		strm >> setup;
		while(strm) {
			_pimpl->setups.push_back(string2orientation(setup));
			strm >> setup;
		}
		if(_pimpl->setups.empty())ORK_THROW(ORK("Setup directory not formatted correctly: ") << get_path() << ORK("/") << setup_dir);

		_pimpl->cached = true;
	}
	if(_pimpl->setups.empty())ORK_THROW(ORK("Setups not found for directory: ") << setup_dir);
	return _pimpl->setups;
}

}//namespace ork
<commit_msg>Applied STL include to tagger<commit_after>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#include"ork/ork.hpp"
#define ORK_STL_INC_FILE <atomic>
#include"ork/core/stl_include.inl"
#include"ork/ork.hpp"
#define ORK_STL_INC_FILE <mutex>
#include"ork/core/stl_include.inl"
#include"ork/ork.hpp"
#define ORK_STL_INC_FILE <sstream>
#include"ork/core/stl_include.inl"
#include"ork/ork.hpp"
#define ORK_STL_INC_FILE <string>
#include"ork/core/stl_include.inl"

#include"ork/file_utils.hpp"
#include"ork/orientation.hpp"
#include"ork/string_utils.hpp"
#include"ork/tagger.hpp"

namespace ork {

class tagger_statics {
private:
	file::path _debug_root = { ORK("./debug") };//Global guarded by mutex
	std::mutex _mutex = { };
public:
	tagger_statics() {}
	ORK_NON_COPYABLE(tagger_statics)
public:
	file::path debug_root() {
		std::lock_guard<std::mutex> lock(_mutex);
		return _debug_root;
	}
	void set_debug_root(const file::path&directory) {
		std::lock_guard<std::mutex> lock(_mutex);
		_debug_root = directory;
	}
};
tagger_statics&g_tagger_statics() {
	static tagger_statics statics;
	return statics;
}

struct tagger::impl {
public:
	std::atomic<unsigned>count;//Local static
	const bool number_folder;//Local static constant
	const string tag;//Local static constant
public:
	impl(const string&tag)
		: count(0)
		, number_folder(true)
		, tag(tag)
	{}
	impl(const string&tag, const bool numbered_folders)
		: count(0)
		, number_folder(numbered_folders)
		, tag(tag)
	{}
	ORK_NON_COPYABLE(impl)
};

tagger::tagger(const string&tag) : _pimpl{ new impl(tag) } {}
tagger::tagger(const string&tag, bool numbered_folders) : _pimpl{ new impl(tag, numbered_folders) } {}
tagger::~tagger() {}

string tagger::sub_tag(const string&tag, size_t&index) {
	string_stream stream;
	stream << tag << index++ << ORK("_");
	return stream.str();
}

string tagger::operator()() {
	string_stream stream;
	file::path p(g_tagger_statics().debug_root());
	if(_pimpl->number_folder) {
		stream << _pimpl->tag << ORK("_") << _pimpl->count++ << ORK("/");
		p /= stream.str();
		return p.ORK_GEN_STR();
	}
	else {
		stream << _pimpl->tag << ORK("/");
		p /= stream.str();
		return p.ORK_GEN_STR() + to_string(_pimpl->count++) + ORK("_");
	}
}

unsigned tagger::count() {
	return _pimpl->count.load();
}

void tagger::set_debug_root(const string&directory) {
	g_tagger_statics().set_debug_root(directory);
}

void tagger::set_debug_root(const string&as_is_path, const string&to_be_path) {
	file::path directory(ORK("./debug/"));
	directory /= file::path(as_is_path).stem();
	directory /= file::path(to_be_path).stem();
	g_tagger_statics().set_debug_root(directory.ORK_GEN_STR() + ORK("/"));
}

struct setup_hierarchy::impl {
public:
	string setup_directory;
	bool cached;
	unsigned score;
	std::vector<orientation>setups;
public:
	impl()
		: setup_directory()
		, cached(false)
		, score(0)
		, setups()
	{}
};

setup_hierarchy::setup_hierarchy(const string&setup_root, const string&as_is_path, const string&to_be_path)
	: _pimpl{ new impl{ } }
{
	if(!file::ensure_directory(setup_root))ORK_THROW(ORK("Setup root could not be created: ") << setup_root);
	//if(!test_file(as_is_path))ORK_THROW(ORK("As-is file could not be found: ") << as_is_path);
	//if(!test_file(to_be_path))ORK_THROW(ORK("To-Be file could not be found: ") << to_be_path);

	const string as_is_name(file::path(as_is_path).stem().ORK_GEN_STR());
	const string to_be_name(file::path(to_be_path).stem().ORK_GEN_STR());

	file::path setup_path(setup_root);
	setup_path /= as_is_name + ORK("/");
	setup_path /= to_be_name + ORK("/");
	_pimpl->setup_directory = setup_path.ORK_GEN_STR();
}

file::path setup_hierarchy::get_subdirectory() {
	file::path setup_dir;
	if(!top_subdirectory(get_path(), setup_dir))ORK_THROW(ORK("Setup directory not found for root: ") << get_path());
	_pimpl->cached = false;//Dirty, in case the client fetches more than one setup
	return setup_dir /= ORK("/");
}

struct setup_match {
	string setups;
	file::path setup_dir;
	setup_match(const string&exact_setups) :setups(exact_setups), setup_dir() {}
	void operator()(const file::path&p) {
		if(!setup_dir.empty())return;//Already found (this could be a superstring, so find might work)
		if(!ext_file::is_directory(p))return;//Just in case
		const string dir_name(p.stem().ORK_GEN_STR());
		if(dir_name.find(setups) != string::npos)setup_dir = p;
	}
};

const string&setup_hierarchy::get_path() {
	return _pimpl->setup_directory;
}

file::path setup_hierarchy::get_subdirectory(const string&exact_setups) {
	setup_match match(exact_setups);
	iterate_directory<setup_match, flat_search, sorted>::run(get_path(), match);
	if(match.setup_dir.empty())ORK_THROW(ORK("Setup permutation not found: ") << exact_setups);
	_pimpl->cached = false;//Dirty, in case the client fetches more than one setup
	return match.setup_dir;
}

const std::vector<orientation>& setup_hierarchy::get_setups() {
	return do_get_setups(file::canonical_normalized_trimmed(get_subdirectory()).filename().ORK_GEN_STR());
}

const std::vector<orientation>& setup_hierarchy::get_setups(const string&exact_setups) {
	return do_get_setups(file::canonical_normalized_trimmed(get_subdirectory(exact_setups)).filename().ORK_GEN_STR());
}

const std::vector<orientation>&setup_hierarchy::do_get_setups(const string&setup_dir) {
	if(!_pimpl->cached) {
		//Directory format: score [orientation]+
		//TODO: abstract writing, reading setups into a utility (right now process_plan class does the writing, but uses ACIS)
		i_string_stream strm(setup_dir);
		strm >> _pimpl->score;

		string setup;
		strm >> setup;
		while(strm) {
			_pimpl->setups.push_back(string2orientation(setup));
			strm >> setup;
		}
		if(_pimpl->setups.empty())ORK_THROW(ORK("Setup directory not formatted correctly: ") << get_path() << ORK("/") << setup_dir);

		_pimpl->cached = true;
	}
	if(_pimpl->setups.empty())ORK_THROW(ORK("Setups not found for directory: ") << setup_dir);
	return _pimpl->setups;
}

}//namespace ork
<|endoftext|>


<commit_before>#include <string>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cassert>
#include <type_traits>
#include <sstream>
#include <iterator>
#include <tarlib/tarlib.h>

namespace {

	template <unsigned int OFFSET, unsigned int COUNT>
	unsigned long long _base256_to_10( typename std::enable_if<COUNT == 0, const Byte*>::type ptr) {
		return ptr[OFFSET];
	}

	template <unsigned int OFFSET, unsigned int COUNT>
	unsigned long long _base256_to_10( typename std::enable_if<COUNT != 0, const Byte*>::type ptr) {
		return (ptr[OFFSET] * 256 * COUNT) + _base256_to_10<OFFSET + 1, COUNT - 1>( ptr );
	}

	template <unsigned int OFFSET, unsigned int COUNT>
	unsigned long long base256_to_10( const Byte* ptr) {
		//return _base256_to_10<OFFSET, COUNT - 1>( ptr );
		return std::strtoull( reinterpret_cast<const char*>(ptr), nullptr, 256 );
	}

	unsigned long base8_to_10( const Byte* ptr ) {
		return std::strtoul( reinterpret_cast<const char*>(ptr), nullptr, 8 );
	}

	// Converts octal to usable decimal values
	void convert( const tar_header& header, tar_stream& strm ) {
		const bool is_base_256 = *header.file_bytes_octal & 0xFF;

		std::uint64_t value;

		if( is_base_256 ) {
			const auto& field = header.file_bytes_octal;
			value = base256_to_10<1, 10>( field );
		} else {
			std::ostringstream stream;
			value = base8_to_10( std::begin(header.file_bytes_octal) );
		}
		strm.file_bytes = value;

		strm.modification_time = base8_to_10( header.modification_time_octal );
	}

	// Internal tar state
	struct internal {
		internal();
		bool put( tar_stream& strm );
	private:
		tar_header    _header_buffer;
		Byte*         _header_ptr;
		std::uint64_t _left;
	};

	internal& intern( tar_stream& strm ) {
		return *static_cast<internal*>( strm.internal );
	}

	Byte* begin( tar_header& header ) {
		return reinterpret_cast<Byte*>( &header );
	}

	Byte* end( tar_header& header ) {
		return begin(header) + sizeof(header);
	}
}

internal::internal() :
	_header_buffer(), _header_ptr( reinterpret_cast<Byte*>(&_header_buffer) ), _left( 0 )
{
}

bool
internal::put( tar_stream& strm ) {
	const auto buffer_end = end(_header_buffer );
	if( _header_ptr == buffer_end && !_left ) {
		if( strm.avail_in ) {
			// Go to the next entry
			_header_ptr = begin( _header_buffer );
			strm.next_out  = nullptr;
			strm.avail_out = 0;
		} else
			return false; //end of entry
	}

	if( _header_ptr != buffer_end ) {
		// Try to make the header full
		const auto header_distance = std::distance( _header_ptr, buffer_end );
		assert( header_distance >= 0 );
		const auto distance = std::min( static_cast<decltype(strm.avail_in)>(header_distance), strm.avail_in );

		std::copy( strm.next_in, strm.next_in + distance, _header_ptr );
		strm.next_in  += distance;
		strm.avail_in -= distance;
		strm.total_in += distance;
		_header_ptr   += distance;

		if( _header_ptr == buffer_end ) {
			// We reached a full header
			const tar_header& header = *reinterpret_cast<tar_header*>( begin(_header_buffer) );
			convert( header, strm );
			strm.header = &header;
			_left = strm.file_bytes;
		}
	}

	if( _header_ptr == buffer_end ) {
		// Process file entry content

		if( strm.avail_in ) {
			// There is actual data here

			// Limit to current file entry only
			strm.avail_out = std::min( strm.avail_in, static_cast<decltype(strm.avail_in)>(_left) );
			strm.next_out  = strm.next_in;
			strm.total_out += strm.avail_out;

			strm.avail_in -= strm.avail_out;
			strm.next_in  += strm.avail_out;
			strm.total_in += strm.avail_out;

			_left         -= strm.avail_out;
		}

		if( !_left )
			return false; // end of entry
	}

	// Continue processing current entry
	return true;
}

// C wrapper
int TAREXPORT
tar_inflateInit( tar_streamp strm ) {
	assert( strm );
	strm->next_in           = nullptr;
	strm->avail_in          = 0;
	strm->next_out          = nullptr;
	strm->avail_out         = 0;
	strm->internal          = new internal();
	strm->header            = nullptr;
	strm->file_bytes        = 0;
	strm->modification_time = 0;
	return TAR_OK;
}

int TAREXPORT
tar_inflate( tar_streamp strm ) {
	assert( strm );
	if( intern(*strm).put( *strm ) )
		return TAR_OK;
	else
		return TAR_ENTRY_END;
}

int TAREXPORT
tar_inflateEnd( tar_streamp strm ) {
	assert( strm );
	delete &intern( *strm );
	// Signal to externals that the stream
	// has ended
	strm->internal = nullptr;
	return TAR_OK;
}

<commit_msg>Make selecting of first byte for base256 check more prominent.<commit_after>#include <string>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cassert>
#include <type_traits>
#include <sstream>
#include <iterator>
#include <tarlib/tarlib.h>

namespace {

	template <unsigned int OFFSET, unsigned int COUNT>
	unsigned long long _base256_to_10( typename std::enable_if<COUNT == 0, const Byte*>::type ptr) {
		return ptr[OFFSET];
	}

	template <unsigned int OFFSET, unsigned int COUNT>
	unsigned long long _base256_to_10( typename std::enable_if<COUNT != 0, const Byte*>::type ptr) {
		return (ptr[OFFSET] * 256 * COUNT) + _base256_to_10<OFFSET + 1, COUNT - 1>( ptr );
	}

	template <unsigned int OFFSET, unsigned int COUNT>
	unsigned long long base256_to_10( const Byte* ptr) {
		//return _base256_to_10<OFFSET, COUNT - 1>( ptr );
		return std::strtoull( reinterpret_cast<const char*>(ptr), nullptr, 256 );
	}

	unsigned long base8_to_10( const Byte* ptr ) {
		return std::strtoul( reinterpret_cast<const char*>(ptr), nullptr, 8 );
	}

	// Converts octal to usable decimal values
	void convert( const tar_header& header, tar_stream& strm ) {

		const bool is_base_256 = header.file_bytes_octal[0] & 0x80;

		std::uint64_t value;

		if( is_base_256 ) {
			const auto& field = header.file_bytes_octal;
			value = base256_to_10<1, 10>( field );
		} else {
			std::ostringstream stream;
			value = base8_to_10( std::begin(header.file_bytes_octal) );
		}
		strm.file_bytes = value;

		strm.modification_time = base8_to_10( header.modification_time_octal );
	}

	// Internal tar state
	struct internal {
		internal();
		bool put( tar_stream& strm );
	private:
		tar_header    _header_buffer;
		Byte*         _header_ptr;
		std::uint64_t _left;
	};

	internal& intern( tar_stream& strm ) {
		return *static_cast<internal*>( strm.internal );
	}

	Byte* begin( tar_header& header ) {
		return reinterpret_cast<Byte*>( &header );
	}

	Byte* end( tar_header& header ) {
		return begin(header) + sizeof(header);
	}
}

internal::internal() :
	_header_buffer(), _header_ptr( reinterpret_cast<Byte*>(&_header_buffer) ), _left( 0 )
{
}

bool
internal::put( tar_stream& strm ) {
	const auto buffer_end = end(_header_buffer );
	if( _header_ptr == buffer_end && !_left ) {
		if( strm.avail_in ) {
			// Go to the next entry
			_header_ptr = begin( _header_buffer );
			strm.next_out  = nullptr;
			strm.avail_out = 0;
		} else
			return false; //end of entry
	}

	if( _header_ptr != buffer_end ) {
		// Try to make the header full
		const auto header_distance = std::distance( _header_ptr, buffer_end );
		assert( header_distance >= 0 );
		const auto distance = std::min( static_cast<decltype(strm.avail_in)>(header_distance), strm.avail_in );

		std::copy( strm.next_in, strm.next_in + distance, _header_ptr );
		strm.next_in  += distance;
		strm.avail_in -= distance;
		strm.total_in += distance;
		_header_ptr   += distance;

		if( _header_ptr == buffer_end ) {
			// We reached a full header
			const tar_header& header = *reinterpret_cast<tar_header*>( begin(_header_buffer) );
			convert( header, strm );
			strm.header = &header;
			_left = strm.file_bytes;
		}
	}

	if( _header_ptr == buffer_end ) {
		// Process file entry content

		if( strm.avail_in ) {
			// There is actual data here

			// Limit to current file entry only
			strm.avail_out = std::min( strm.avail_in, static_cast<decltype(strm.avail_in)>(_left) );
			strm.next_out  = strm.next_in;
			strm.total_out += strm.avail_out;

			strm.avail_in -= strm.avail_out;
			strm.next_in  += strm.avail_out;
			strm.total_in += strm.avail_out;

			_left         -= strm.avail_out;
		}

		if( !_left )
			return false; // end of entry
	}

	// Continue processing current entry
	return true;
}

// C wrapper
int TAREXPORT
tar_inflateInit( tar_streamp strm ) {
	assert( strm );
	strm->next_in           = nullptr;
	strm->avail_in          = 0;
	strm->next_out          = nullptr;
	strm->avail_out         = 0;
	strm->internal          = new internal();
	strm->header            = nullptr;
	strm->file_bytes        = 0;
	strm->modification_time = 0;
	return TAR_OK;
}

int TAREXPORT
tar_inflate( tar_streamp strm ) {
	assert( strm );
	if( intern(*strm).put( *strm ) )
		return TAR_OK;
	else
		return TAR_ENTRY_END;
}

int TAREXPORT
tar_inflateEnd( tar_streamp strm ) {
	assert( strm );
	delete &intern( *strm );
	// Signal to externals that the stream
	// has ended
	strm->internal = nullptr;
	return TAR_OK;
}

<|endoftext|>


<commit_before>#include <qdebug.h>
#include <QThread>
#include <QMutex>
#include <MG.h>
#include <configuration.h>
#include<string>
#include "XbeeLink.h"

XbeeLink::XbeeLink(QString portName, int baudRate) :
	m_xbeeCon(NULL),
    m_id(-1),
    m_portName(NULL),
    m_portNameLength(0),
    m_baudRate(baudRate),
    m_connected(false),
	m_addrHigh(0),
    m_addrLow(0)
{

	/* setup the xbee */
	this->setPortName(portName);

	//this->connect();
	// Set unique ID and add link to the list of links
	this->m_id = getNextLinkId();
	// set the Name
	this->m_name = tr("xbee link") + QString::number(this->m_id);
	emit nameChanged(this->m_name);
}

XbeeLink::~XbeeLink()
{
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	_disconnect();
}

QString XbeeLink::getPortName() const
{
	QString portName;
	for(unsigned int i = 0;i<this->m_portNameLength;i++)
	{
		portName.append(this->m_portName[i]);
	}
	return portName;
}

int XbeeLink::getBaudRate() const
{
	return this->m_baudRate;
}

bool XbeeLink::setPortName(QString portName)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	QStringList list = portName.split(QRegExp("\\s+"),QString::SkipEmptyParts);
	if(list.size()>0)
	{
		this->m_portNameLength = list[0].size()+1;
		m_portName = new char[this->m_portNameLength];
		for(int i=0;i<list[0].size();i++)
		{
			this->m_portName[i]=list[0][i].toLatin1();
		}
		this->m_portName[list[0].size()] = '\0';
	}
	else
	{
		this->m_portNameLength = 1;
		m_portName = new char[this->m_portNameLength];
		this->m_portName[0] = '\0';
	}

	bool retVal(true);
	if(reconnect)
	{
		retVal = _connect();
	}

	return retVal;
}

bool XbeeLink::setBaudRate(int rate)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	bool retVal(true);
	this->m_baudRate = rate;
	if(reconnect)
	{
		retVal = _connect();
	}
	return retVal;
}

int XbeeLink::getId() const
{
	return this->m_id;
}

QString XbeeLink::getName() const
{
	return this->m_name;
}

bool XbeeLink::isConnected() const
{
	return this->m_connected;
}

qint64 XbeeLink::getConnectionSpeed() const
{
	return this->m_baudRate;
}

qint64 XbeeLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 XbeeLink::getCurrentOutDataRate() const
{
    return 0;
}

bool XbeeLink::hardwareConnect()
{
	emit tryConnectBegin(true);
	if(this->isConnected())
	{
		_disconnect();
	}
	if (*this->m_portName == '\0')
	{
		return false;
	}
	if (xbee_setupAPI(this->m_portName,this->m_baudRate,0x2B,0x3E8) == -1)
		{
		  /* oh no... it failed */
			qDebug() <<"xbee_setup() failed...\n";
			emit tryConnectEnd(true);
			return false;
		}
	this->m_xbeeCon = xbee_newcon('A',xbee2_data,0x13A200,0x403D0935);
	emit tryConnectEnd(true);
	this->m_connected = true;
	emit connected();
	emit connected(true);
	return true;
}

bool XbeeLink::_connect(void)
{
	if (this->isRunning()) this->disconnect();
    this->start(LowPriority);
    return true;
}

bool XbeeLink::_disconnect(void)
{
	if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect

	if(this->m_xbeeCon)
	{
		xbee_end();
		this->m_xbeeCon = NULL;
	}
	this->m_connected = false;

	emit disconnected();
	emit connected(false);
	return true;
}

void XbeeLink::writeBytes(const char *bytes, qint64 length)  // TO DO: delete the data array
{
	char *data;
	data = new char[length];
	for(long i=0;i<length;i++)
	{
		data[i] = bytes[i];
	}
	if(!xbee_nsenddata(this->m_xbeeCon,data,length)) // return value of 0 is successful written
	{
        // Log the amount and time written out for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, length, QDateTime::currentMSecsSinceEpoch());
	}
	else
	{
		this->disconnect();
		emit communicationError(this->getName(), tr("Could not send data - link %1 is disconnected!").arg(this->getName()));
	}
}

void XbeeLink::readBytes()
{
	xbee_pkt *xbeePkt;
	xbeePkt = xbee_getpacketwait(this->m_xbeeCon);
	if(!(NULL==xbeePkt))
	{
		QByteArray data;
		for(unsigned int i=0;i<=xbeePkt->datalen;i++)
		{
			data.push_back(xbeePkt->data[i]);
        }

        emit bytesReceived(this, data);

        // Log the amount and time received for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, data.length(), QDateTime::currentMSecsSinceEpoch());
	}
}

void XbeeLink::run()
{
    // Initialize the connection
	if(this->hardwareConnect())
	{
		// Qt way to make clear what a while(1) loop does
		forever
		{
			this->readBytes();
	    }
	}
}

bool XbeeLink::setRemoteAddressHigh(quint32 high)
{
	this->m_addrHigh = high;
	return true;
}

bool XbeeLink::setRemoteAddressLow(quint32 low)
{
	this->m_addrLow = low;
	return true;
}

/*
void CALLTYPE XbeeLink::portCallback(xbee_con *xbeeCon, xbee_pkt *XbeePkt)
{
	QByteArray buf;
	for(quint8 i=0;i<XbeePkt->datalen;i++)
	{
		buf.push_back(XbeePkt->data[i]);
	}
	emit bytesReceived(this, buf);
}*/
<commit_msg>Fixes for new _connect/_disconnect apis<commit_after>#include <qdebug.h>
#include <QThread>
#include <QMutex>
#include <MG.h>
#include <configuration.h>
#include<string>
#include "XbeeLink.h"

XbeeLink::XbeeLink(QString portName, int baudRate) :
	m_xbeeCon(NULL),
    m_id(-1),
    m_portName(NULL),
    m_portNameLength(0),
    m_baudRate(baudRate),
    m_connected(false),
	m_addrHigh(0),
    m_addrLow(0)
{

	/* setup the xbee */
	this->setPortName(portName);

	//this->connect();
	// Set unique ID and add link to the list of links
	this->m_id = getNextLinkId();
	// set the Name
	this->m_name = tr("xbee link") + QString::number(this->m_id);
	emit nameChanged(this->m_name);
}

XbeeLink::~XbeeLink()
{
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	_disconnect();
}

QString XbeeLink::getPortName() const
{
	QString portName;
	for(unsigned int i = 0;i<this->m_portNameLength;i++)
	{
		portName.append(this->m_portName[i]);
	}
	return portName;
}

int XbeeLink::getBaudRate() const
{
	return this->m_baudRate;
}

bool XbeeLink::setPortName(QString portName)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	if(m_portName)
	{
		delete m_portName;
		m_portName = NULL;
	}
	QStringList list = portName.split(QRegExp("\\s+"),QString::SkipEmptyParts);
	if(list.size()>0)
	{
		this->m_portNameLength = list[0].size()+1;
		m_portName = new char[this->m_portNameLength];
		for(int i=0;i<list[0].size();i++)
		{
			this->m_portName[i]=list[0][i].toLatin1();
		}
		this->m_portName[list[0].size()] = '\0';
	}
	else
	{
		this->m_portNameLength = 1;
		m_portName = new char[this->m_portNameLength];
		this->m_portName[0] = '\0';
	}

	bool retVal(true);
	if(reconnect)
	{
		retVal = _connect();
	}

	return retVal;
}

bool XbeeLink::setBaudRate(int rate)
{
	bool reconnect(false);
	if(this->m_connected)
	{
		_disconnect();
		reconnect = true;
	}
	bool retVal(true);
	this->m_baudRate = rate;
	if(reconnect)
	{
		retVal = _connect();
	}
	return retVal;
}

int XbeeLink::getId() const
{
	return this->m_id;
}

QString XbeeLink::getName() const
{
	return this->m_name;
}

bool XbeeLink::isConnected() const
{
	return this->m_connected;
}

qint64 XbeeLink::getConnectionSpeed() const
{
	return this->m_baudRate;
}

qint64 XbeeLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 XbeeLink::getCurrentOutDataRate() const
{
    return 0;
}

bool XbeeLink::hardwareConnect()
{
	emit tryConnectBegin(true);
	if(this->isConnected())
	{
		_disconnect();
	}
	if (*this->m_portName == '\0')
	{
		return false;
	}
	if (xbee_setupAPI(this->m_portName,this->m_baudRate,0x2B,0x3E8) == -1)
		{
		  /* oh no... it failed */
			qDebug() <<"xbee_setup() failed...\n";
			emit tryConnectEnd(true);
			return false;
		}
	this->m_xbeeCon = xbee_newcon('A',xbee2_data,0x13A200,0x403D0935);
	emit tryConnectEnd(true);
	this->m_connected = true;
	emit connected();
	emit connected(true);
	return true;
}

bool XbeeLink::_connect(void)
{
	if (this->isRunning()) _disconnect();
    this->start(LowPriority);
    return true;
}

bool XbeeLink::_disconnect(void)
{
	if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect

	if(this->m_xbeeCon)
	{
		xbee_end();
		this->m_xbeeCon = NULL;
	}
	this->m_connected = false;

	emit disconnected();
	emit connected(false);
	return true;
}

void XbeeLink::writeBytes(const char *bytes, qint64 length)  // TO DO: delete the data array
{
	char *data;
	data = new char[length];
	for(long i=0;i<length;i++)
	{
		data[i] = bytes[i];
	}
	if(!xbee_nsenddata(this->m_xbeeCon,data,length)) // return value of 0 is successful written
	{
        // Log the amount and time written out for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, length, QDateTime::currentMSecsSinceEpoch());
	}
	else
	{
		_disconnect();
		emit communicationError(this->getName(), tr("Could not send data - link %1 is disconnected!").arg(this->getName()));
	}
}

void XbeeLink::readBytes()
{
	xbee_pkt *xbeePkt;
	xbeePkt = xbee_getpacketwait(this->m_xbeeCon);
	if(!(NULL==xbeePkt))
	{
		QByteArray data;
		for(unsigned int i=0;i<=xbeePkt->datalen;i++)
		{
			data.push_back(xbeePkt->data[i]);
        }

        emit bytesReceived(this, data);

        // Log the amount and time received for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, data.length(), QDateTime::currentMSecsSinceEpoch());
	}
}

void XbeeLink::run()
{
    // Initialize the connection
	if(this->hardwareConnect())
	{
		// Qt way to make clear what a while(1) loop does
		forever
		{
			this->readBytes();
	    }
	}
}

bool XbeeLink::setRemoteAddressHigh(quint32 high)
{
	this->m_addrHigh = high;
	return true;
}

bool XbeeLink::setRemoteAddressLow(quint32 low)
{
	this->m_addrLow = low;
	return true;
}

/*
void CALLTYPE XbeeLink::portCallback(xbee_con *xbeeCon, xbee_pkt *XbeePkt)
{
	QByteArray buf;
	for(quint8 i=0;i<XbeePkt->datalen;i++)
	{
		buf.push_back(XbeePkt->data[i]);
	}
	emit bytesReceived(this, buf);
}*/
<|endoftext|>


<commit_before>/*

Copyright (c) 2006, MassaRoddel, Arvid Norberg
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 the author 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.

*/

#include "libtorrent/pch.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_connection.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/extensions.hpp"

#include "libtorrent/extensions/ut_pex.hpp"

namespace libtorrent { namespace
{
	const char extension_name[] = "ut_pex";

	enum
	{
		extension_index = 1,
		max_peer_entries = 100
	};

	bool send_peer(peer_connection const& p)
	{
		// don't send out peers that we haven't connected to
		// (that have connected to us)
		if (!p.is_local()) return false;
		// don't send out peers that we haven't successfully connected to
		if (p.is_connecting()) return false;
		return true;
	}

	struct ut_pex_plugin: torrent_plugin
	{
		ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(55) {}

		virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection* pc);

		std::vector<char>& get_ut_pex_msg()
		{
			return m_ut_pex_msg;
		}

		// the second tick of the torrent
		// each minute the new lists of "added" + "added.f" and "dropped"
		// are calculated here and the pex message is created
		// each peer connection will use this message
		// max_peer_entries limits the packet size
		virtual void tick()
		{
			if (++m_1_minute < 60) return;

			m_1_minute = 0;

			entry pex;
			std::string& pla = pex["added"].string();
			std::string& pld = pex["dropped"].string();
			std::string& plf = pex["added.f"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& pld6 = pex["dropped6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> pld_out(pld);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> pld6_out(pld6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			std::set<tcp::endpoint> dropped;
			m_old_peers.swap(dropped);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
					peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				tcp::endpoint const& remote = peer->remote();
				m_old_peers.insert(remote);

				std::set<tcp::endpoint>::iterator di = dropped.find(remote);
				if (di == dropped.end())
				{
					// don't write too big of a package
					if (num_added >= max_peer_entries) break;

					// only send proper bittorrent peers
					bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
					if (!p) continue;

					// no supported flags to set yet
					// 0x01 - peer supports encryption
					// 0x02 - peer is a seed
					int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
					flags |= p->supports_encryption() ? 1 : 0;
#endif
					// i->first was added since the last time
					if (remote.address().is_v4())
					{
						detail::write_endpoint(remote, pla_out);
						detail::write_uint8(flags, plf_out);
					}
					else
					{
						detail::write_endpoint(remote, pla6_out);
						detail::write_uint8(flags, plf6_out);
					}
					++num_added;
				}
				else
				{
					// this was in the previous message
					// so, it wasn't dropped
					dropped.erase(di);
				}
			}

			for (std::set<tcp::endpoint>::const_iterator i = dropped.begin()
				, end(dropped.end()); i != end; ++i)
			{
				if (i->address().is_v4())
					detail::write_endpoint(*i, pld_out);
				else
					detail::write_endpoint(*i, pld6_out);
			}

			m_ut_pex_msg.clear();
			bencode(std::back_inserter(m_ut_pex_msg), pex);
		}

	private:
		torrent& m_torrent;

		std::set<tcp::endpoint> m_old_peers;
		int m_1_minute;
		std::vector<char> m_ut_pex_msg;
	};

	struct ut_pex_peer_plugin : peer_plugin
	{
		ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
			: m_torrent(t)
			, m_pc(pc)
			, m_tp(tp)
			, m_1_minute(55)
			, m_message_index(0)
			, m_first_time(true)
		{}

		virtual void add_handshake(entry& h)
		{
			entry& messages = h["m"];
			messages[extension_name] = extension_index;
		}

		virtual bool on_extension_handshake(entry const& h)
		{
			m_message_index = 0;
			entry const* messages = h.find_key("m");
			if (!messages || messages->type() != entry::dictionary_t) return false;

			entry const* index = messages->find_key(extension_name);
			if (!index || index->type() != entry::int_t) return false;

			m_message_index = int(index->integer());
			return true;
		}

		virtual bool on_extended(int length, int msg, buffer::const_interval body)
		{
			if (msg != extension_index) return false;
			if (m_message_index == 0) return false;

			if (length > 500 * 1024)
				throw protocol_error("uT peer exchange message larger than 500 kB");

			if (body.left() < length) return true;

			entry pex_msg = bdecode(body.begin, body.end);

			entry const* p = pex_msg.find_key("added");
			entry const* pf = pex_msg.find_key("added.f");

			if (p != 0 && pf != 0 && p->type() == entry::string_t && pf->type() == entry::string_t)
			{
				std::string const& peers = p->string();
				std::string const& peer_flags = pf->string();

				int num_peers = peers.length() / 6;
				char const* in = peers.c_str();
				char const* fin = peer_flags.c_str();

				if (int(peer_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}
			}

			entry const* p6 = pex_msg.find_key("added6");
			entry const* p6f = pex_msg.find_key("added6.f");
			if (p6 && p6f && p6->type() == entry::string_t && p6f->type() == entry::string_t)
			{
				std::string const& peers6 = p6->string();
				std::string const& peer6_flags = p6f->string();

				int num_peers = peers6.length() / 18;
				char const* in = peers6.c_str();
				char const* fin = peer6_flags.c_str();

				if (int(peer6_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}
			}
			return true;
		}

		// the peers second tick
		// every minute we send a pex message
		virtual void tick()
		{
			if (!m_message_index) return;	// no handshake yet
			if (++m_1_minute <= 60) return;

			if (m_first_time)
			{
				send_ut_peer_list();
				m_first_time = false;
			}
			else
			{
				send_ut_peer_diff();
			}
			m_1_minute = 0;
		}

	private:

		void send_ut_peer_diff()
		{
			std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		void send_ut_peer_list()
		{
			entry pex;
			// leave the dropped string empty
			pex["dropped"].string();
			std::string& pla = pex["added"].string();
			std::string& plf = pex["added.f"].string();
			pex["dropped6"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				// don't write too big of a package
				if (num_added >= max_peer_entries) break;

				// only send proper bittorrent peers
				bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
				if (!p) continue;

				// no supported flags to set yet
				// 0x01 - peer supports encryption
				// 0x02 - peer is a seed
				int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
				flags |= p->supports_encryption() ? 1 : 0;
#endif
				tcp::endpoint const& remote = peer->remote();
				// i->first was added since the last time
				if (remote.address().is_v4())
				{
					detail::write_endpoint(remote, pla_out);
					detail::write_uint8(flags, plf_out);
				}
				else
				{
					detail::write_endpoint(remote, pla6_out);
					detail::write_uint8(flags, plf6_out);
				}
				++num_added;
			}
			std::vector<char> pex_msg;
			bencode(std::back_inserter(pex_msg), pex);

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		torrent& m_torrent;
		peer_connection& m_pc;
		ut_pex_plugin& m_tp;
		int m_1_minute;
		int m_message_index;

		// this is initialized to true, and set to
		// false after the first pex message has been sent.
		// it is used to know if a diff message or a full
		// message should be sent.
		bool m_first_time;
	};

	boost::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection* pc)
	{
		bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
		if (!c) return boost::shared_ptr<peer_plugin>();
		return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
			, *pc, *this));
	}
}}

namespace libtorrent
{

	boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
	{
		if (t->torrent_file().priv())
		{
			return boost::shared_ptr<torrent_plugin>();
		}
		return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
	}

}

<commit_msg>ut_pex exception fix<commit_after>/*

Copyright (c) 2006, MassaRoddel, Arvid Norberg
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 the author 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.

*/

#include "libtorrent/pch.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_connection.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/extensions.hpp"

#include "libtorrent/extensions/ut_pex.hpp"

namespace libtorrent { namespace
{
	const char extension_name[] = "ut_pex";

	enum
	{
		extension_index = 1,
		max_peer_entries = 100
	};

	bool send_peer(peer_connection const& p)
	{
		// don't send out peers that we haven't connected to
		// (that have connected to us)
		if (!p.is_local()) return false;
		// don't send out peers that we haven't successfully connected to
		if (p.is_connecting()) return false;
		return true;
	}

	struct ut_pex_plugin: torrent_plugin
	{
		ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(55) {}

		virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection* pc);

		std::vector<char>& get_ut_pex_msg()
		{
			return m_ut_pex_msg;
		}

		// the second tick of the torrent
		// each minute the new lists of "added" + "added.f" and "dropped"
		// are calculated here and the pex message is created
		// each peer connection will use this message
		// max_peer_entries limits the packet size
		virtual void tick()
		{
			if (++m_1_minute < 60) return;

			m_1_minute = 0;

			entry pex;
			std::string& pla = pex["added"].string();
			std::string& pld = pex["dropped"].string();
			std::string& plf = pex["added.f"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& pld6 = pex["dropped6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> pld_out(pld);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> pld6_out(pld6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			std::set<tcp::endpoint> dropped;
			m_old_peers.swap(dropped);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
					peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				tcp::endpoint const& remote = peer->remote();
				m_old_peers.insert(remote);

				std::set<tcp::endpoint>::iterator di = dropped.find(remote);
				if (di == dropped.end())
				{
					// don't write too big of a package
					if (num_added >= max_peer_entries) break;

					// only send proper bittorrent peers
					bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
					if (!p) continue;

					// no supported flags to set yet
					// 0x01 - peer supports encryption
					// 0x02 - peer is a seed
					int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
					flags |= p->supports_encryption() ? 1 : 0;
#endif
					// i->first was added since the last time
					if (remote.address().is_v4())
					{
						detail::write_endpoint(remote, pla_out);
						detail::write_uint8(flags, plf_out);
					}
					else
					{
						detail::write_endpoint(remote, pla6_out);
						detail::write_uint8(flags, plf6_out);
					}
					++num_added;
				}
				else
				{
					// this was in the previous message
					// so, it wasn't dropped
					dropped.erase(di);
				}
			}

			for (std::set<tcp::endpoint>::const_iterator i = dropped.begin()
				, end(dropped.end()); i != end; ++i)
			{
				if (i->address().is_v4())
					detail::write_endpoint(*i, pld_out);
				else
					detail::write_endpoint(*i, pld6_out);
			}

			m_ut_pex_msg.clear();
			bencode(std::back_inserter(m_ut_pex_msg), pex);
		}

	private:
		torrent& m_torrent;

		std::set<tcp::endpoint> m_old_peers;
		int m_1_minute;
		std::vector<char> m_ut_pex_msg;
	};

	struct ut_pex_peer_plugin : peer_plugin
	{
		ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
			: m_torrent(t)
			, m_pc(pc)
			, m_tp(tp)
			, m_1_minute(55)
			, m_message_index(0)
			, m_first_time(true)
		{}

		virtual void add_handshake(entry& h)
		{
			entry& messages = h["m"];
			messages[extension_name] = extension_index;
		}

		virtual bool on_extension_handshake(entry const& h)
		{
			m_message_index = 0;
			entry const* messages = h.find_key("m");
			if (!messages || messages->type() != entry::dictionary_t) return false;

			entry const* index = messages->find_key(extension_name);
			if (!index || index->type() != entry::int_t) return false;

			m_message_index = int(index->integer());
			return true;
		}

		virtual bool on_extended(int length, int msg, buffer::const_interval body)
		{
			if (msg != extension_index) return false;
			if (m_message_index == 0) return false;

			if (length > 500 * 1024)
			{
				m_pc.disconnect("peer exchange message larger than 500 kB");
				return true;
			}

			if (body.left() < length) return true;

			entry pex_msg = bdecode(body.begin, body.end);

			entry const* p = pex_msg.find_key("added");
			entry const* pf = pex_msg.find_key("added.f");

			if (p != 0 && pf != 0 && p->type() == entry::string_t && pf->type() == entry::string_t)
			{
				std::string const& peers = p->string();
				std::string const& peer_flags = pf->string();

				int num_peers = peers.length() / 6;
				char const* in = peers.c_str();
				char const* fin = peer_flags.c_str();

				if (int(peer_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}
			}

			entry const* p6 = pex_msg.find_key("added6");
			entry const* p6f = pex_msg.find_key("added6.f");
			if (p6 && p6f && p6->type() == entry::string_t && p6f->type() == entry::string_t)
			{
				std::string const& peers6 = p6->string();
				std::string const& peer6_flags = p6f->string();

				int num_peers = peers6.length() / 18;
				char const* in = peers6.c_str();
				char const* fin = peer6_flags.c_str();

				if (int(peer6_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}
			}
			return true;
		}

		// the peers second tick
		// every minute we send a pex message
		virtual void tick()
		{
			if (!m_message_index) return;	// no handshake yet
			if (++m_1_minute <= 60) return;

			if (m_first_time)
			{
				send_ut_peer_list();
				m_first_time = false;
			}
			else
			{
				send_ut_peer_diff();
			}
			m_1_minute = 0;
		}

	private:

		void send_ut_peer_diff()
		{
			std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		void send_ut_peer_list()
		{
			entry pex;
			// leave the dropped string empty
			pex["dropped"].string();
			std::string& pla = pex["added"].string();
			std::string& plf = pex["added.f"].string();
			pex["dropped6"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				// don't write too big of a package
				if (num_added >= max_peer_entries) break;

				// only send proper bittorrent peers
				bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
				if (!p) continue;

				// no supported flags to set yet
				// 0x01 - peer supports encryption
				// 0x02 - peer is a seed
				int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
				flags |= p->supports_encryption() ? 1 : 0;
#endif
				tcp::endpoint const& remote = peer->remote();
				// i->first was added since the last time
				if (remote.address().is_v4())
				{
					detail::write_endpoint(remote, pla_out);
					detail::write_uint8(flags, plf_out);
				}
				else
				{
					detail::write_endpoint(remote, pla6_out);
					detail::write_uint8(flags, plf6_out);
				}
				++num_added;
			}
			std::vector<char> pex_msg;
			bencode(std::back_inserter(pex_msg), pex);

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		torrent& m_torrent;
		peer_connection& m_pc;
		ut_pex_plugin& m_tp;
		int m_1_minute;
		int m_message_index;

		// this is initialized to true, and set to
		// false after the first pex message has been sent.
		// it is used to know if a diff message or a full
		// message should be sent.
		bool m_first_time;
	};

	boost::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection* pc)
	{
		bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
		if (!c) return boost::shared_ptr<peer_plugin>();
		return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
			, *pc, *this));
	}
}}

namespace libtorrent
{

	boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
	{
		if (t->torrent_file().priv())
		{
			return boost::shared_ptr<torrent_plugin>();
		}
		return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
	}

}

<|endoftext|>