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

#include "chrome/browser/privacy_blacklist/blacklist.h"

#include <algorithm>
#include <string>

#include "base/file_path.h"
#include "base/file_util.h"
#include "chrome/browser/privacy_blacklist/blacklist_store.h"
#include "net/http/http_util.h"

#define STRINGIZE(s) #s

namespace {

bool matches(const std::string& pattern, const std::string& url) {
  return url.find(pattern) != std::string::npos;
}

const char* const cookie_headers[2] = { "cookie", "set-cookie" };

}  // namespace

// Value is not important, here just that the object has an address.
const void* const Blacklist::kRequestDataKey = 0;

unsigned int Blacklist::String2Attribute(const std::string& s) {
  if (s == STRINGIZE(kBlockAll))
    return kBlockAll;
  else if (s == STRINGIZE(kDontSendCookies))
    return kDontSendCookies;
  else if (s == STRINGIZE(kDontStoreCookies))
    return kDontStoreCookies;
  else if (s == STRINGIZE(kDontPersistCookies))
    return kDontPersistCookies;
  else if (s == STRINGIZE(kDontSendReferrer))
    return kDontSendReferrer;
  else if (s == STRINGIZE(kDontSendUserAgent))
    return kDontSendUserAgent;
  else if (s == STRINGIZE(kBlockByType))
    return kBlockByType;
  else if (s == STRINGIZE(kBlockUnsecure))
    return kBlockUnsecure;
  return 0;
}

bool Blacklist::Entry::MatchType(const std::string& type) const {
  return std::find(types_.begin(), types_.end(), type) != types_.end();
}

bool Blacklist::Entry::IsBlocked(const GURL& url) const {
  return (attributes_ & kBlockAll) ||
    ((attributes_ & kBlockUnsecure) && !url.SchemeIsSecure());
}

Blacklist::Entry::Entry(const std::string& pattern, const Provider* provider)
    : pattern_(pattern), attributes_(0), provider_(provider) {}

void Blacklist::Entry::AddAttributes(unsigned int attributes) {
  attributes_ |= attributes;
}

void Blacklist::Entry::AddType(const std::string& type) {
  types_.push_back(type);
}

void Blacklist::Entry::Merge(const Entry& entry) {
  attributes_ |= entry.attributes_;

  std::copy(entry.types_.begin(), entry.types_.end(),
            std::back_inserter(types_));
}

void Blacklist::Entry::SwapTypes(std::vector<std::string>* types) {
  if (types && types->size()) {
    types->swap(types_);
  }
}

Blacklist::Blacklist(const FilePath& file) {
  // No blacklist, nothing to load.
  if (file.value().empty())
    return;

  BlacklistStoreInput input(file_util::OpenFile(file, "rb"));

  // Read the providers
  std::size_t n = input.ReadNumProviders();
  providers_.reserve(n);
  std::string name;
  std::string url;
  for (std::size_t i = 0; i < n; ++i) {
    input.ReadProvider(&name, &url);
    providers_.push_back(new Provider(name.c_str(), url.c_str()));
  }

  // Read the entries
  n = input.ReadNumEntries();
  std::string pattern;
  unsigned int attributes, provider;
  std::vector<std::string> types;
  for (unsigned int i = 0; i < n; ++i) {
    input.ReadEntry(&pattern, &attributes, &types, &provider);
    Entry* entry = new Entry(pattern, providers_[provider]);
    entry->AddAttributes(attributes);
    entry->SwapTypes(&types);
    blacklist_.push_back(entry);
  }
}

Blacklist::~Blacklist() {
  for (std::vector<Entry*>::iterator i = blacklist_.begin();
       i != blacklist_.end(); ++i)
    delete *i;
  for (std::vector<Provider*>::iterator i = providers_.begin();
       i != providers_.end(); ++i)
    delete *i;
}

// Returns a pointer to the Blacklist-owned entry which matches the given
// URL. If no matching Entry is found, returns null.
const Blacklist::Entry* Blacklist::findMatch(const GURL& url) const {
  for (std::vector<Entry*>::const_iterator i = blacklist_.begin();
       i != blacklist_.end(); ++i)
    if (matches((*i)->pattern(), url.spec()))
      return *i;
  return 0;
}

std::string Blacklist::StripCookies(const std::string& header) {
  return net::HttpUtil::StripHeaders(header, cookie_headers, 2);
}

std::string Blacklist::StripCookieExpiry(const std::string& cookie) {
  std::string::size_type start = cookie.find("; expires=");
  if (start != std::string::npos) {
    std::string::size_type finish = cookie.find(";", start+1);
    std::string session_cookie(cookie, 0, start);
    if (finish != std::string::npos)
      session_cookie.append(cookie.substr(finish));
    return session_cookie;
  }
  return cookie;
}