// 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 "net/base/force_tls_state.h"

#include "base/logging.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
#include "googleurl/src/gurl.h"
#include "net/base/registry_controlled_domain.h"

namespace net {

ForceTLSState::ForceTLSState() {
}

void ForceTLSState::DidReceiveHeader(const GURL& url,
                                     const std::string& value) {
  // TODO(abarth): Actually parse |value| once the spec settles down.
  EnableHost(url.host());
}

void ForceTLSState::EnableHost(const std::string& host) {
  // TODO(abarth): Canonicalize host.
  AutoLock lock(lock_);
  enabled_hosts_.insert(host);
}

bool ForceTLSState::IsEnabledForHost(const std::string& host) {
  // TODO(abarth): Canonicalize host.
  AutoLock lock(lock_);
  return enabled_hosts_.find(host) != enabled_hosts_.end();
}

// "X-Force-TLS" ":" "max-age" "=" delta-seconds *1INCLUDESUBDOMAINS
// INCLUDESUBDOMAINS = [ " includeSubDomains" ]
bool ForceTLSState::ParseHeader(const std::string& value,
                                int* max_age,
                                bool* include_subdomains) {
  DCHECK(max_age);
  DCHECK(include_subdomains);

  int max_age_candidate;

  enum ParserState {
    START,
    AFTER_MAX_AGE_LABEL,
    AFTER_MAX_AGE_EQUALS,
    AFTER_MAX_AGE,
    AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER,
    AFTER_INCLUDE_SUBDOMAINS,
  } state = START;

  StringTokenizer tokenizer(value, " =");
  tokenizer.set_options(StringTokenizer::RETURN_DELIMS);
  while (tokenizer.GetNext()) {
    DCHECK(!tokenizer.token_is_delim() || tokenizer.token().length() == 1);
    DCHECK(tokenizer.token_is_delim() || *tokenizer.token_begin() != ' ');
    switch (state) {
      case START:
        if (*tokenizer.token_begin() == ' ')
          continue;
        if (!LowerCaseEqualsASCII(tokenizer.token(), "max-age"))
          return false;
        state = AFTER_MAX_AGE_LABEL;
        break;

      case AFTER_MAX_AGE_LABEL:
        if (*tokenizer.token_begin() == ' ')
          continue;
        if (*tokenizer.token_begin() != '=')
          return false;
        DCHECK(tokenizer.token().length() ==  1);
        state = AFTER_MAX_AGE_EQUALS;
        break;

      case AFTER_MAX_AGE_EQUALS:
        if (*tokenizer.token_begin() == ' ')
          continue;
        if (!StringToInt(tokenizer.token(), &max_age_candidate))
          return false;
        if (max_age_candidate < 0)
          return false;
        state = AFTER_MAX_AGE;
        break;

      case AFTER_MAX_AGE:
        if (*tokenizer.token_begin() != ' ')
          return false;
        state = AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER;
        break;

      case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
        if (*tokenizer.token_begin() == ' ')
          continue;
        if (!LowerCaseEqualsASCII(tokenizer.token(), "includesubdomains"))
          return false;
        state = AFTER_INCLUDE_SUBDOMAINS;
        break;

      case AFTER_INCLUDE_SUBDOMAINS:
        if (*tokenizer.token_begin() != ' ')
          return false;
        break;

      default:
        NOTREACHED();
    }
  }

  // We've consumed all the input.  Let's see what state we ended up in.
  switch (state) {
    case START:
    case AFTER_MAX_AGE_LABEL:
    case AFTER_MAX_AGE_EQUALS:
      return false;
    case AFTER_MAX_AGE:
    case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER:
      *max_age = max_age_candidate;
      *include_subdomains = false;
      return true;
    case AFTER_INCLUDE_SUBDOMAINS:
      *max_age = max_age_candidate;
      *include_subdomains = true;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

}  // namespace