<commit_before>/*
 * Copyright 2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <thrift/lib/cpp/util/kerberos/Krb5CCacheStore.h>

#include <glog/logging.h>
#include <memory>
#include <set>
#include <stdio.h>

#include <folly/stats/BucketedTimeSeries-defs.h>
#include <folly/Memory.h>
#include <folly/ScopeGuard.h>
#include <folly/String.h>

// In older version of krb, this constant is not defined. Define it
#ifndef KRB5_GC_NO_STORE
#define KRB5_GC_NO_STORE     8  /**< Do not store in credential cache */
#endif

namespace apache { namespace thrift { namespace krb5 {
using namespace std;

const int Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS = 10;
const int Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD = 600;

static bool serviceCountCompare (
    const pair<string, uint64_t>& i,
    const pair<string, uint64_t>& j) {
  return (i.second > j.second);
}

Krb5CCacheStore::ServiceData::ServiceData() :
  timeSeries(
    Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS,
    std::chrono::seconds(
      Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD)) {
}

void Krb5CCacheStore::ServiceData::bumpCount() {
  folly::SharedMutex::WriteHolder guard(lockTimeSeries);
  time_t now = time(nullptr);
  timeSeries.addValue(std::chrono::seconds(now), 1);
}

uint64_t Krb5CCacheStore::ServiceData::getCount() {
  folly::SharedMutex::ReadHolder guard(lockTimeSeries);
  // Note that we don't have a need to call timeSeries_.update(<time>)
  // here because we don't care to have the exact count at the current
  // time. We're ok with grabbing the count at the last update.
  return timeSeries.count();
}

std::shared_ptr<Krb5CCache> Krb5CCacheStore::waitForCache(
    const Krb5Principal& service,
    SecurityLogger* logger) {
  std::shared_ptr<ServiceData> dataPtr = getServiceDataPtr(service);

  // Bump that we've used the principal
  dataPtr->bumpCount();

  if (logger) {
    logger->logStart("get_prepared_cache", folly::to<string>(service));
  }

  {
    folly::SharedMutex::ReadHolder readLock(dataPtr->lockCache);
    // If there is a cache, just return it. Try with a read lock first
    // for performance reasons.
    if (dataPtr->cache) {
      if (logger) {
        logger->logEnd("get_prepared_cache");
      }
      return dataPtr->cache;
    }
  }

  // If we didn't find a cache, initialize a local, temporary cache for the
  // service. We might throw this away if some other thread initialized the
  // cache meanwhile.
  if (logger) {
    logger->logStart("init_cache_for_service", folly::to<string>(service));
  }
  auto tempCache = initCacheForService(service, nullptr, logger);
  if (logger) {
    logger->logEnd("init_cache_for_service");
  }

  // Upgrade to a write lock, and initialize the cache only if it is not already
  // initialized by some other thread meanwhile.
  folly::SharedMutex::WriteHolder writeLock(dataPtr->lockCache);
  if (!dataPtr->cache) {
    dataPtr->cache = std::move(tempCache);
  }
  return dataPtr->cache;
}

std::shared_ptr<Krb5CCacheStore::ServiceData>
    Krb5CCacheStore::getServiceDataPtr(const Krb5Principal& service) {

  // If ccache is disabled, just return a fresh ServiceData obj
  if (maxCacheSize_ == 0) {
    return std::make_shared<ServiceData>();
  }

  string service_name = folly::to<string>(service);
  {
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    auto found = serviceDataMap_.find(service_name);
    if (found != serviceDataMap_.end()) {
      // Get the element from the map if it exists
      return found->second;
    }
  }

  // Not found, we need to create it
  folly::SharedMutex::WriteHolder writeLock(serviceDataMapLock_);
  auto found = serviceDataMap_.find(service_name);
  if (found == serviceDataMap_.end()) {
    // If we reached the limit, then we need to free some room
    if (maxCacheSize_ > 0 && cacheItemQueue_.size() >= maxCacheSize_) {
      serviceDataMap_.erase(cacheItemQueue_.front());
      cacheItemQueue_.pop();
    }
    serviceDataMap_[service_name] = std::make_shared<ServiceData>();
    cacheItemQueue_.push(service_name);
  }
  return serviceDataMap_[service_name];
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::initCacheForService(
    const Krb5Principal& service,
    const krb5_creds* creds,
    SecurityLogger* logger) {

  if (logger) {
    logger->logStart("wait_for_tgt");
  }
  std::shared_ptr<const Krb5Credentials> tgt;
  auto client_princ = tgts_.getClientPrincipal();
  // Get a cross-realm tgt if we need
  string srealm = service.getRealm();
  if (srealm != tgts_.getClientPrincipal().getRealm()) {
    tgt = tgts_.getTgtForRealm(srealm);
  } else {
    tgt = tgts_.getTgt();
  }
  if (logger) {
    logger->logEnd("wait_for_tgt");
  }

  if (logger) {
    logger->logStart("init_barebones_ccache");
  }
  // Make a new memory cache.
  auto mem = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  mem->setDestroyOnClose();
  mem->initialize(client_princ.get());
  mem->storeCred(tgt->get());
  if (logger) {
    logger->logEnd("init_barebones_ccache");
  }

  if (creds != nullptr) {
    mem->storeCred(*creds);
  } else {
    if (logger) {
      logger->logStart("get_credential_from_kdc");
    }
    mem->getCredentials(service.get());
    if (logger) {
      logger->logEnd("get_credential_from_kdc");
    }
  }

  return mem;
}

void Krb5CCacheStore::importCache(
    Krb5CCache& file_cache) {
  logger_->logStart("import_tgts");
  // Split out tgts and service principals
  std::vector<Krb5Credentials> tgts;
  std::vector<Krb5Credentials> services;
  uint64_t count = 0;
  for (auto& tmp_cred : file_cache) {
    Krb5Credentials cred(std::move(tmp_cred));
    Krb5Principal server = Krb5Principal::copyPrincipal(
      ctx_.get(), cred.get().server);
    if (server.isTgt()) {
      tgts.push_back(std::move(cred));
      count++;
    } else if (!server.getRealm().empty()) {
      // Sometimes, somehow, principals for host/{hg.vvv,svn.vip} have no
      // realm!!  Why?  No idea, but they exist and they blow up the entire
      // cache reading thread, so defend against them for now by ignoring that
      // entry (example: #7808411).
      services.push_back(std::move(cred));
    }
  }

  // Import TGTs
  Krb5Tgts tgts_obj;
  Krb5Principal client_principal = file_cache.getClientPrincipal();
  tgts_obj.setClientPrincipal(client_principal);
  for (auto& tgt : tgts) {
    Krb5Principal server_principal = Krb5Principal::copyPrincipal(
      ctx_.get(), tgt.get().server);
    if (server_principal.getComponent(1) == client_principal.getRealm()) {
      tgts_obj.setTgt(folly::make_unique<Krb5Credentials>(std::move(tgt)));
    } else {
      tgts_obj.setTgtForRealm(
        server_principal.getComponent(1),
        folly::make_unique<Krb5Credentials>(std::move(tgt)));
    }
  }
  tgts_ = std::move(tgts_obj);
  logger_->logEnd("import_tgts", folly::to<std::string>(count));

  // Import service creds
  DataMapType new_data_map;
  std::queue<std::string> new_data_queue;
  logger_->logStart("import_service_creds");
  count = 0;
  for (auto& service : services) {
    if (maxCacheSize_ >= 0 && count >= maxCacheSize_) {
      break;
    }
    Krb5Principal princ = Krb5Principal::copyPrincipal(
      ctx_.get(), service.get().server);
    auto mem = initCacheForService(princ, &service.get());

    auto data = std::make_shared<ServiceData>();
    std::string name = folly::to<string>(princ);
    new_data_map[name] = data;
    new_data_queue.push(name);
    data->cache = std::move(mem);
    data->bumpCount();
    count++;
  }

  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_ = std::move(new_data_map);
  cacheItemQueue_ = std::move(new_data_queue);
  logger_->logEnd("import_service_creds", folly::to<std::string>(count));
}

std::vector<Krb5Principal> Krb5CCacheStore::getServicePrincipalList() {
  std::vector<Krb5Principal> services;
  folly::SharedMutex::ReadHolder lock(serviceDataMapLock_);
  for (auto& data : serviceDataMap_) {
    folly::SharedMutex::ReadHolder lock(data.second->lockCache);
    if (!data.second->cache) {
      continue;
    }
    auto princ_list = data.second->cache->getServicePrincipalList();
    if (princ_list.size() < 1) {
      throw std::runtime_error("Principal list too small in ccache");
    }
    auto princ = std::move(princ_list[0]);
    services.push_back(std::move(princ));
  }
  return services;
}

uint64_t Krb5CCacheStore::renewCreds() {
  auto curTgt = tgts_.getTgt();
  auto client_princ = tgts_.getClientPrincipal();
  auto realms = tgts_.getValidRealms();
  uint64_t renewCount = 0;

  // Renew TGTs
  Krb5Tgts tgts;
  tgts.kInit(client_princ);
  for (const auto& realm : realms) {
    tgts.getTgtForRealm(realm);
  }
  tgts_ = std::move(tgts);

  // Renew service creds, and store the renewed creds in a temporary map
  std::unordered_map<string, std::unique_ptr<Krb5CCache>> renewed_map;
  for (auto& service : getServicePrincipalList()) {
    try {
      auto mem = initCacheForService(service);
      renewed_map[folly::to<string>(service)] = std::move(mem);
      renewCount++;
    } catch (const std::runtime_error& e) {
      VLOG(4) << "Failed to renew cred for service: "
              << folly::to<string>(service) << " "
              << e.what();
    }
  }

  // Update the main service data map with the renewed creds.
  // If the creds failed to be renewed and the old ones are stale, then
  // just delete the old ones.
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  for (auto& entry : serviceDataMap_) {
    auto renewed_entry = renewed_map.find(entry.first);
    folly::SharedMutex::WriteHolder lock(entry.second->lockCache);
    if (renewed_entry != renewed_map.end()) {
      entry.second->cache = std::move(renewed_entry->second);
    } else if (entry.second->cache) {
      // Not found, see if it's a new cred or old one
      auto lifetime = entry.second->cache->getLifetime();
      auto tgt_lifetime = tgts_.getLifetime();
      if (lifetime.second < tgt_lifetime.second) {
        // Delete old credential
        entry.second->cache.reset();
      }
    } // else
    // It's possible this thread wins the race to hit a service in the
    // data map that hasn't yet obtained a ccache. In this case, just leave
    // it alone. It will get the correct ccache.
  }
  return renewCount;
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::exportCache(size_t limit) {
  Krb5Principal client_principal = tgts_.getClientPrincipal();

  // Make a new memory cache.
  auto temp_cache = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  temp_cache->initialize(client_principal.get());

  {
    // Put 'limit' number of most frequently used credentials into the
    // top_services set.
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    vector<pair<string, uint64_t>> count_vector;
    for (auto& element : serviceDataMap_) {
      count_vector.emplace_back(element.first, element.second->getCount());
    }

    sort(count_vector.begin(), count_vector.end(), serviceCountCompare);

    std::set<string> top_services;
    int count = 0;
    for (auto& element : count_vector) {
      if (count >= limit) {
        break;
      }
      top_services.insert(element.first);
      count++;
    }

    for (auto& data : serviceDataMap_) {
      folly::SharedMutex::ReadHolder lock(data.second->lockCache);
      if (!data.second->cache) {
        continue;
      }
      auto princ_list = data.second->cache->getServicePrincipalList();
      if (princ_list.size() < 1) {
        throw std::runtime_error("Principal list too small in ccache");
      }
      auto princ = std::move(princ_list[0]);
      if (top_services.count(folly::to<string>(princ)) == 0) {
        continue;
      }
      Krb5Credentials service_cred = data.second->cache->retrieveCred(
        princ.get());
      temp_cache->storeCred(service_cred.get());
    }
  }

  // Store the TGTs in the cache.
  temp_cache->storeCred(tgts_.getTgt()->get());
  auto realms = tgts_.getValidRealms();
  for (const auto& realm : realms) {
    auto cred = tgts_.getTgtForRealm(realm);
    temp_cache->storeCred(cred->get());
  }
  return temp_cache;
}

bool Krb5CCacheStore::isInitialized() {
  return tgts_.isInitialized();
}

std::pair<uint64_t, uint64_t> Krb5CCacheStore::getLifetime() {
  return tgts_.getLifetime();
}

Krb5Principal Krb5CCacheStore::getClientPrincipal() {
  return tgts_.getClientPrincipal();
}

void Krb5CCacheStore::kInit(const Krb5Principal& client) {
  tgts_.kInit(client);
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_.clear();
  std::queue<std::string> emptyQueue;
  cacheItemQueue_ = std::move(emptyQueue);
}

void Krb5CCacheStore::notifyOfError(const std::string& error) {
  tgts_.notifyOfError(error);
}

}}}
<commit_msg>Add a log line if we ignore a service with an empty realm.<commit_after>/*
 * Copyright 2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <thrift/lib/cpp/util/kerberos/Krb5CCacheStore.h>

#include <glog/logging.h>
#include <memory>
#include <set>
#include <stdio.h>

#include <folly/stats/BucketedTimeSeries-defs.h>
#include <folly/Memory.h>
#include <folly/ScopeGuard.h>
#include <folly/String.h>

// In older version of krb, this constant is not defined. Define it
#ifndef KRB5_GC_NO_STORE
#define KRB5_GC_NO_STORE     8  /**< Do not store in credential cache */
#endif

namespace apache { namespace thrift { namespace krb5 {
using namespace std;

const int Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS = 10;
const int Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD = 600;

static bool serviceCountCompare (
    const pair<string, uint64_t>& i,
    const pair<string, uint64_t>& j) {
  return (i.second > j.second);
}

Krb5CCacheStore::ServiceData::ServiceData() :
  timeSeries(
    Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS,
    std::chrono::seconds(
      Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD)) {
}

void Krb5CCacheStore::ServiceData::bumpCount() {
  folly::SharedMutex::WriteHolder guard(lockTimeSeries);
  time_t now = time(nullptr);
  timeSeries.addValue(std::chrono::seconds(now), 1);
}

uint64_t Krb5CCacheStore::ServiceData::getCount() {
  folly::SharedMutex::ReadHolder guard(lockTimeSeries);
  // Note that we don't have a need to call timeSeries_.update(<time>)
  // here because we don't care to have the exact count at the current
  // time. We're ok with grabbing the count at the last update.
  return timeSeries.count();
}

std::shared_ptr<Krb5CCache> Krb5CCacheStore::waitForCache(
    const Krb5Principal& service,
    SecurityLogger* logger) {
  std::shared_ptr<ServiceData> dataPtr = getServiceDataPtr(service);

  // Bump that we've used the principal
  dataPtr->bumpCount();

  if (logger) {
    logger->logStart("get_prepared_cache", folly::to<string>(service));
  }

  {
    folly::SharedMutex::ReadHolder readLock(dataPtr->lockCache);
    // If there is a cache, just return it. Try with a read lock first
    // for performance reasons.
    if (dataPtr->cache) {
      if (logger) {
        logger->logEnd("get_prepared_cache");
      }
      return dataPtr->cache;
    }
  }

  // If we didn't find a cache, initialize a local, temporary cache for the
  // service. We might throw this away if some other thread initialized the
  // cache meanwhile.
  if (logger) {
    logger->logStart("init_cache_for_service", folly::to<string>(service));
  }
  auto tempCache = initCacheForService(service, nullptr, logger);
  if (logger) {
    logger->logEnd("init_cache_for_service");
  }

  // Upgrade to a write lock, and initialize the cache only if it is not already
  // initialized by some other thread meanwhile.
  folly::SharedMutex::WriteHolder writeLock(dataPtr->lockCache);
  if (!dataPtr->cache) {
    dataPtr->cache = std::move(tempCache);
  }
  return dataPtr->cache;
}

std::shared_ptr<Krb5CCacheStore::ServiceData>
    Krb5CCacheStore::getServiceDataPtr(const Krb5Principal& service) {

  // If ccache is disabled, just return a fresh ServiceData obj
  if (maxCacheSize_ == 0) {
    return std::make_shared<ServiceData>();
  }

  string service_name = folly::to<string>(service);
  {
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    auto found = serviceDataMap_.find(service_name);
    if (found != serviceDataMap_.end()) {
      // Get the element from the map if it exists
      return found->second;
    }
  }

  // Not found, we need to create it
  folly::SharedMutex::WriteHolder writeLock(serviceDataMapLock_);
  auto found = serviceDataMap_.find(service_name);
  if (found == serviceDataMap_.end()) {
    // If we reached the limit, then we need to free some room
    if (maxCacheSize_ > 0 && cacheItemQueue_.size() >= maxCacheSize_) {
      serviceDataMap_.erase(cacheItemQueue_.front());
      cacheItemQueue_.pop();
    }
    serviceDataMap_[service_name] = std::make_shared<ServiceData>();
    cacheItemQueue_.push(service_name);
  }
  return serviceDataMap_[service_name];
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::initCacheForService(
    const Krb5Principal& service,
    const krb5_creds* creds,
    SecurityLogger* logger) {

  if (logger) {
    logger->logStart("wait_for_tgt");
  }
  std::shared_ptr<const Krb5Credentials> tgt;
  auto client_princ = tgts_.getClientPrincipal();
  // Get a cross-realm tgt if we need
  string srealm = service.getRealm();
  if (srealm != tgts_.getClientPrincipal().getRealm()) {
    tgt = tgts_.getTgtForRealm(srealm);
  } else {
    tgt = tgts_.getTgt();
  }
  if (logger) {
    logger->logEnd("wait_for_tgt");
  }

  if (logger) {
    logger->logStart("init_barebones_ccache");
  }
  // Make a new memory cache.
  auto mem = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  mem->setDestroyOnClose();
  mem->initialize(client_princ.get());
  mem->storeCred(tgt->get());
  if (logger) {
    logger->logEnd("init_barebones_ccache");
  }

  if (creds != nullptr) {
    mem->storeCred(*creds);
  } else {
    if (logger) {
      logger->logStart("get_credential_from_kdc");
    }
    mem->getCredentials(service.get());
    if (logger) {
      logger->logEnd("get_credential_from_kdc");
    }
  }

  return mem;
}

void Krb5CCacheStore::importCache(
    Krb5CCache& file_cache) {
  logger_->logStart("import_tgts");
  // Split out tgts and service principals
  std::vector<Krb5Credentials> tgts;
  std::vector<Krb5Credentials> services;
  uint64_t count = 0;
  for (auto& tmp_cred : file_cache) {
    Krb5Credentials cred(std::move(tmp_cred));
    Krb5Principal server = Krb5Principal::copyPrincipal(
      ctx_.get(), cred.get().server);
    if (server.isTgt()) {
      tgts.push_back(std::move(cred));
      count++;
    } else if (!server.getRealm().empty()) {
      // Sometimes, somehow, principals for host/{hg.vvv,svn.vip} have no
      // realm!!  Why?  No idea, but they exist and they blow up the entire
      // cache reading thread, so defend against them for now by ignoring that
      // entry (example: #7808411).
      services.push_back(std::move(cred));
    } else if logger() {
      logger->log("Realm empty, so service ignored");
    }

  }

  // Import TGTs
  Krb5Tgts tgts_obj;
  Krb5Principal client_principal = file_cache.getClientPrincipal();
  tgts_obj.setClientPrincipal(client_principal);
  for (auto& tgt : tgts) {
    Krb5Principal server_principal = Krb5Principal::copyPrincipal(
      ctx_.get(), tgt.get().server);
    if (server_principal.getComponent(1) == client_principal.getRealm()) {
      tgts_obj.setTgt(folly::make_unique<Krb5Credentials>(std::move(tgt)));
    } else {
      tgts_obj.setTgtForRealm(
        server_principal.getComponent(1),
        folly::make_unique<Krb5Credentials>(std::move(tgt)));
    }
  }
  tgts_ = std::move(tgts_obj);
  logger_->logEnd("import_tgts", folly::to<std::string>(count));

  // Import service creds
  DataMapType new_data_map;
  std::queue<std::string> new_data_queue;
  logger_->logStart("import_service_creds");
  count = 0;
  for (auto& service : services) {
    if (maxCacheSize_ >= 0 && count >= maxCacheSize_) {
      break;
    }
    Krb5Principal princ = Krb5Principal::copyPrincipal(
      ctx_.get(), service.get().server);
    auto mem = initCacheForService(princ, &service.get());

    auto data = std::make_shared<ServiceData>();
    std::string name = folly::to<string>(princ);
    new_data_map[name] = data;
    new_data_queue.push(name);
    data->cache = std::move(mem);
    data->bumpCount();
    count++;
  }

  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_ = std::move(new_data_map);
  cacheItemQueue_ = std::move(new_data_queue);
  logger_->logEnd("import_service_creds", folly::to<std::string>(count));
}

std::vector<Krb5Principal> Krb5CCacheStore::getServicePrincipalList() {
  std::vector<Krb5Principal> services;
  folly::SharedMutex::ReadHolder lock(serviceDataMapLock_);
  for (auto& data : serviceDataMap_) {
    folly::SharedMutex::ReadHolder lock(data.second->lockCache);
    if (!data.second->cache) {
      continue;
    }
    auto princ_list = data.second->cache->getServicePrincipalList();
    if (princ_list.size() < 1) {
      throw std::runtime_error("Principal list too small in ccache");
    }
    auto princ = std::move(princ_list[0]);
    services.push_back(std::move(princ));
  }
  return services;
}

uint64_t Krb5CCacheStore::renewCreds() {
  auto curTgt = tgts_.getTgt();
  auto client_princ = tgts_.getClientPrincipal();
  auto realms = tgts_.getValidRealms();
  uint64_t renewCount = 0;

  // Renew TGTs
  Krb5Tgts tgts;
  tgts.kInit(client_princ);
  for (const auto& realm : realms) {
    tgts.getTgtForRealm(realm);
  }
  tgts_ = std::move(tgts);

  // Renew service creds, and store the renewed creds in a temporary map
  std::unordered_map<string, std::unique_ptr<Krb5CCache>> renewed_map;
  for (auto& service : getServicePrincipalList()) {
    try {
      auto mem = initCacheForService(service);
      renewed_map[folly::to<string>(service)] = std::move(mem);
      renewCount++;
    } catch (const std::runtime_error& e) {
      VLOG(4) << "Failed to renew cred for service: "
              << folly::to<string>(service) << " "
              << e.what();
    }
  }

  // Update the main service data map with the renewed creds.
  // If the creds failed to be renewed and the old ones are stale, then
  // just delete the old ones.
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  for (auto& entry : serviceDataMap_) {
    auto renewed_entry = renewed_map.find(entry.first);
    folly::SharedMutex::WriteHolder lock(entry.second->lockCache);
    if (renewed_entry != renewed_map.end()) {
      entry.second->cache = std::move(renewed_entry->second);
    } else if (entry.second->cache) {
      // Not found, see if it's a new cred or old one
      auto lifetime = entry.second->cache->getLifetime();
      auto tgt_lifetime = tgts_.getLifetime();
      if (lifetime.second < tgt_lifetime.second) {
        // Delete old credential
        entry.second->cache.reset();
      }
    } // else
    // It's possible this thread wins the race to hit a service in the
    // data map that hasn't yet obtained a ccache. In this case, just leave
    // it alone. It will get the correct ccache.
  }
  return renewCount;
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::exportCache(size_t limit) {
  Krb5Principal client_principal = tgts_.getClientPrincipal();

  // Make a new memory cache.
  auto temp_cache = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  temp_cache->initialize(client_principal.get());

  {
    // Put 'limit' number of most frequently used credentials into the
    // top_services set.
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    vector<pair<string, uint64_t>> count_vector;
    for (auto& element : serviceDataMap_) {
      count_vector.emplace_back(element.first, element.second->getCount());
    }

    sort(count_vector.begin(), count_vector.end(), serviceCountCompare);

    std::set<string> top_services;
    int count = 0;
    for (auto& element : count_vector) {
      if (count >= limit) {
        break;
      }
      top_services.insert(element.first);
      count++;
    }

    for (auto& data : serviceDataMap_) {
      folly::SharedMutex::ReadHolder lock(data.second->lockCache);
      if (!data.second->cache) {
        continue;
      }
      auto princ_list = data.second->cache->getServicePrincipalList();
      if (princ_list.size() < 1) {
        throw std::runtime_error("Principal list too small in ccache");
      }
      auto princ = std::move(princ_list[0]);
      if (top_services.count(folly::to<string>(princ)) == 0) {
        continue;
      }
      Krb5Credentials service_cred = data.second->cache->retrieveCred(
        princ.get());
      temp_cache->storeCred(service_cred.get());
    }
  }

  // Store the TGTs in the cache.
  temp_cache->storeCred(tgts_.getTgt()->get());
  auto realms = tgts_.getValidRealms();
  for (const auto& realm : realms) {
    auto cred = tgts_.getTgtForRealm(realm);
    temp_cache->storeCred(cred->get());
  }
  return temp_cache;
}

bool Krb5CCacheStore::isInitialized() {
  return tgts_.isInitialized();
}

std::pair<uint64_t, uint64_t> Krb5CCacheStore::getLifetime() {
  return tgts_.getLifetime();
}

Krb5Principal Krb5CCacheStore::getClientPrincipal() {
  return tgts_.getClientPrincipal();
}

void Krb5CCacheStore::kInit(const Krb5Principal& client) {
  tgts_.kInit(client);
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_.clear();
  std::queue<std::string> emptyQueue;
  cacheItemQueue_ = std::move(emptyQueue);
}

void Krb5CCacheStore::notifyOfError(const std::string& error) {
  tgts_.notifyOfError(error);
}

}}}
<|endoftext|>


<commit_before>// Copyright (c) 2012 Plenluno All rights reserved.

#include <gtest/gtest.h>
#include <libj/set.h>
#include <libj/string.h>

namespace libj {

TEST(GTestSet, TestCreate) {
    Set::Ptr s = Set::create();
    ASSERT_EQ(s->size(), 0);
}

TEST(GTestSet, TestInstanceOf) {
    Set::Ptr s = Set::create();
    ASSERT_TRUE(s->instanceOf(Type<Set>::id()));
    ASSERT_TRUE(s->instanceOf(Type<Collection>::id()));
    ASSERT_TRUE(s->instanceOf(Type<Mutable>::id()));
    ASSERT_TRUE(s->instanceOf(Type<Object>::id()));
}

TEST(GTestSet, TestSize) {
    Set::Ptr s = Set::create();
    s->add(123);
    ASSERT_EQ(s->size(), 1);
    s->add(456);
    ASSERT_EQ(s->size(), 2);
    s->add(String::create("abc"));
    ASSERT_EQ(s->size(), 3);
    s->add(String::create("edf"));
    ASSERT_EQ(s->size(), 4);
}

TEST(GTestSet, TestIterator) {
    Set::Ptr s = Set::create();
    s->add(123);
    s->add(456);

    int v1, v2;
    Iterator::Ptr itr = s->iterator();
    ASSERT_TRUE(itr->hasNext());
    ASSERT_TRUE(to<int>(itr->next(), &v1));
    ASSERT_TRUE(v1 == 123 || v1 == 456);
    ASSERT_TRUE(itr->hasNext());
    ASSERT_TRUE(to<int>(itr->next(), &v2));
    ASSERT_TRUE(v1 != v2 && (v2 == 123 || v2 == 456));
    ASSERT_FALSE(itr->hasNext());
}

#ifdef LIBJ_USE_SP
TEST(GTestSet, TestUseCount) {
    Set::CPtr p = Set::create();
    ASSERT_EQ(p.use_count(), 1);
}
#endif

}  // namespace libj
<commit_msg>add tests for Set<commit_after>// Copyright (c) 2012 Plenluno All rights reserved.

#include <gtest/gtest.h>
#include <libj/set.h>
#include <libj/string.h>

namespace libj {

TEST(GTestSet, TestCreate) {
    Set::Ptr s = Set::create();
    ASSERT_EQ(s->size(), 0);
}

TEST(GTestSet, TestInstanceOf) {
    Set::Ptr s = Set::create();
    ASSERT_TRUE(s->instanceOf(Type<Set>::id()));
    ASSERT_TRUE(s->instanceOf(Type<Collection>::id()));
    ASSERT_TRUE(s->instanceOf(Type<Mutable>::id()));
    ASSERT_TRUE(s->instanceOf(Type<Object>::id()));
}

TEST(GTestSet, TestSize) {
    Set::Ptr s = Set::create();
    s->add(123);
    ASSERT_EQ(s->size(), 1);
    s->add(456);
    ASSERT_EQ(s->size(), 2);
    s->add(String::create("abc"));
    ASSERT_EQ(s->size(), 3);
    s->add(String::create("edf"));
    ASSERT_EQ(s->size(), 4);
}

TEST(GTestSet, TestIterator) {
    Set::Ptr s = Set::create();
    s->add(123);
    s->add(456);

    int v1, v2;
    Iterator::Ptr itr = s->iterator();
    ASSERT_TRUE(itr->hasNext());
    ASSERT_TRUE(to<int>(itr->next(), &v1));
    ASSERT_TRUE(v1 == 123 || v1 == 456);
    ASSERT_TRUE(itr->hasNext());
    ASSERT_TRUE(to<int>(itr->next(), &v2));
    ASSERT_TRUE(v1 != v2 && (v2 == 123 || v2 == 456));
    ASSERT_FALSE(itr->hasNext());
}

TEST(GTestSet, TestRemove) {
    Set::Ptr s = Set::create();
    Int i = 5;
    s->add(i);
    s->remove(i);
    ASSERT_EQ(s->size(), 0);
}

TEST(GTestSet, TestClear) {
    Set::Ptr s = Set::create();
    s->add(5);
    s->clear();
    ASSERT_EQ(s->size(), 0);
}

#ifdef LIBJ_USE_SP
TEST(GTestSet, TestUseCount) {
    Set::CPtr p = Set::create();
    ASSERT_EQ(p.use_count(), 1);
}
#endif

}  // namespace libj
<|endoftext|>


<commit_before>#include "storage.h"
#include "util.h"

#include <iostream>
#include <cassert>
#include <cstdarg>
#include <chrono>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include <json/json.h>
#include <boost/filesystem.hpp>

namespace C3 {

  bool _entryEquals(const std::string &entry, const std::string &key) {
    auto eit = entry.begin();
    auto kit = key.begin();

    while(kit != key.end()) {
      if(eit == entry.end()) return false;
      else if(*eit != *kit) return false;
      ++kit;
      ++eit;
    }

    if(eit == entry.end() || *eit == ',') return true;
    else return false;
  }

  CommaSepComparator postCmp({ Limitor::Greater });
  CommaSepComparator commentCmp({ Limitor::Greater, Limitor::Less });
  CommaSepComparator entryCmp({ Limitor::Less, Limitor::Greater });
  CommaSepComparator indexCmp({ Limitor::Less, Limitor::Greater });

  leveldb::DB *postDB;
  leveldb::DB *indexDB;
  leveldb::DB *commentDB;
  leveldb::DB *entryDB;

  Json::Reader reader;
  Json::FastWriter writer;

  Post::Post(
      const std::string &uident,
      const std::string &url,
      const std::string &topic,
      const std::string &content,
      const std::vector<std::string> &tags,
      uint64_t post_time) :
        uident(uident), url(url), topic(topic), content(content), tags(tags), post_time(post_time) {}

  Post::Post(const std::string &json) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    std::vector<std::string> tags(r["tags"].size());
    for(auto it = r["tags"].begin(); it != r["tags"].end(); ++it) {
      tags[it.index()] = it->asString();
    }

    uident = r["uident"].asString();
    url = r["url"].asString();
    topic = r["topic"].asString();
    content = r["content"].asString();
    tags = tags;
    post_time = r["post_time"].asUInt64();
  }

  Post::Post(const std::string &json, const std::string &uident) :
        uident(uident), post_time(current_time()) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    std::vector<std::string> tags(r["tags"].size());
    for(auto it = r["tags"].begin(); it != r["tags"].end(); ++it) {
      tags[it.index()] = it->asString();
    }

    url = r["url"].asString();
    topic = r["topic"].asString();
    content = r["content"].asString();
    tags = tags;
  }

  std::string Post::to_json(void) const {
    Json::Value r;
    r["uident"] = uident;
    r["url"] = url;
    r["topic"] = topic;
    r["content"] = content;
    r["post_time"] = (Json::UInt64) post_time;
    Json::Value v;
    for(auto e : tags) v.append(e);
    r["tags"] = v;

    return writer.write(r);
  }

  Comment::Comment(
      const std::string &uident,
      const std::string &content,
      uint64_t comment_time) :
        uident(uident), content(content), comment_time(comment_time) { }

  Comment::Comment(const std::string &json) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    uident = r["uident"].asString();
    content = r["content"].asString();
    comment_time = r["comment_time"].asUInt64();
  }

  Comment::Comment(const std::string &json, const std::string &uident) :
        uident(uident), comment_time(current_time()) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    content = r["content"].asString();
  }

  std::string Comment::to_json(void) const {
    Json::Value r;
    r["uident"] = uident;
    r["content"] = content;

    return writer.write(r);
  }

  User::User(
      UserType type,
      const std::string &id,
      const std::string &name,
      const std::string &email,
      const std::string &avatar) :
        type(type), id(id), name(name), email(email), avatar(avatar) { }

  User::User(const std::string &json) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    // Type
    std::string typestr = r["type"].asString();
    if(typestr == "google") type = uGoogle;
    else type = uUnknown;

    id = r["id"].asString();
    name = r["name"].asString();
    email = r["email"].asString();
    avatar = r["avatar"].asString();
  }

  std::string User::to_json(void) const {
    Json::Value r;
    if(type == uGoogle) r["type"] = "google";
    else r["type"] = "unknown";

    r["id"] = id;
    r["name"] = name;
    r["email"] = email;
    r["avatar"] = avatar;

    return writer.write(r);
  }

  CommaSepComparator::CommaSepComparator(std::initializer_list<Limitor> lims) :
     lims(lims) { }

  int CommaSepComparator::Compare(const leveldb::Slice &a, const leveldb::Slice &b) const {
    std::string sa = a.ToString();
    std::string sb = b.ToString();

    std::vector<std::string> ia = split(sa, ',');
    std::vector<std::string> ib = split(sb, ',');

    for(int i = 0; ; ++i) {
      if(i == ia.size()) {
        if(i == ib.size()) return 0;
        else return -1;
      } else if(i == ib.size()) return 1;

      Limitor lim;
      if(i<this->lims.size()) lim = this->lims[i];
      else lim = Limitor::Less;

      if(lim == Limitor::Less) {
        if(ia[i] < ib[i]) return -1;
        else if(ia[i] > ib[i]) return 1;
      } else {
        if(ia[i] < ib[i]) return 1;
        else if(ia[i] > ib[i]) return -1;
      }
    }
  }

  const char* CommaSepComparator::Name() const { return "CommaSepComparator"; }

  void CommaSepComparator::FindShortestSeparator(std::string *, const leveldb::Slice &) const { }

  void CommaSepComparator::FindShortSuccessor(std::string *) const { }

  bool setup_storage(const std::string &dir) {
    // Ckeck if the folder exists

    boost::filesystem::path dbpath(dir);
    if(!boost::filesystem::exists(dbpath)) {
      std::cout<<"Storage: Creating database path: "<<dir<<std::endl;
      // Check for parent pathes

      for(auto p = dbpath.parent_path();; p = p.parent_path()) {
        if(p.empty()) break; // Valid
        if(!boost::filesystem::exists(p)) continue;
        if(boost::filesystem::is_directory(p)) break; // Valid
        else {
          std::cout<<"Storage: "<<p.native()<<" is not a directory."<<std::endl;
          return false;
        }
      }
      if(!boost::filesystem::create_directories(dbpath)) {
        std::cout<<"Storage: Failed to create database path"<<std::endl;
        return false;
      }
    } else if(!boost::filesystem::is_directory(dbpath)) {
      std::cout<<"Storage: "<<dir<<" is not a directory."<<std::endl;
      return false;
    }

    // TODO: cache
    std::cout<<"Storage: Opening/Creating db at "<<dir<<std::endl;

    leveldb::Options postOpt;
    postOpt.create_if_missing = true;
    postOpt.comparator = &postCmp;

    leveldb::Status postStatus = leveldb::DB::Open(postOpt, dir + "/post", &postDB);
    assert(postStatus.ok());

    leveldb::Options indexOpt;
    indexOpt.create_if_missing = true;
    indexOpt.comparator = &indexCmp;

    leveldb::Status indexStatus = leveldb::DB::Open(indexOpt, dir + "/index", &indexDB);
    assert(indexStatus.ok());

    leveldb::Options commentOpt;
    commentOpt.create_if_missing = true;
    commentOpt.comparator = &commentCmp;

    leveldb::Status commentStatus = leveldb::DB::Open(commentOpt, dir + "/comment", &commentDB);
    assert(commentStatus.ok());

    leveldb::Options entryOpt;
    entryOpt.create_if_missing = true;
    entryOpt.comparator = &entryCmp;

    leveldb::Status entryStatus = leveldb::DB::Open(entryOpt, dir + "/entry", &entryDB);
    assert(entryStatus.ok());

    return true;
  }

  void stop_storage(void) {
    delete postDB;
    delete indexDB;
    delete commentDB;
    delete indexDB;
  }

  uint64_t add_post(const Post &post) {
    // Using nanoseconds since Unix Epoch as post id
    uint64_t ts = post.post_time;

    //Assume that we can't submit two post at the same nanosecond
    //TODO: upate tags and indexes
    leveldb::Status s = postDB->Put(leveldb::WriteOptions(), std::to_string(ts), post.to_json());
    if(s.ok()) return ts;
    else {
      throw s;
    }
  }

  Post get_post(const uint64_t &id) {
    return Post(get_post_str(id));
  }

  std::string get_post_str(const uint64_t &id) {
    std::string v;
    leveldb::Status s = postDB->Get(leveldb::ReadOptions(), std::to_string(id), &v);
    if(s.ok()) return v;
    else {
      if(s.IsNotFound()) throw NotFound;
      else throw s;
    }
  }

  void update_post(const uint64_t &id, const Post &post) {
    if(!(post.post_time == id)) throw IDMismatch;
    //TODO: upate tags and indexes
    leveldb::Status s = postDB->Put(leveldb::WriteOptions(), std::to_string(id), post.to_json());
    if(!s.ok()) throw s;
  }

  void delete_post(const uint64_t &id) {
    leveldb::Status s = postDB->Delete(leveldb::WriteOptions(), std::to_string(id));
    if(!s.ok()) {
      if(s.IsNotFound()) throw NotFound;
      else throw s;
    }
  }

  std::list<Post> list_posts(int offset, int count) {
    leveldb::Iterator *it = postDB->NewIterator(leveldb::ReadOptions());
    it->SeekToFirst();

    std::list<Post> result;

    while(it->Valid() && offset-- > 0) it->Next();

    for(int i = 0; i < count && it->Valid(); ++i) {
      result.push_back(Post(it->value().ToString()));
      it->Next();
    }

    return result;
  }

  /* Entries */
  void _generate_remove_entries(const uint64_t &id, const std::list<std::string> &list, leveldb::WriteBatch &batch) {
    for(auto it = list.begin(); it != list.end(); ++it)
      batch.Delete(*it + "," + std::to_string(id));
  }

  void _generate_add_entries(const uint64_t &id, const std::list<std::string> &list, leveldb::WriteBatch &batch) {
    for(auto it = list.begin(); it != list.end(); ++it)
      batch.Put(*it + "," + std::to_string(id), std::to_string(id));
  }

  void remove_entries(const uint64_t &id, const std::list<std::string> &list) {
    leveldb::WriteBatch batch;
    _generate_remove_entries(id, list, batch);
    entryDB->Write(leveldb::WriteOptions(), &batch);
  }

  void add_entries(const uint64_t &id, const std::list<std::string> &list) {
    leveldb::WriteBatch batch;
    _generate_add_entries(id, list, batch);
    entryDB->Write(leveldb::WriteOptions(), &batch);
  }

  void add_remove_entries(const uint64_t &id, const std::list<std::string> &add, const std::list<std::string> &remove) {
    leveldb::WriteBatch batch;
    _generate_add_entries(id, add, batch);
    _generate_remove_entries(id, remove, batch);
    /* TODO: status */
    entryDB->Write(leveldb::WriteOptions(), &batch);
  }

  std::list<uint64_t> list_posts_by_tag(const std::string &entry, int offset, int count) {
    leveldb::Iterator *it = entryDB->NewIterator(leveldb::ReadOptions());
    it->Seek(entry);

    std::list<uint64_t> result;
    while(it->Valid() && offset-- > 0) it->Next();

    for(int i = 0; i < count && it->Valid() && _entryEquals(it->key().ToString(), entry); ++i) {
      result.push_back(std::stoull(it->value().ToString()));
      it->Next();
    }
    return result;
  }
}
<commit_msg>Fixed tags not being saved.<commit_after>#include "storage.h"
#include "util.h"

#include <iostream>
#include <cassert>
#include <cstdarg>
#include <chrono>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include <json/json.h>
#include <boost/filesystem.hpp>

namespace C3 {

  bool _entryEquals(const std::string &entry, const std::string &key) {
    auto eit = entry.begin();
    auto kit = key.begin();

    while(kit != key.end()) {
      if(eit == entry.end()) return false;
      else if(*eit != *kit) return false;
      ++kit;
      ++eit;
    }

    if(eit == entry.end() || *eit == ',') return true;
    else return false;
  }

  CommaSepComparator postCmp({ Limitor::Greater });
  CommaSepComparator commentCmp({ Limitor::Greater, Limitor::Less });
  CommaSepComparator entryCmp({ Limitor::Less, Limitor::Greater });
  CommaSepComparator indexCmp({ Limitor::Less, Limitor::Greater });

  leveldb::DB *postDB;
  leveldb::DB *indexDB;
  leveldb::DB *commentDB;
  leveldb::DB *entryDB;

  Json::Reader reader;
  Json::FastWriter writer;

  Post::Post(
      const std::string &uident,
      const std::string &url,
      const std::string &topic,
      const std::string &content,
      const std::vector<std::string> &tags,
      uint64_t post_time) :
        uident(uident), url(url), topic(topic), content(content), tags(tags), post_time(post_time) {}

  Post::Post(const std::string &json) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    std::vector<std::string> tags(r["tags"].size());
    for(auto it = r["tags"].begin(); it != r["tags"].end(); ++it) {
      tags[it.index()] = it->asString();
    }

    uident = r["uident"].asString();
    url = r["url"].asString();
    topic = r["topic"].asString();
    content = r["content"].asString();
    this->tags = tags;
    post_time = r["post_time"].asUInt64();
  }

  Post::Post(const std::string &json, const std::string &uident) :
        uident(uident), post_time(current_time()) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    std::vector<std::string> tags(r["tags"].size());
    for(auto it = r["tags"].begin(); it != r["tags"].end(); ++it) {
      tags[it.index()] = it->asString();
    }

    url = r["url"].asString();
    topic = r["topic"].asString();
    content = r["content"].asString();
    this->tags = tags;
  }

  std::string Post::to_json(void) const {
    Json::Value r;
    r["uident"] = uident;
    r["url"] = url;
    r["topic"] = topic;
    r["content"] = content;
    r["post_time"] = (Json::UInt64) post_time;
    Json::Value v;
    for(auto e : tags) v.append(e);
    r["tags"] = v;

    return writer.write(r);
  }

  Comment::Comment(
      const std::string &uident,
      const std::string &content,
      uint64_t comment_time) :
        uident(uident), content(content), comment_time(comment_time) { }

  Comment::Comment(const std::string &json) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    uident = r["uident"].asString();
    content = r["content"].asString();
    comment_time = r["comment_time"].asUInt64();
  }

  Comment::Comment(const std::string &json, const std::string &uident) :
        uident(uident), comment_time(current_time()) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    content = r["content"].asString();
  }

  std::string Comment::to_json(void) const {
    Json::Value r;
    r["uident"] = uident;
    r["content"] = content;

    return writer.write(r);
  }

  User::User(
      UserType type,
      const std::string &id,
      const std::string &name,
      const std::string &email,
      const std::string &avatar) :
        type(type), id(id), name(name), email(email), avatar(avatar) { }

  User::User(const std::string &json) {
    Json::Value r;
    if(!reader.parse(json, r)) throw ParseError;

    // Type
    std::string typestr = r["type"].asString();
    if(typestr == "google") type = uGoogle;
    else type = uUnknown;

    id = r["id"].asString();
    name = r["name"].asString();
    email = r["email"].asString();
    avatar = r["avatar"].asString();
  }

  std::string User::to_json(void) const {
    Json::Value r;
    if(type == uGoogle) r["type"] = "google";
    else r["type"] = "unknown";

    r["id"] = id;
    r["name"] = name;
    r["email"] = email;
    r["avatar"] = avatar;

    return writer.write(r);
  }

  CommaSepComparator::CommaSepComparator(std::initializer_list<Limitor> lims) :
     lims(lims) { }

  int CommaSepComparator::Compare(const leveldb::Slice &a, const leveldb::Slice &b) const {
    std::string sa = a.ToString();
    std::string sb = b.ToString();

    std::vector<std::string> ia = split(sa, ',');
    std::vector<std::string> ib = split(sb, ',');

    for(int i = 0; ; ++i) {
      if(i == ia.size()) {
        if(i == ib.size()) return 0;
        else return -1;
      } else if(i == ib.size()) return 1;

      Limitor lim;
      if(i<this->lims.size()) lim = this->lims[i];
      else lim = Limitor::Less;

      if(lim == Limitor::Less) {
        if(ia[i] < ib[i]) return -1;
        else if(ia[i] > ib[i]) return 1;
      } else {
        if(ia[i] < ib[i]) return 1;
        else if(ia[i] > ib[i]) return -1;
      }
    }
  }

  const char* CommaSepComparator::Name() const { return "CommaSepComparator"; }

  void CommaSepComparator::FindShortestSeparator(std::string *, const leveldb::Slice &) const { }

  void CommaSepComparator::FindShortSuccessor(std::string *) const { }

  bool setup_storage(const std::string &dir) {
    // Ckeck if the folder exists

    boost::filesystem::path dbpath(dir);
    if(!boost::filesystem::exists(dbpath)) {
      std::cout<<"Storage: Creating database path: "<<dir<<std::endl;
      // Check for parent pathes

      for(auto p = dbpath.parent_path();; p = p.parent_path()) {
        if(p.empty()) break; // Valid
        if(!boost::filesystem::exists(p)) continue;
        if(boost::filesystem::is_directory(p)) break; // Valid
        else {
          std::cout<<"Storage: "<<p.native()<<" is not a directory."<<std::endl;
          return false;
        }
      }
      if(!boost::filesystem::create_directories(dbpath)) {
        std::cout<<"Storage: Failed to create database path"<<std::endl;
        return false;
      }
    } else if(!boost::filesystem::is_directory(dbpath)) {
      std::cout<<"Storage: "<<dir<<" is not a directory."<<std::endl;
      return false;
    }

    // TODO: cache
    std::cout<<"Storage: Opening/Creating db at "<<dir<<std::endl;

    leveldb::Options postOpt;
    postOpt.create_if_missing = true;
    postOpt.comparator = &postCmp;

    leveldb::Status postStatus = leveldb::DB::Open(postOpt, dir + "/post", &postDB);
    assert(postStatus.ok());

    leveldb::Options indexOpt;
    indexOpt.create_if_missing = true;
    indexOpt.comparator = &indexCmp;

    leveldb::Status indexStatus = leveldb::DB::Open(indexOpt, dir + "/index", &indexDB);
    assert(indexStatus.ok());

    leveldb::Options commentOpt;
    commentOpt.create_if_missing = true;
    commentOpt.comparator = &commentCmp;

    leveldb::Status commentStatus = leveldb::DB::Open(commentOpt, dir + "/comment", &commentDB);
    assert(commentStatus.ok());

    leveldb::Options entryOpt;
    entryOpt.create_if_missing = true;
    entryOpt.comparator = &entryCmp;

    leveldb::Status entryStatus = leveldb::DB::Open(entryOpt, dir + "/entry", &entryDB);
    assert(entryStatus.ok());

    return true;
  }

  void stop_storage(void) {
    delete postDB;
    delete indexDB;
    delete commentDB;
    delete indexDB;
  }

  uint64_t add_post(const Post &post) {
    // Using nanoseconds since Unix Epoch as post id
    uint64_t ts = post.post_time;

    //Assume that we can't submit two post at the same nanosecond
    //TODO: upate tags and indexes
    leveldb::Status s = postDB->Put(leveldb::WriteOptions(), std::to_string(ts), post.to_json());
    if(s.ok()) return ts;
    else {
      throw s;
    }
  }

  Post get_post(const uint64_t &id) {
    return Post(get_post_str(id));
  }

  std::string get_post_str(const uint64_t &id) {
    std::string v;
    leveldb::Status s = postDB->Get(leveldb::ReadOptions(), std::to_string(id), &v);
    if(s.ok()) return v;
    else {
      if(s.IsNotFound()) throw NotFound;
      else throw s;
    }
  }

  void update_post(const uint64_t &id, const Post &post) {
    if(!(post.post_time == id)) throw IDMismatch;
    //TODO: upate tags and indexes
    leveldb::Status s = postDB->Put(leveldb::WriteOptions(), std::to_string(id), post.to_json());
    if(!s.ok()) throw s;
  }

  void delete_post(const uint64_t &id) {
    leveldb::Status s = postDB->Delete(leveldb::WriteOptions(), std::to_string(id));
    if(!s.ok()) {
      if(s.IsNotFound()) throw NotFound;
      else throw s;
    }
  }

  std::list<Post> list_posts(int offset, int count) {
    leveldb::Iterator *it = postDB->NewIterator(leveldb::ReadOptions());
    it->SeekToFirst();

    std::list<Post> result;

    while(it->Valid() && offset-- > 0) it->Next();

    for(int i = 0; i < count && it->Valid(); ++i) {
      result.push_back(Post(it->value().ToString()));
      it->Next();
    }

    return result;
  }

  /* Entries */
  void _generate_remove_entries(const uint64_t &id, const std::list<std::string> &list, leveldb::WriteBatch &batch) {
    for(auto it = list.begin(); it != list.end(); ++it)
      batch.Delete(*it + "," + std::to_string(id));
  }

  void _generate_add_entries(const uint64_t &id, const std::list<std::string> &list, leveldb::WriteBatch &batch) {
    for(auto it = list.begin(); it != list.end(); ++it)
      batch.Put(*it + "," + std::to_string(id), std::to_string(id));
  }

  void remove_entries(const uint64_t &id, const std::list<std::string> &list) {
    leveldb::WriteBatch batch;
    _generate_remove_entries(id, list, batch);
    entryDB->Write(leveldb::WriteOptions(), &batch);
  }

  void add_entries(const uint64_t &id, const std::list<std::string> &list) {
    leveldb::WriteBatch batch;
    _generate_add_entries(id, list, batch);
    entryDB->Write(leveldb::WriteOptions(), &batch);
  }

  void add_remove_entries(const uint64_t &id, const std::list<std::string> &add, const std::list<std::string> &remove) {
    leveldb::WriteBatch batch;
    _generate_add_entries(id, add, batch);
    _generate_remove_entries(id, remove, batch);
    /* TODO: status */
    entryDB->Write(leveldb::WriteOptions(), &batch);
  }

  std::list<uint64_t> list_posts_by_tag(const std::string &entry, int offset, int count) {
    leveldb::Iterator *it = entryDB->NewIterator(leveldb::ReadOptions());
    it->Seek(entry);

    std::list<uint64_t> result;
    while(it->Valid() && offset-- > 0) it->Next();

    for(int i = 0; i < count && it->Valid() && _entryEquals(it->key().ToString(), entry); ++i) {
      result.push_back(std::stoull(it->value().ToString()));
      it->Next();
    }
    return result;
  }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: dlgedview.cxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 00:32:33 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basctl.hxx"

#ifndef _BASCTL_DLGEDVIEW_HXX
#include "dlgedview.hxx"
#endif

#ifndef _BASCTL_DLGED_HXX
#include "dlged.hxx"
#endif

#ifndef _BASCTL_DLGEDPAGE_HXX
#include <dlgedpage.hxx>
#endif

#ifndef _SVXIDS_HRC
#include <svx/svxids.hrc>
#endif

#ifndef _SFXVIEWFRM_HXX
#include <sfx2/viewfrm.hxx>
#endif

#include <basidesh.hxx>
#include <iderdll.hxx>

TYPEINIT1( DlgEdView, SdrView );

//----------------------------------------------------------------------------

DlgEdView::DlgEdView( SdrModel* pModel, OutputDevice* pOut, DlgEditor* pEditor )
    :SdrView( pModel, pOut )
    ,pDlgEditor( pEditor )
{
}

//----------------------------------------------------------------------------

DlgEdView::~DlgEdView()
{
}

//----------------------------------------------------------------------------

void DlgEdView::MarkListHasChanged()
{
    SdrView::MarkListHasChanged();

    DlgEdHint aHint( DLGED_HINT_SELECTIONCHANGED );
    if ( pDlgEditor )
    {
        pDlgEditor->Broadcast( aHint );
        pDlgEditor->UpdatePropertyBrowserDelayed();
    }
}

//----------------------------------------------------------------------------

void DlgEdView::MakeVisible( const Rectangle& rRect, Window& rWin )
{
    // visible area
    MapMode aMap( rWin.GetMapMode() );
    Point aOrg( aMap.GetOrigin() );
    Size aVisSize( rWin.GetOutputSize() );
    Rectangle RectTmp( Point(-aOrg.X(),-aOrg.Y()), aVisSize );
    Rectangle aVisRect( RectTmp );

    // check, if rectangle is inside visible area
    if ( !aVisRect.IsInside( rRect ) )
    {
        // calculate scroll distance; the rectangle must be inside the visible area
        sal_Int32 nScrollX = 0, nScrollY = 0;

        sal_Int32 nVisLeft   = aVisRect.Left();
        sal_Int32 nVisRight  = aVisRect.Right();
        sal_Int32 nVisTop    = aVisRect.Top();
        sal_Int32 nVisBottom = aVisRect.Bottom();

        sal_Int32 nDeltaX = pDlgEditor->GetHScroll()->GetLineSize();
        sal_Int32 nDeltaY = pDlgEditor->GetVScroll()->GetLineSize();

        while ( rRect.Right() > nVisRight + nScrollX )
            nScrollX += nDeltaX;

        while ( rRect.Left() < nVisLeft + nScrollX )
            nScrollX -= nDeltaX;

        while ( rRect.Bottom() > nVisBottom + nScrollY )
            nScrollY += nDeltaY;

        while ( rRect.Top() < nVisTop + nScrollY )
            nScrollY -= nDeltaY;

        // don't scroll beyond the page size
        Size aPageSize = pDlgEditor->GetPage()->GetSize();
        sal_Int32 nPageWidth  = aPageSize.Width();
        sal_Int32 nPageHeight = aPageSize.Height();

        if ( nVisRight + nScrollX > nPageWidth )
            nScrollX = nPageWidth - nVisRight;

        if ( nVisLeft + nScrollX < 0 )
            nScrollX = -nVisLeft;

        if ( nVisBottom + nScrollY > nPageHeight )
            nScrollY = nPageHeight - nVisBottom;

        if ( nVisTop + nScrollY < 0 )
            nScrollY = -nVisTop;

        // scroll window
        rWin.Update();
        rWin.Scroll( -nScrollX, -nScrollY );
        aMap.SetOrigin( Point( aOrg.X() - nScrollX, aOrg.Y() - nScrollY ) );
        rWin.SetMapMode( aMap );
        rWin.Update();
        rWin.Invalidate();

        // update scroll bars
        if ( pDlgEditor )
            pDlgEditor->UpdateScrollBars();

        DlgEdHint aHint( DLGED_HINT_WINDOWSCROLLED );
        if ( pDlgEditor )
            pDlgEditor->Broadcast( aHint );
    }
}

//----------------------------------------------------------------------------

<commit_msg>INTEGRATION: CWS aw024 (1.10.2); FILE MERGED 2006/09/22 03:53:14 aw 1.10.2.2: RESYNC: (1.10-1.11); FILE MERGED 2006/07/28 16:06:08 aw 1.10.2.1: #114408# adaptions for SW<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: dlgedview.cxx,v $
 *
 *  $Revision: 1.12 $
 *
 *  last change: $Author: ihi $ $Date: 2006-11-14 15:29:13 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basctl.hxx"

#ifndef _BASCTL_DLGEDVIEW_HXX
#include "dlgedview.hxx"
#endif

#ifndef _BASCTL_DLGED_HXX
#include "dlged.hxx"
#endif

#ifndef _BASCTL_DLGEDPAGE_HXX
#include <dlgedpage.hxx>
#endif

#ifndef _SVXIDS_HRC
#include <svx/svxids.hrc>
#endif

#ifndef _SFXVIEWFRM_HXX
#include <sfx2/viewfrm.hxx>
#endif

#include <basidesh.hxx>
#include <iderdll.hxx>

TYPEINIT1( DlgEdView, SdrView );

//----------------------------------------------------------------------------

DlgEdView::DlgEdView( SdrModel* pModel, OutputDevice* pOut, DlgEditor* pEditor )
    :SdrView( pModel, pOut )
    ,pDlgEditor( pEditor )
{
    // #114898#
    SetBufferedOutputAllowed(true);
    SetBufferedOverlayAllowed(true);
}

//----------------------------------------------------------------------------

DlgEdView::~DlgEdView()
{
}

//----------------------------------------------------------------------------

void DlgEdView::MarkListHasChanged()
{
    SdrView::MarkListHasChanged();

    DlgEdHint aHint( DLGED_HINT_SELECTIONCHANGED );
    if ( pDlgEditor )
    {
        pDlgEditor->Broadcast( aHint );
        pDlgEditor->UpdatePropertyBrowserDelayed();
    }
}

//----------------------------------------------------------------------------

void DlgEdView::MakeVisible( const Rectangle& rRect, Window& rWin )
{
    // visible area
    MapMode aMap( rWin.GetMapMode() );
    Point aOrg( aMap.GetOrigin() );
    Size aVisSize( rWin.GetOutputSize() );
    Rectangle RectTmp( Point(-aOrg.X(),-aOrg.Y()), aVisSize );
    Rectangle aVisRect( RectTmp );

    // check, if rectangle is inside visible area
    if ( !aVisRect.IsInside( rRect ) )
    {
        // calculate scroll distance; the rectangle must be inside the visible area
        sal_Int32 nScrollX = 0, nScrollY = 0;

        sal_Int32 nVisLeft   = aVisRect.Left();
        sal_Int32 nVisRight  = aVisRect.Right();
        sal_Int32 nVisTop    = aVisRect.Top();
        sal_Int32 nVisBottom = aVisRect.Bottom();

        sal_Int32 nDeltaX = pDlgEditor->GetHScroll()->GetLineSize();
        sal_Int32 nDeltaY = pDlgEditor->GetVScroll()->GetLineSize();

        while ( rRect.Right() > nVisRight + nScrollX )
            nScrollX += nDeltaX;

        while ( rRect.Left() < nVisLeft + nScrollX )
            nScrollX -= nDeltaX;

        while ( rRect.Bottom() > nVisBottom + nScrollY )
            nScrollY += nDeltaY;

        while ( rRect.Top() < nVisTop + nScrollY )
            nScrollY -= nDeltaY;

        // don't scroll beyond the page size
        Size aPageSize = pDlgEditor->GetPage()->GetSize();
        sal_Int32 nPageWidth  = aPageSize.Width();
        sal_Int32 nPageHeight = aPageSize.Height();

        if ( nVisRight + nScrollX > nPageWidth )
            nScrollX = nPageWidth - nVisRight;

        if ( nVisLeft + nScrollX < 0 )
            nScrollX = -nVisLeft;

        if ( nVisBottom + nScrollY > nPageHeight )
            nScrollY = nPageHeight - nVisBottom;

        if ( nVisTop + nScrollY < 0 )
            nScrollY = -nVisTop;

        // scroll window
        rWin.Update();
        rWin.Scroll( -nScrollX, -nScrollY );
        aMap.SetOrigin( Point( aOrg.X() - nScrollX, aOrg.Y() - nScrollY ) );
        rWin.SetMapMode( aMap );
        rWin.Update();
        rWin.Invalidate();

        // update scroll bars
        if ( pDlgEditor )
            pDlgEditor->UpdateScrollBars();

        DlgEdHint aHint( DLGED_HINT_WINDOWSCROLLED );
        if ( pDlgEditor )
            pDlgEditor->Broadcast( aHint );
    }
}

//----------------------------------------------------------------------------

<|endoftext|>


<commit_before>#include "fsettingsdialog.h"
#include "ui_fsettingsdialog.h"
#include <QDebug>
#include <fartmanager.h>
#include <QMessageBox>
#include <QDesktopServices>

FSettingsDialog::FSettingsDialog(FDB *db, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::FSettingsDialog)
{
    ui->setupUi(this);
    this->db = db;

    //Hast to be the Same order as the Stacked Widget.
   ui->listWidget->addItem("General");
   ui->listWidget->addItem("Database");
   ui->listWidget->addItem("Interface");
   ui->listWidget->addItem("Artwork");
   ui->listWidget->setCurrentRow(0);

   ui->le_Stylesheet->setText(db->getTextPref("stylesheet"));
   ui->cb_ScanLibOnStartup->setChecked(db->getBoolPref("ScanLibsOnStartup", true));

   ui->cb_Artwork_UseCache->setChecked(db->getBoolPref("useArtworkCache", true));

}

FSettingsDialog::~FSettingsDialog()
{
    delete ui;
}

void FSettingsDialog::on_listWidget_currentRowChanged(int i)
{
    ui->settingPages->setCurrentIndex(i);
}

void FSettingsDialog::on_pb_selectStylesheet_clicked()
{
    QString stylesheetFile = QFileDialog::getOpenFileName(this, "Choose stylesheet", QDir::homePath(), "*.qss");
    qDebug() << "Stylesheet: " << stylesheetFile;
    if(QFile::exists(stylesheetFile))
    {
        qDebug() << "New stylesheet added: " << stylesheetFile;
        db->updateTextPref("stylesheet", stylesheetFile);
        emit reloadStylesheet();
    }
}

void FSettingsDialog::on_pb_ResetStylesheet_clicked()
{
    db->updateTextPref("stylesheet", ":/stylesheet.qss");
    emit reloadStylesheet();
}

void FSettingsDialog::on_pb_ScanNow_clicked()
{
    FCrawler crawler;
    crawler.scanAllFolders();
    emit reloadLibrary();
}

void FSettingsDialog::on_btn_Artwork_DownloadAll_clicked() {
    if(QMessageBox::warning(this, "Please confirm!", "If Fusion is able to find Artwork, existing Artwork will be overwritten!",QMessageBox::Ok, QMessageBox::Cancel) ==QMessageBox::Cancel)
        return;

    runningDownloads = 0;
    totalDownloads = 0;

   gameList = db->getGameList();
   for(int i=0;i<gameList.length();++i) {
       FArtManager *artmanager = new FArtManager();
       connect(artmanager, SIGNAL(startedDownload()), this, SLOT(downloadStarted()));
       connect(artmanager, SIGNAL(finishedDownload()), this, SLOT(downloadFinished()));
       artmanager->getGameData(&gameList[i], "PC");
       artmanager->deleteLater();
   }

}

void FSettingsDialog::downloadFinished() {
    --runningDownloads;
    ui->la_Artwork_DownloadStatus->setText("Running Downloads:" + QString::number(runningDownloads));
    if(runningDownloads<=0)
        QMessageBox::information(this, "Downloads finished", "Finished " + QString::number(totalDownloads) + " download(s)");
}

void FSettingsDialog::downloadStarted() {
    ++runningDownloads;
    ++totalDownloads;
    ui->la_Artwork_DownloadStatus->setText("Running Downloads:" + QString::number(runningDownloads));
}

void FSettingsDialog::on_btn_Artwork_openCache_clicked() {
    QDesktopServices::openUrl(FGame::getCacheDir());

}

void FSettingsDialog::on_btn_Artwork_ClearCache_clicked() {
    QDir cacheDir(FGame::getCacheDir());
    cacheDir.removeRecursively();
}

void FSettingsDialog::on_buttonBox_accepted()
{
   db->updateBoolPref("ScanLibsOnStartup", (bool)ui->cb_ScanLibOnStartup->checkState());

   //Artwortk-Page
   db->updateBoolPref("useArtworkCache", (bool)ui->cb_Artwork_UseCache->checkState());
}
<commit_msg>Fix the Cache-Clear on QT <5<commit_after>#include "fsettingsdialog.h"
#include "ui_fsettingsdialog.h"
#include <QDebug>
#include <fartmanager.h>
#include <QMessageBox>
#include <QDesktopServices>

FSettingsDialog::FSettingsDialog(FDB *db, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::FSettingsDialog)
{
    ui->setupUi(this);
    this->db = db;

    //Hast to be the Same order as the Stacked Widget.
   ui->listWidget->addItem("General");
   ui->listWidget->addItem("Database");
   ui->listWidget->addItem("Interface");
   ui->listWidget->addItem("Artwork");
   ui->listWidget->setCurrentRow(0);

   ui->le_Stylesheet->setText(db->getTextPref("stylesheet"));
   ui->cb_ScanLibOnStartup->setChecked(db->getBoolPref("ScanLibsOnStartup", true));

   ui->cb_Artwork_UseCache->setChecked(db->getBoolPref("useArtworkCache", true));

}

FSettingsDialog::~FSettingsDialog()
{
    delete ui;
}

void FSettingsDialog::on_listWidget_currentRowChanged(int i)
{
    ui->settingPages->setCurrentIndex(i);
}

void FSettingsDialog::on_pb_selectStylesheet_clicked()
{
    QString stylesheetFile = QFileDialog::getOpenFileName(this, "Choose stylesheet", QDir::homePath(), "*.qss");
    qDebug() << "Stylesheet: " << stylesheetFile;
    if(QFile::exists(stylesheetFile))
    {
        qDebug() << "New stylesheet added: " << stylesheetFile;
        db->updateTextPref("stylesheet", stylesheetFile);
        emit reloadStylesheet();
    }
}

void FSettingsDialog::on_pb_ResetStylesheet_clicked()
{
    db->updateTextPref("stylesheet", ":/stylesheet.qss");
    emit reloadStylesheet();
}

void FSettingsDialog::on_pb_ScanNow_clicked()
{
    FCrawler crawler;
    crawler.scanAllFolders();
    emit reloadLibrary();
}

void FSettingsDialog::on_btn_Artwork_DownloadAll_clicked() {
    if(QMessageBox::warning(this, "Please confirm!", "If Fusion is able to find Artwork, existing Artwork will be overwritten!",QMessageBox::Ok, QMessageBox::Cancel) ==QMessageBox::Cancel)
        return;

    runningDownloads = 0;
    totalDownloads = 0;

   gameList = db->getGameList();
   for(int i=0;i<gameList.length();++i) {
       FArtManager *artmanager = new FArtManager();
       connect(artmanager, SIGNAL(startedDownload()), this, SLOT(downloadStarted()));
       connect(artmanager, SIGNAL(finishedDownload()), this, SLOT(downloadFinished()));
       artmanager->getGameData(&gameList[i], "PC");
       artmanager->deleteLater();
   }

}

void FSettingsDialog::downloadFinished() {
    --runningDownloads;
    ui->la_Artwork_DownloadStatus->setText("Running Downloads:" + QString::number(runningDownloads));
    if(runningDownloads<=0)
        QMessageBox::information(this, "Downloads finished", "Finished " + QString::number(totalDownloads) + " download(s)");
}

void FSettingsDialog::downloadStarted() {
    ++runningDownloads;
    ++totalDownloads;
    ui->la_Artwork_DownloadStatus->setText("Running Downloads:" + QString::number(runningDownloads));
}

void FSettingsDialog::on_btn_Artwork_openCache_clicked() {
    QDesktopServices::openUrl(FGame::getCacheDir());

}

void FSettingsDialog::on_btn_Artwork_ClearCache_clicked() {
    QDir cacheDir(FGame::getCacheDir());

#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
    cacheDir.setNameFilters(QStringList()<<"*.*");
    QStringList steamFiles = cacheDir.entryList();
    for(int i=0;i<steamFiles.length();++i) {
        cacheDir.remove(steamFiles[i]);
    }
#else
   cacheDir.removeRecursively();
#endif

}

void FSettingsDialog::on_buttonBox_accepted()
{
   db->updateBoolPref("ScanLibsOnStartup", (bool)ui->cb_ScanLibOnStartup->checkState());

   //Artwortk-Page
   db->updateBoolPref("useArtworkCache", (bool)ui->cb_Artwork_UseCache->checkState());
}
<|endoftext|>


<commit_before>// Author: Roel Aaij 21/07/2007

/*************************************************************************
 * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGTable.h"
#include "TClass.h"
#include "TGWindow.h"
#include "TGResourcePool.h"
#include "Riostream.h"
#include "TSystem.h"
#include "TImage.h"
#include "TEnv.h"
#include "TGToolTip.h"
#include "TGPicture.h"
#include "TRandom3.h"
#include "TGSimpleTableInterface.h"
#include "TGSimpleTable.h"

ClassImp(TGSimpleTable)

//______________________________________________________________________________
/* Begin Html

TGSimpleTable

To provide a simple class to visualize an array of doubles, the class
TGSimpleTable is provided. TGSimpleTable creates it's own
TGSimpleTableInterface. For more information, see the documentation of
TGTable

The interface is accesible through the GetInterface() method.
End_Html
*/

//______________________________________________________________________________
TGSimpleTable::TGSimpleTable(TGWindow *p, Int_t id, Double_t **data,
                             UInt_t nrows, UInt_t ncolumns)
   : TGTable(p, id, 0, nrows, ncolumns)
{
   // TGSimpleTable constuctor.

   TGSimpleTableInterface *iface = new TGSimpleTableInterface(data, nrows,
                                                              ncolumns);
   SetInterface(iface,nrows, ncolumns);
}

//______________________________________________________________________________
TGSimpleTable::~TGSimpleTable()
{
   // TGSimpleTable destructor.

   delete fInterface;
}

<commit_msg>Fix a typo (thanks Axel)<commit_after>// Author: Roel Aaij 21/07/2007

/*************************************************************************
 * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGTable.h"
#include "TClass.h"
#include "TGWindow.h"
#include "TGResourcePool.h"
#include "Riostream.h"
#include "TSystem.h"
#include "TImage.h"
#include "TEnv.h"
#include "TGToolTip.h"
#include "TGPicture.h"
#include "TRandom3.h"
#include "TGSimpleTableInterface.h"
#include "TGSimpleTable.h"

ClassImp(TGSimpleTable)

//______________________________________________________________________________
/* Begin_Html

TGSimpleTable

To provide a simple class to visualize an array of doubles, the class
TGSimpleTable is provided. TGSimpleTable creates it's own
TGSimpleTableInterface. For more information, see the documentation of
TGTable

The interface is accesible through the GetInterface() method.
End_Html
*/

//______________________________________________________________________________
TGSimpleTable::TGSimpleTable(TGWindow *p, Int_t id, Double_t **data,
                             UInt_t nrows, UInt_t ncolumns)
   : TGTable(p, id, 0, nrows, ncolumns)
{
   // TGSimpleTable constuctor.

   TGSimpleTableInterface *iface = new TGSimpleTableInterface(data, nrows,
                                                              ncolumns);
   SetInterface(iface,nrows, ncolumns);
}

//______________________________________________________________________________
TGSimpleTable::~TGSimpleTable()
{
   // TGSimpleTable destructor.

   delete fInterface;
}

<|endoftext|>


<commit_before>#include "tabs/monitors-tab.h"
#include <QMenu>
#include <ui_monitors-tab.h>
#include <algorithm>
#include "logger.h"
#include "main-window.h"
#include "models/monitor.h"
#include "models/monitor-manager.h"
#include "models/profile.h"
#include "models/site.h"
#include "monitor-window.h"

MonitorsTab::MonitorsTab(Profile *profile, MonitorManager *monitorManager, MonitoringCenter *monitoringCenter, MainWindow *parent)
	: QWidget(parent), ui(new Ui::MonitorsTab), m_profile(profile), m_monitorManager(monitorManager), m_monitoringCenter(monitoringCenter), m_parent(parent)
{
	ui->setupUi(this);

	connect(m_monitoringCenter, &MonitoringCenter::statusChanged, this, &MonitorsTab::statusChanged);
	connect(m_monitorManager, &MonitorManager::changed, this, &MonitorsTab::refresh);
	refresh();
}

MonitorsTab::~MonitorsTab()
{
	close();
	delete ui;
}

void MonitorsTab::changeEvent(QEvent *event)
{
	// Automatically re-translate this tab on language change
	if (event->type() == QEvent::LanguageChange) {
		ui->retranslateUi(this);
	}

	QWidget::changeEvent(event);
}

QIcon &MonitorsTab::getIcon(const QString &path)
{
	if (!m_icons.contains(path)) {
		m_icons.insert(path, QIcon(path));
	}

	return m_icons[path];
}

void MonitorsTab::statusChanged(const Monitor &monitor, MonitoringCenter::MonitoringStatus status)
{
	int row = m_monitorManager->monitors().indexOf(monitor);
	if (row == -1) {
		return;
	}

	QString path;
	switch (status)
	{
		case MonitoringCenter::MonitoringStatus::Waiting: path = ":/images/status/pending.png"; break;
		case MonitoringCenter::MonitoringStatus::Checking: path = ":/images/status/downloading.png"; break;
		case MonitoringCenter::MonitoringStatus::Performing: path = ":/images/status/ok.png"; break;
		default: return;
	}

	ui->tableMonitors->item(row, 0)->setIcon(getIcon(path));
}

void MonitorsTab::refresh()
{
	const QList<Monitor> &monitors = m_monitorManager->monitors();

	ui->tableMonitors->setRowCount(monitors.count());

	for (int i = 0; i < monitors.count(); ++i) {
		const Monitor &monitor = monitors[i];

		ui->tableMonitors->setItem(i, 0, new QTableWidgetItem(getIcon(":/images/status/pending.png"), ""));
		ui->tableMonitors->setItem(i, 1, new QTableWidgetItem(monitor.query().toString()));
		ui->tableMonitors->setItem(i, 2, new QTableWidgetItem(monitor.site()->url()));
		ui->tableMonitors->setItem(i, 3, new QTableWidgetItem(QString::number(monitor.interval())));
		ui->tableMonitors->setItem(i, 4, new QTableWidgetItem(monitor.download() ? "Download" : ""));
	}
}

void MonitorsTab::monitorsTableContextMenu(const QPoint &pos)
{
	auto *item = ui->tableMonitors->itemAt(pos);
	if (item == nullptr) {
		return;
	}

	int row = item->row();
	const Monitor &monitor = m_monitorManager->monitors()[row];

	auto *menu = new QMenu(this);
	menu->addAction(tr("Edit"), [this, monitor]() { (new MonitorWindow(m_profile, monitor, this))->show(); });
	menu->addAction(tr("Remove"), [this, monitor]() { m_monitorManager->remove(monitor); });
	menu->exec(QCursor::pos());
}
<commit_msg>Add icons to monitor tab context menu<commit_after>#include "tabs/monitors-tab.h"
#include <QMenu>
#include <ui_monitors-tab.h>
#include <algorithm>
#include "logger.h"
#include "main-window.h"
#include "models/monitor.h"
#include "models/monitor-manager.h"
#include "models/profile.h"
#include "models/site.h"
#include "monitor-window.h"

MonitorsTab::MonitorsTab(Profile *profile, MonitorManager *monitorManager, MonitoringCenter *monitoringCenter, MainWindow *parent)
	: QWidget(parent), ui(new Ui::MonitorsTab), m_profile(profile), m_monitorManager(monitorManager), m_monitoringCenter(monitoringCenter), m_parent(parent)
{
	ui->setupUi(this);

	connect(m_monitoringCenter, &MonitoringCenter::statusChanged, this, &MonitorsTab::statusChanged);
	connect(m_monitorManager, &MonitorManager::changed, this, &MonitorsTab::refresh);
	refresh();
}

MonitorsTab::~MonitorsTab()
{
	close();
	delete ui;
}

void MonitorsTab::changeEvent(QEvent *event)
{
	// Automatically re-translate this tab on language change
	if (event->type() == QEvent::LanguageChange) {
		ui->retranslateUi(this);
	}

	QWidget::changeEvent(event);
}

QIcon &MonitorsTab::getIcon(const QString &path)
{
	if (!m_icons.contains(path)) {
		m_icons.insert(path, QIcon(path));
	}

	return m_icons[path];
}

void MonitorsTab::statusChanged(const Monitor &monitor, MonitoringCenter::MonitoringStatus status)
{
	int row = m_monitorManager->monitors().indexOf(monitor);
	if (row == -1) {
		return;
	}

	QString path;
	switch (status)
	{
		case MonitoringCenter::MonitoringStatus::Waiting: path = ":/images/status/pending.png"; break;
		case MonitoringCenter::MonitoringStatus::Checking: path = ":/images/status/downloading.png"; break;
		case MonitoringCenter::MonitoringStatus::Performing: path = ":/images/status/ok.png"; break;
		default: return;
	}

	ui->tableMonitors->item(row, 0)->setIcon(getIcon(path));
}

void MonitorsTab::refresh()
{
	const QList<Monitor> &monitors = m_monitorManager->monitors();

	ui->tableMonitors->setRowCount(monitors.count());

	for (int i = 0; i < monitors.count(); ++i) {
		const Monitor &monitor = monitors[i];

		ui->tableMonitors->setItem(i, 0, new QTableWidgetItem(getIcon(":/images/status/pending.png"), ""));
		ui->tableMonitors->setItem(i, 1, new QTableWidgetItem(monitor.query().toString()));
		ui->tableMonitors->setItem(i, 2, new QTableWidgetItem(monitor.site()->url()));
		ui->tableMonitors->setItem(i, 3, new QTableWidgetItem(QString::number(monitor.interval())));
		ui->tableMonitors->setItem(i, 4, new QTableWidgetItem(monitor.download() ? "Download" : ""));
	}
}

void MonitorsTab::monitorsTableContextMenu(const QPoint &pos)
{
	auto *item = ui->tableMonitors->itemAt(pos);
	if (item == nullptr) {
		return;
	}

	int row = item->row();
	const Monitor &monitor = m_monitorManager->monitors()[row];

	auto *menu = new QMenu(this);
	menu->addAction(getIcon(":/images/icons/edit.png"), tr("Edit"), [this, monitor]() { (new MonitorWindow(m_profile, monitor, this))->show(); });
	menu->addAction(getIcon(":/images/icons/remove.png"), tr("Remove"), [this, monitor]() { m_monitorManager->remove(monitor); });
	menu->exec(QCursor::pos());
}
<|endoftext|>


<commit_before>#include <unordered_map>
#include <memory>
#include <atomic>
#include <glog/logging.h>
#include <streams.h>
#include <util.h>
#include <scheduler.h>
#include <atom.h>

void call_rescue(e_t e, const children_t& children) {
  for (auto& s: children) {
    s(e);
  }
}

stream_t prn() {
  return [](e_t e) {
    LOG(INFO) << "prn() " <<  event_to_json(e);
  };
}

stream_t with(const with_changes_t & changes,
              const bool & replace,
              const children_t & children)
{
  return [=](e_t e) {
    Event ne(e);
    for (auto &kv: changes) {
      set_event_value(ne, kv.first, kv.second, replace);
    }
    call_rescue(ne, children);
  };
}

stream_t with(const with_changes_t& changes, const children_t& children) {
  return with(changes, true, children);
}

stream_t with_ifempty(
    const with_changes_t& changes,
    const children_t& children)
{
  return with(changes, false, children);
}

stream_t split(const split_clauses_t clauses,
               const stream_t& default_stream)
{
  return [=](e_t e) {
    for (auto const &pair: clauses) {
      if (pair.first(e)) {
        call_rescue(e, {pair.second});
        return;
      }
    }
    if (default_stream) {
      call_rescue(e, {default_stream});
    }
  };
}

typedef std::unordered_map<std::string, const children_t> by_streams_map_t;

stream_t by(const by_keys_t & keys, const by_streams_t & streams) {
  auto atom_streams = make_shared_atom<by_streams_map_t>();

  return [=](e_t e) mutable {
    if (keys.empty()) {
      return;
    }

    std::string key;
    for (const auto & k: keys) {
      key += string_to_value(e, k) + " ";
    }

    auto fn_value = [&]() {
      children_t children;
      for (auto & s: streams) {
        children.push_back(s());
      }
      return children;
    };

    map_on_sync_insert<std::string, const children_t>(
        atom_streams,
        key,
        fn_value,
        [&](const children_t & c) { call_rescue(e, c); }
    );
  };
}

stream_t where(const predicate_t& predicate, const children_t& children,
               const children_t& else_children)
{
  return [=](e_t e) {
    if (predicate(e)) {
      call_rescue(e, children);
    } else {
      call_rescue(e, else_children);
    }
  };
}

stream_t rate(const int interval, const children_t& children) {
  auto rate = std::make_shared<std::atomic<double>>(0);
  g_scheduler.add_periodic_task(
     [=]() mutable
      {
        VLOG(3) << "rate-timer()";
        Event e;
        e.set_metric_d(rate->exchange(0) / interval);
        e.set_time(g_scheduler.unix_time());
        VLOG(3) << "rate-timer() value: " << e.metric_d();
        call_rescue(e, children);
      },
      interval
  );

  return [=](e_t e) mutable {
    double expected, newval;
    do {
      expected = rate->load();
      newval = expected + metric_to_double(e);
    } while (!rate->compare_exchange_strong(expected, newval));
  };
}

stream_t changed_state(std::string initial, const children_t& children) {
  std::string last_state(initial);

  return [=](e_t e) mutable {
    VLOG(3) << "changed_state() last_state: "
            << last_state << " new state " << e.state();
    if (last_state != e.state()) {
      VLOG(3) << "changed_state() state change";
      last_state.assign(e.state());
      call_rescue(e, children);
    }
  };
}

stream_t tagged_any(const tags_t& tags, const children_t& children) {
  return [=](e_t e) {
    VLOG(3) << "tagged_any()";
    if (tagged_any_(e, tags)) {
      VLOG(3) << "tagged_any() match";
      call_rescue(e, children);
    }
  };
}

stream_t tagged_all(const tags_t& tags, const children_t& children) {
  return [=](e_t e) {
    VLOG(3) << "tagged_all()";
    if (tagged_all_(e, tags)) {
      VLOG(3) << "tagged_all() match";
      call_rescue(e, children);
    }
  };
}

bool tagged_any_(e_t e, const tags_t& tags) {
  for (auto &tag: tags) {
    if (tag_exists(e, tag)) {
      return true;
    }
  }
  return false;
}

bool tagged_all_(e_t e, const tags_t& tags) {
  for (auto &tag: tags) {
    if (!tag_exists(e, tag)) {
      return false;
    }
  }
  return true;
}

stream_t send_index(class index& idx) {
  return [&](e_t e) {
    if (e.state() != "expired") {
      idx.add_event(e);
    }
  };
}

void streams::add_stream(stream_t stream) {
  VLOG(3) << "adding stream";
  streams_.push_back(stream);
}

void streams::process_message(const Msg& message) {
  VLOG(3) << "process message. num of streams " << streams_.size();
  unsigned long int sec = time(0);
  for (int i = 0; i < message.events_size(); i++) {
    const Event &event = message.events(i);
    if (!event.has_time()) {
      Event nevent(event);
      nevent.set_time(sec);
      push_event(std::move(nevent));
    } else {
      push_event(event);
    }
  }
}

void streams::push_event(const Event& e) {
  for (auto& s: streams_) {
    s(e);
  }
}

<commit_msg>Make changed_state thread safe<commit_after>#include <unordered_map>
#include <memory>
#include <atomic>
#include <glog/logging.h>
#include <streams.h>
#include <util.h>
#include <scheduler.h>
#include <atom.h>

void call_rescue(e_t e, const children_t& children) {
  for (auto& s: children) {
    s(e);
  }
}

stream_t prn() {
  return [](e_t e) {
    LOG(INFO) << "prn() " <<  event_to_json(e);
  };
}

stream_t with(const with_changes_t & changes,
              const bool & replace,
              const children_t & children)
{
  return [=](e_t e) {
    Event ne(e);
    for (auto &kv: changes) {
      set_event_value(ne, kv.first, kv.second, replace);
    }
    call_rescue(ne, children);
  };
}

stream_t with(const with_changes_t& changes, const children_t& children) {
  return with(changes, true, children);
}

stream_t with_ifempty(
    const with_changes_t& changes,
    const children_t& children)
{
  return with(changes, false, children);
}

stream_t split(const split_clauses_t clauses,
               const stream_t& default_stream)
{
  return [=](e_t e) {
    for (auto const &pair: clauses) {
      if (pair.first(e)) {
        call_rescue(e, {pair.second});
        return;
      }
    }
    if (default_stream) {
      call_rescue(e, {default_stream});
    }
  };
}

typedef std::unordered_map<std::string, const children_t> by_streams_map_t;

stream_t by(const by_keys_t & keys, const by_streams_t & streams) {
  auto atom_streams = make_shared_atom<by_streams_map_t>();

  return [=](e_t e) mutable {
    if (keys.empty()) {
      return;
    }

    std::string key;
    for (const auto & k: keys) {
      key += string_to_value(e, k) + " ";
    }

    auto fn_value = [&]() {
      children_t children;
      for (auto & s: streams) {
        children.push_back(s());
      }
      return children;
    };

    map_on_sync_insert<std::string, const children_t>(
        atom_streams,
        key,
        fn_value,
        [&](const children_t & c) { call_rescue(e, c); }
    );
  };
}

stream_t where(const predicate_t& predicate, const children_t& children,
               const children_t& else_children)
{
  return [=](e_t e) {
    if (predicate(e)) {
      call_rescue(e, children);
    } else {
      call_rescue(e, else_children);
    }
  };
}

stream_t rate(const int interval, const children_t& children) {
  auto rate = std::make_shared<std::atomic<double>>(0);
  g_scheduler.add_periodic_task(
     [=]() mutable
      {
        VLOG(3) << "rate-timer()";
        Event e;
        e.set_metric_d(rate->exchange(0) / interval);
        e.set_time(g_scheduler.unix_time());
        VLOG(3) << "rate-timer() value: " << e.metric_d();
        call_rescue(e, children);
      },
      interval
  );

  return [=](e_t e) mutable {
    double expected, newval;
    do {
      expected = rate->load();
      newval = expected + metric_to_double(e);
    } while (!rate->compare_exchange_strong(expected, newval));
  };
}

stream_t changed_state(std::string initial, const children_t& children) {
  auto prev = make_shared_atom<std::string>(initial);

  return [=](e_t e) mutable {
    prev->update(
        e.state(),
        [&](const std::string & prev_state)
          {
            if (prev_state != e.state())
              call_rescue(e, children);
          }
    );
  };
}

stream_t tagged_any(const tags_t& tags, const children_t& children) {
  return [=](e_t e) {
    VLOG(3) << "tagged_any()";
    if (tagged_any_(e, tags)) {
      VLOG(3) << "tagged_any() match";
      call_rescue(e, children);
    }
  };
}

stream_t tagged_all(const tags_t& tags, const children_t& children) {
  return [=](e_t e) {
    VLOG(3) << "tagged_all()";
    if (tagged_all_(e, tags)) {
      VLOG(3) << "tagged_all() match";
      call_rescue(e, children);
    }
  };
}

bool tagged_any_(e_t e, const tags_t& tags) {
  for (auto &tag: tags) {
    if (tag_exists(e, tag)) {
      return true;
    }
  }
  return false;
}

bool tagged_all_(e_t e, const tags_t& tags) {
  for (auto &tag: tags) {
    if (!tag_exists(e, tag)) {
      return false;
    }
  }
  return true;
}

stream_t send_index(class index& idx) {
  return [&](e_t e) {
    if (e.state() != "expired") {
      idx.add_event(e);
    }
  };
}

void streams::add_stream(stream_t stream) {
  VLOG(3) << "adding stream";
  streams_.push_back(stream);
}

void streams::process_message(const Msg& message) {
  VLOG(3) << "process message. num of streams " << streams_.size();
  unsigned long int sec = time(0);
  for (int i = 0; i < message.events_size(); i++) {
    const Event &event = message.events(i);
    if (!event.has_time()) {
      Event nevent(event);
      nevent.set_time(sec);
      push_event(std::move(nevent));
    } else {
      push_event(event);
    }
  }
}

void streams::push_event(const Event& e) {
  for (auto& s: streams_) {
    s(e);
  }
}

<|endoftext|>