/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "pagespeed/apache/apache_config.h"

#include "base/logging.h"
#include "net/instaweb/public/version.h"
#include "pagespeed/kernel/base/thread_system.h"

namespace net_instaweb {

namespace {

const char kModPagespeedStatisticsHandlerPath[] = "/mod_pagespeed_statistics";
const char kProxyAuth[] = "ProxyAuth";
const char kForceBuffering[] = "ForceBuffering";
const char kProxyAllRequests[] = "ExperimentalProxyAllRequests";
const char kMeasurementProxy[] = "ExperimentalMeasurementProxy";

}  // namespace

RewriteOptions::Properties* ApacheConfig::apache_properties_ = nullptr;

void ApacheConfig::Initialize() {
  if (Properties::Initialize(&apache_properties_)) {
    SystemRewriteOptions::Initialize();
    AddProperties();
  }
}

void ApacheConfig::Terminate() {
  if (Properties::Terminate(&apache_properties_)) {
    SystemRewriteOptions::Terminate();
  }
}

ApacheConfig::ApacheConfig(const StringPiece& description,
                           ThreadSystem* thread_system)
    : SystemRewriteOptions(description, thread_system) {
  Init();
}

ApacheConfig::ApacheConfig(ThreadSystem* thread_system)
    : SystemRewriteOptions(thread_system) {
  Init();
}

ApacheConfig::~ApacheConfig() {
}

void ApacheConfig::Init() {
  DCHECK(apache_properties_ != NULL)
      << "Call ApacheConfig::Initialize() before construction";
  InitializeOptions(apache_properties_);
}

void ApacheConfig::AddProperties() {
  AddApacheProperty(
      "", &ApacheConfig::proxy_auth_, "prxa",
      kProxyAuth,
      "CookieName[=Value][:RedirectUrl] -- checks proxy requests for "
      "CookieName.  If CookieValue is specified, checks for that.  If "
      "Redirect is specified, a failure results in a redirection to that URL "
      "otherwise a 403 is generated.",
      false /* safe_to_print */);

  AddApacheProperty(
      false, &ApacheConfig::force_buffering_, "afb",
      kForceBuffering,
      "Force buffering of non-html fetch responses rather than streaming",
      true /* safe_to_print */);

  AddApacheProperty(
      false, &ApacheConfig::proxy_all_requests_mode_, "aparx",
      kProxyAllRequests,
      "Experimental mode where mod_pagespeed acts entirely as a proxy, and "
      "doesn't attempt to work with any local serving. ",
      false /* safe_to_print*/);

  // Register deprecated options.
  AddDeprecatedProperty("CollectRefererStatistics",
                        RewriteOptions::kDirectoryScope);
  AddDeprecatedProperty("HashRefererStatistics",
                        RewriteOptions::kDirectoryScope);
  AddDeprecatedProperty("RefererStatisticsOutputLevel",
                        RewriteOptions::kDirectoryScope);
  AddDeprecatedProperty("StatisticsLoggingFile",
                        RewriteOptions::kDirectoryScope);
  AddDeprecatedProperty("DisableForBots",
                        RewriteOptions::kDirectoryScope);
  AddDeprecatedProperty("GeneratedFilePrefix",
                        RewriteOptions::kServerScope);
  AddDeprecatedProperty("InheritVHostConfig",
                        RewriteOptions::kServerScope);
  AddDeprecatedProperty("FetchFromModSpdy",
                        RewriteOptions::kServerScope);
  AddDeprecatedProperty("NumShards", RewriteOptions::kServerScope);
  AddDeprecatedProperty("UrlPrefix", RewriteOptions::kServerScope);

  MergeSubclassProperties(apache_properties_);

  // Default properties are global but to set them the current API requires
  // an ApacheConfig instance and we're in a static method.
  //
  // TODO(jmarantz): Perform these operations on the Properties directly and
  // get rid of this hack.
  //
  // Instantiation of the options with a null thread system wouldn't usually be
  // safe but it's ok here because we're only updating the static properties on
  // process startup.  We won't have a thread-system yet or multiple threads.
  ApacheConfig config("dummy_options", NULL);
  config.set_default_x_header_value(kModPagespeedVersion);
}

ApacheConfig* ApacheConfig::Clone() const {
  ApacheConfig* options =
      new ApacheConfig(StrCat("cloned from ", description()), thread_system());
  options->Merge(*this);
  return options;
}

ApacheConfig* ApacheConfig::NewOptions() const {
  return new ApacheConfig(StrCat("derived from ", description()),
                          thread_system());
}

const ApacheConfig* ApacheConfig::DynamicCast(const RewriteOptions* instance) {
  const ApacheConfig* config = dynamic_cast<const ApacheConfig*>(instance);
  DCHECK(config != NULL);
  return config;
}

ApacheConfig* ApacheConfig::DynamicCast(RewriteOptions* instance) {
  ApacheConfig* config = dynamic_cast<ApacheConfig*>(instance);
  DCHECK(config != NULL);
  return config;
}

void ApacheConfig::Merge(const RewriteOptions& src) {
  SystemRewriteOptions::Merge(src);
  const ApacheConfig* asrc = DynamicCast(&src);
  CHECK(asrc != NULL);

  // Can't use Merge() since we don't have names here.
  measurement_proxy_root_.MergeHelper(&asrc->measurement_proxy_root_);
  measurement_proxy_password_.MergeHelper(&asrc->measurement_proxy_password_);
}

RewriteOptions::OptionSettingResult ApacheConfig::ParseAndSetOptionFromName2(
    StringPiece name, StringPiece arg1, StringPiece arg2,
    GoogleString* msg, MessageHandler* handler) {
  OptionSettingResult result = SystemRewriteOptions::ParseAndSetOptionFromName2(
      name, arg1, arg2, msg, handler);
  if (result == RewriteOptions::kOptionNameUnknown) {
    if (name == kMeasurementProxy) {
      arg1.CopyToString(&measurement_proxy_root_.mutable_value());
      arg2.CopyToString(&measurement_proxy_password_.mutable_value());
      result = RewriteOptions::kOptionOk;
    }
  }
  return result;
}

GoogleString ApacheConfig::SubclassSignatureLockHeld() {
  return StrCat(SystemRewriteOptions::SubclassSignatureLockHeld(),
                "_MPR:", measurement_proxy_root_.value(),
                "_MPP:", measurement_proxy_password_.value());
}

bool ApacheConfig::GetProxyAuth(StringPiece* name, StringPiece* value,
                                StringPiece* redirect) const {
  StringPiece auth = proxy_auth_.value();
  TrimWhitespace(&auth);
  if (auth.empty()) {
    return false;
  }

  // Strip the redirect off the tail if a colon is present.  Note that
  // a colon may exist in the redirect URL but we search from the beginning
  // so it's no problem.
  stringpiece_ssize_type colon = auth.find(':');
  if (colon == StringPiece::npos) {
    *redirect = StringPiece();
  } else {
    *redirect = auth.substr(colon + 1);
    auth = auth.substr(0, colon);
  }

  // Split into name/value if an equals is present.
  stringpiece_ssize_type equals = auth.find('=');
  if (equals == StringPiece::npos) {
    *name = auth;
    *value = StringPiece();
  } else {
    *name = auth.substr(0, equals);
    *value = auth.substr(equals + 1);
  }
  return true;
}

}  // namespace net_instaweb
