<commit_before>/* OpenHoW
 * Copyright (C) 2017-2019 Mark Sowden <markelswo@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "../engine.h"
#include "../game/GameManager.h"

#include "map_config_editor.h"

MapConfigEditor::MapConfigEditor() {
  map_ = GameManager::GetInstance()->GetCurrentMap();
  if (map_ == nullptr) {
    throw std::runtime_error("Attempted to create config editor without a valid map loaded!\n");
  }

  manifest_ = map_->GetManifest();
  backup_ = *manifest_;

  strncpy(name_buffer, manifest_->name.c_str(), sizeof(name_buffer));
  strncpy(author_buffer, manifest_->author.c_str(), sizeof(author_buffer));
}

MapConfigEditor::~MapConfigEditor() {
  RestoreManifest();
}

void MapConfigEditor::DisplayTemperature() {
  enum {
    TEMP_NORMAL, TEMP_HOT, TEMP_COLD, MAX_TEMP
  };
  static const char *temperatures[MAX_TEMP] = {"Normal", "Hot", "Cold"};
  static int temperature_index = -1;
  if (temperature_index == -1) {
    if (pl_strcasecmp(manifest_->temperature.c_str(), "hot") == 0) {
      temperature_index = TEMP_HOT;
    } else if (pl_strcasecmp(manifest_->temperature.c_str(), "cold") == 0) {
      temperature_index = TEMP_COLD;
    } else {
      temperature_index = TEMP_NORMAL;
    }
  }

  if (ImGui::BeginCombo("Temperature", temperatures[temperature_index])) {
    for (int i = 0; i < MAX_TEMP; ++i) {
      if (ImGui::Selectable(temperatures[i], (temperature_index == i))) {
        ImGui::SetItemDefaultFocus();
        temperature_index = i;
        manifest_->temperature = u_stringtolower(temperatures[temperature_index]);
      }
    }

    ImGui::EndCombo();
  }
}

void MapConfigEditor::DisplayWeather() {
  enum {
    WEATHER_CLEAR, WEATHER_RAIN, WEATHER_SNOW, MAX_WEATHER
  };
  static const char *weather_labels[MAX_WEATHER] = {"Clear", "Rain", "Snow"};
  static int weather_index = -1;
  if (weather_index == -1) {
    if (pl_strcasecmp(manifest_->weather.c_str(), "rain") == 0) {
      weather_index = WEATHER_RAIN;
    } else if (pl_strcasecmp(manifest_->weather.c_str(), "snow") == 0) {
      weather_index = WEATHER_SNOW;
    } else {
      weather_index = WEATHER_CLEAR;
    }
  }

  if (ImGui::BeginCombo("Weather", weather_labels[weather_index])) {
    for (int i = 0; i < MAX_WEATHER; ++i) {
      if (ImGui::Selectable(weather_labels[i], (weather_index == i))) {
        ImGui::SetItemDefaultFocus();
        weather_index = i;
        manifest_->weather = u_stringtolower(weather_labels[weather_index]);
      }
    }

    ImGui::EndCombo();
  }
}

void MapConfigEditor::DisplayTime() {
  enum {
    TIME_DAY, TIME_NIGHT, MAX_TIME
  };
  static const char *times[MAX_TIME] = {"Day", "Night"};
  static int time_index = -1;
  if (time_index == -1) {
    if (pl_strcasecmp(manifest_->time.c_str(), "day") == 0) {
      time_index = TIME_DAY;
    } else {
      time_index = TIME_NIGHT;
    }
  }

  if (ImGui::BeginCombo("Time", times[time_index])) {
    for (int i = 0; i < MAX_TIME; ++i) {
      if (ImGui::Selectable(times[i], (time_index == i))) {
        ImGui::SetItemDefaultFocus();
        time_index = i;
        manifest_->time = u_stringtolower(times[time_index]);
      }
    }

    ImGui::EndCombo();
  }
}

void MapConfigEditor::Display() {
  ImGui::SetNextWindowSize(ImVec2(310, 512), ImGuiCond_Once);
  ImGui::Begin(dname("Map Config Editor"), &status_, ED_DEFAULT_WINDOW_FLAGS);
  ImGui::InputText("Name", name_buffer, sizeof(name_buffer));
  ImGui::InputText("Author", author_buffer, sizeof(author_buffer));

  // todo: mode selection - need to query wherever this ends up being implemented...

  ImGui::Separator();

  DisplayTemperature();
  DisplayWeather();
  DisplayTime();

  ImGui::Separator();

  ImGui::Text("Sky Settings");

  float rgb[3];
  rgb[0] = plByteToFloat(manifest_->sky_colour_top.r);
  rgb[1] = plByteToFloat(manifest_->sky_colour_top.g);
  rgb[2] = plByteToFloat(manifest_->sky_colour_top.b);
  if (ImGui::ColorEdit3("Top Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->sky_colour_top.r = plFloatToByte(rgb[0]);
    manifest_->sky_colour_top.g = plFloatToByte(rgb[1]);
    manifest_->sky_colour_top.b = plFloatToByte(rgb[2]);

    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  rgb[0] = plByteToFloat(manifest_->sky_colour_bottom.r);
  rgb[1] = plByteToFloat(manifest_->sky_colour_bottom.g);
  rgb[2] = plByteToFloat(manifest_->sky_colour_bottom.b);
  if (ImGui::ColorEdit3("Bottom Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->sky_colour_bottom.r = plFloatToByte(rgb[0]);
    manifest_->sky_colour_bottom.g = plFloatToByte(rgb[1]);
    manifest_->sky_colour_bottom.b = plFloatToByte(rgb[2]);

    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  ImGui::Separator();

  ImGui::Text("Lighting Settings");

  ImGui::SliderAngle("Sun Pitch", &manifest_->sun_pitch, 0, 90, nullptr);
  ImGui::SliderAngle("Sun Yaw", &manifest_->sun_yaw, 0, 360, nullptr);

  rgb[0] = plByteToFloat(manifest_->sun_colour.r);
  rgb[1] = plByteToFloat(manifest_->sun_colour.g);
  rgb[2] = plByteToFloat(manifest_->sun_colour.b);
  if (ImGui::ColorEdit3("Sun Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->sun_colour.r = plFloatToByte(rgb[0]);
    manifest_->sun_colour.g = plFloatToByte(rgb[1]);
    manifest_->sun_colour.b = plFloatToByte(rgb[2]);
  }

  rgb[0] = plByteToFloat(manifest_->ambient_colour.r);
  rgb[1] = plByteToFloat(manifest_->ambient_colour.g);
  rgb[2] = plByteToFloat(manifest_->ambient_colour.b);
  if (ImGui::ColorEdit3("Ambient Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->ambient_colour.r = plFloatToByte(rgb[0]);
    manifest_->ambient_colour.g = plFloatToByte(rgb[1]);
    manifest_->ambient_colour.b = plFloatToByte(rgb[2]);

    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  ImGui::Separator();

  // Fog
  {
    ImGui::Text("Fog Settings");

    rgb[0] = plByteToFloat(manifest_->fog_colour.r);
    rgb[1] = plByteToFloat(manifest_->fog_colour.g);
    rgb[2] = plByteToFloat(manifest_->fog_colour.b);
    if (ImGui::ColorEdit3("Fog Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
      manifest_->fog_colour.r = plFloatToByte(rgb[0]);
      manifest_->fog_colour.g = plFloatToByte(rgb[1]);
      manifest_->fog_colour.b = plFloatToByte(rgb[2]);

      map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
    }

    if (ImGui::SliderFloat("Fog Intensity", &manifest_->fog_intensity, -100.0f, 100.0f, "%.0f")) {
      map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
    }

    if (ImGui::SliderFloat("Fog Distance", &manifest_->fog_distance, 0, 300.0f, "%.0f")) {
      map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
    }
  }

  ImGui::Separator();

  if (ImGui::Button("Save")) {
    std::string dir_name = map_->GetId();
    SaveManifest(std::string(u_get_full_path()) + "/maps/" + dir_name + ".map");
  }

  ImGui::SameLine();

  if (ImGui::Button("Cancel")) {
    RestoreManifest();

    // Close the window
    SetStatus(false);
  }

  ImGui::End();
}

void MapConfigEditor::SaveManifest(const std::string &path) {
  std::ofstream output(path);
  if (!output.is_open()) {
    LogWarn("Failed to write to \"%s\", aborting!n\"\n", filename_buffer);
    return;
  }

  output << "{";

  manifest_->name = name_buffer;
  output << R"("name":")" + manifest_->name + "\",";

  manifest_->name = author_buffer;
  output << R"("author":")" + manifest_->author + "\",";

  output << R"("description":")" + manifest_->description + "\",";
  if (!manifest_->modes.empty()) {
    output << R"("modes":[)";
    for (size_t i = 0; i < manifest_->modes.size(); ++i) {
      output << "\"" + manifest_->modes[i] + "\"";
      if (i != manifest_->modes.size() - 1) {
        output << ",";
      }
    }
    output << "],";
  }

  output << R"("ambientColour":")" +
      std::to_string(manifest_->ambient_colour.r) + " " +
      std::to_string(manifest_->ambient_colour.g) + " " +
      std::to_string(manifest_->ambient_colour.b) + "\",";
  output << R"("skyColourTop":")" +
      std::to_string(manifest_->sky_colour_top.r) + " " +
      std::to_string(manifest_->sky_colour_top.g) + " " +
      std::to_string(manifest_->sky_colour_top.b) + "\",";
  output << R"("skyColourBottom":")" +
      std::to_string(manifest_->sky_colour_bottom.r) + " " +
      std::to_string(manifest_->sky_colour_bottom.g) + " " +
      std::to_string(manifest_->sky_colour_bottom.b) + "\",";
  output << R"("sunColour":")" +
      std::to_string(manifest_->sun_colour.r) + " " +
      std::to_string(manifest_->sun_colour.g) + " " +
      std::to_string(manifest_->sun_colour.b) + "\",";
  output << R"("sunYaw":")" + std::to_string(manifest_->sun_yaw) + "\",";
  output << R"("sunPitch":")" + std::to_string(manifest_->sun_pitch) + "\",";
  output << R"("temperature":")" + manifest_->temperature + "\",";
  output << R"("weather":")" + manifest_->weather + "\",";
  output << R"("time":")" + manifest_->time + "\",";

  // Fog
  output << R"("fogColour":")" +
      std::to_string(manifest_->fog_colour.r) + " " +
      std::to_string(manifest_->fog_colour.g) + " " +
      std::to_string(manifest_->fog_colour.b) + "\",";
  output << R"("fogIntensity":")" + std::to_string(manifest_->fog_intensity) + "\",";
  output << R"("fogDistance":")" + std::to_string(manifest_->fog_distance) + "\"";

  output << "}\n";

  LogInfo("Wrote \"%s\"!\n", path.c_str());
  backup_ = *manifest_;
}

void MapConfigEditor::RestoreManifest() {
  *manifest_ = backup_;
  map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
}
<commit_msg>reintroduced changes to map config editor<commit_after>/* OpenHoW
 * Copyright (C) 2017-2019 Mark Sowden <markelswo@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "../engine.h"
#include "../game/GameManager.h"

#include "map_config_editor.h"

MapConfigEditor::MapConfigEditor() {
  map_ = GameManager::GetInstance()->GetCurrentMap();
  if (map_ == nullptr) {
    throw std::runtime_error("Attempted to create config editor without a valid map loaded!\n");
  }

  manifest_ = map_->GetManifest();
  backup_ = *manifest_;

  strncpy(name_buffer, manifest_->name.c_str(), sizeof(name_buffer));
  strncpy(author_buffer, manifest_->author.c_str(), sizeof(author_buffer));
}

MapConfigEditor::~MapConfigEditor() {
  RestoreManifest();
}

void MapConfigEditor::DisplayTemperature() {
  enum {
    TEMP_NORMAL, TEMP_HOT, TEMP_COLD, MAX_TEMP
  };
  static const char *temperatures[MAX_TEMP] = {"Normal", "Hot", "Cold"};
  static int temperature_index = -1;
  if (temperature_index == -1) {
    if (pl_strcasecmp(manifest_->temperature.c_str(), "hot") == 0) {
      temperature_index = TEMP_HOT;
    } else if (pl_strcasecmp(manifest_->temperature.c_str(), "cold") == 0) {
      temperature_index = TEMP_COLD;
    } else {
      temperature_index = TEMP_NORMAL;
    }
  }

  if (ImGui::BeginCombo("Temperature", temperatures[temperature_index])) {
    for (int i = 0; i < MAX_TEMP; ++i) {
      if (ImGui::Selectable(temperatures[i], (temperature_index == i))) {
        ImGui::SetItemDefaultFocus();
        temperature_index = i;
        manifest_->temperature = u_stringtolower(temperatures[temperature_index]);
      }
    }

    ImGui::EndCombo();
  }
}

void MapConfigEditor::DisplayWeather() {
  enum {
    WEATHER_CLEAR, WEATHER_RAIN, WEATHER_SNOW, MAX_WEATHER
  };
  static const char *weather_labels[MAX_WEATHER] = {"Clear", "Rain", "Snow"};
  static int weather_index = -1;
  if (weather_index == -1) {
    if (pl_strcasecmp(manifest_->weather.c_str(), "rain") == 0) {
      weather_index = WEATHER_RAIN;
    } else if (pl_strcasecmp(manifest_->weather.c_str(), "snow") == 0) {
      weather_index = WEATHER_SNOW;
    } else {
      weather_index = WEATHER_CLEAR;
    }
  }

  if (ImGui::BeginCombo("Weather", weather_labels[weather_index])) {
    for (int i = 0; i < MAX_WEATHER; ++i) {
      if (ImGui::Selectable(weather_labels[i], (weather_index == i))) {
        ImGui::SetItemDefaultFocus();
        weather_index = i;
        manifest_->weather = u_stringtolower(weather_labels[weather_index]);
      }
    }

    ImGui::EndCombo();
  }
}

void MapConfigEditor::DisplayTime() {
  enum {
    TIME_DAY, TIME_NIGHT, MAX_TIME
  };
  static const char *times[MAX_TIME] = {"Day", "Night"};
  static int time_index = -1;
  if (time_index == -1) {
    if (pl_strcasecmp(manifest_->time.c_str(), "day") == 0) {
      time_index = TIME_DAY;
    } else {
      time_index = TIME_NIGHT;
    }
  }

  if (ImGui::BeginCombo("Time", times[time_index])) {
    for (int i = 0; i < MAX_TIME; ++i) {
      if (ImGui::Selectable(times[i], (time_index == i))) {
        ImGui::SetItemDefaultFocus();
        time_index = i;
        manifest_->time = u_stringtolower(times[time_index]);
      }
    }

    ImGui::EndCombo();
  }
}

void MapConfigEditor::Display() {
  ImGui::SetNextWindowSize(ImVec2(310, 512), ImGuiCond_Once);
  ImGui::Begin(dname("Map Config Editor"), &status_, ED_DEFAULT_WINDOW_FLAGS);
  ImGui::InputText("Name", name_buffer, sizeof(name_buffer));
  ImGui::InputText("Author", author_buffer, sizeof(author_buffer));

  // todo: mode selection - need to query wherever this ends up being implemented...

  ImGui::Separator();

  DisplayTemperature();
  DisplayWeather();
  DisplayTime();

  ImGui::Separator();

  ImGui::Text("Sky Settings");

  float rgb[3];
  rgb[0] = plByteToFloat(manifest_->sky_colour_top.r);
  rgb[1] = plByteToFloat(manifest_->sky_colour_top.g);
  rgb[2] = plByteToFloat(manifest_->sky_colour_top.b);
  if (ImGui::ColorEdit3("Top Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->sky_colour_top.r = plFloatToByte(rgb[0]);
    manifest_->sky_colour_top.g = plFloatToByte(rgb[1]);
    manifest_->sky_colour_top.b = plFloatToByte(rgb[2]);
    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  rgb[0] = plByteToFloat(manifest_->sky_colour_bottom.r);
  rgb[1] = plByteToFloat(manifest_->sky_colour_bottom.g);
  rgb[2] = plByteToFloat(manifest_->sky_colour_bottom.b);
  if (ImGui::ColorEdit3("Bottom Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->sky_colour_bottom.r = plFloatToByte(rgb[0]);
    manifest_->sky_colour_bottom.g = plFloatToByte(rgb[1]);
    manifest_->sky_colour_bottom.b = plFloatToByte(rgb[2]);
    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  ImGui::Separator();

  ImGui::Text("Lighting Settings");

  if(ImGui::SliderAngle("Sun Pitch", &manifest_->sun_pitch, 0, 90, nullptr)) {
    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }
  if(ImGui::SliderAngle("Sun Yaw", &manifest_->sun_yaw, 0, 360, nullptr)) {
    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  rgb[0] = plByteToFloat(manifest_->sun_colour.r);
  rgb[1] = plByteToFloat(manifest_->sun_colour.g);
  rgb[2] = plByteToFloat(manifest_->sun_colour.b);
  if (ImGui::ColorEdit3("Sun Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->sun_colour.r = plFloatToByte(rgb[0]);
    manifest_->sun_colour.g = plFloatToByte(rgb[1]);
    manifest_->sun_colour.b = plFloatToByte(rgb[2]);
    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  rgb[0] = plByteToFloat(manifest_->ambient_colour.r);
  rgb[1] = plByteToFloat(manifest_->ambient_colour.g);
  rgb[2] = plByteToFloat(manifest_->ambient_colour.b);
  if (ImGui::ColorEdit3("Ambient Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
    manifest_->ambient_colour.r = plFloatToByte(rgb[0]);
    manifest_->ambient_colour.g = plFloatToByte(rgb[1]);
    manifest_->ambient_colour.b = plFloatToByte(rgb[2]);
    map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
  }

  ImGui::Separator();

  // Fog
  {
    ImGui::Text("Fog Settings");

    rgb[0] = plByteToFloat(manifest_->fog_colour.r);
    rgb[1] = plByteToFloat(manifest_->fog_colour.g);
    rgb[2] = plByteToFloat(manifest_->fog_colour.b);
    if (ImGui::ColorEdit3("Fog Colour", rgb, ImGuiColorEditFlags_InputRGB)) {
      manifest_->fog_colour.r = plFloatToByte(rgb[0]);
      manifest_->fog_colour.g = plFloatToByte(rgb[1]);
      manifest_->fog_colour.b = plFloatToByte(rgb[2]);

      map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
    }

    if (ImGui::SliderFloat("Fog Intensity", &manifest_->fog_intensity, -100.0f, 100.0f, "%.0f")) {
      map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
    }

    if (ImGui::SliderFloat("Fog Distance", &manifest_->fog_distance, 0, 300.0f, "%.0f")) {
      map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
    }
  }

  ImGui::Separator();

  if (ImGui::Button("Save")) {
    std::string dir_name = map_->GetId();
    SaveManifest(std::string(u_get_full_path()) + "/maps/" + dir_name + ".map");
  }

  ImGui::SameLine();

  if (ImGui::Button("Cancel")) {
    RestoreManifest();

    // Close the window
    SetStatus(false);
  }

  ImGui::End();
}

void MapConfigEditor::SaveManifest(const std::string &path) {
  std::ofstream output(path);
  if (!output.is_open()) {
    LogWarn("Failed to write to \"%s\", aborting!n\"\n", filename_buffer);
    return;
  }

  output << "{";

  manifest_->name = name_buffer;
  output << R"("name":")" + manifest_->name + "\",";

  manifest_->name = author_buffer;
  output << R"("author":")" + manifest_->author + "\",";

  output << R"("description":")" + manifest_->description + "\",";
  if (!manifest_->modes.empty()) {
    output << R"("modes":[)";
    for (size_t i = 0; i < manifest_->modes.size(); ++i) {
      output << "\"" + manifest_->modes[i] + "\"";
      if (i != manifest_->modes.size() - 1) {
        output << ",";
      }
    }
    output << "],";
  }

  output << R"("ambientColour":")" +
      std::to_string(manifest_->ambient_colour.r) + " " +
      std::to_string(manifest_->ambient_colour.g) + " " +
      std::to_string(manifest_->ambient_colour.b) + "\",";
  output << R"("skyColourTop":")" +
      std::to_string(manifest_->sky_colour_top.r) + " " +
      std::to_string(manifest_->sky_colour_top.g) + " " +
      std::to_string(manifest_->sky_colour_top.b) + "\",";
  output << R"("skyColourBottom":")" +
      std::to_string(manifest_->sky_colour_bottom.r) + " " +
      std::to_string(manifest_->sky_colour_bottom.g) + " " +
      std::to_string(manifest_->sky_colour_bottom.b) + "\",";
  output << R"("sunColour":")" +
      std::to_string(manifest_->sun_colour.r) + " " +
      std::to_string(manifest_->sun_colour.g) + " " +
      std::to_string(manifest_->sun_colour.b) + "\",";
  output << R"("sunYaw":")" + std::to_string(manifest_->sun_yaw) + "\",";
  output << R"("sunPitch":")" + std::to_string(manifest_->sun_pitch) + "\",";
  output << R"("temperature":")" + manifest_->temperature + "\",";
  output << R"("weather":")" + manifest_->weather + "\",";
  output << R"("time":")" + manifest_->time + "\",";

  // Fog
  output << R"("fogColour":")" +
      std::to_string(manifest_->fog_colour.r) + " " +
      std::to_string(manifest_->fog_colour.g) + " " +
      std::to_string(manifest_->fog_colour.b) + "\",";
  output << R"("fogIntensity":")" + std::to_string(manifest_->fog_intensity) + "\",";
  output << R"("fogDistance":")" + std::to_string(manifest_->fog_distance) + "\"";

  output << "}\n";

  LogInfo("Wrote \"%s\"!\n", path.c_str());
  backup_ = *manifest_;
}

void MapConfigEditor::RestoreManifest() {
  *manifest_ = backup_;
  map_->ApplySkyColours(manifest_->sky_colour_bottom, manifest_->sky_colour_top);
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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/cert_verifier.h"

#include "base/callback.h"
#include "base/file_path.h"
#include "base/stringprintf.h"
#include "net/base/cert_test_util.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/base/x509_certificate.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

class TestTimeService : public CertVerifier::TimeService {
 public:
  // CertVerifier::TimeService methods:
  virtual base::Time Now() { return current_time_; }

  void set_current_time(base::Time now) { current_time_ = now; }

 private:
  base::Time current_time_;
};

class CertVerifierTest : public testing::Test {
};

class ExplodingCallback : public CallbackRunner<Tuple1<int> > {
 public:
  virtual void RunWithParams(const Tuple1<int>& params) {
    FAIL();
  }
};

// Tests a cache hit, which should results in synchronous completion.
TEST_F(CertVerifierTest, CacheHit) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> google_cert(
      ImportCertFromFile(certs_dir, "google.single.der"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(1u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  // Synchronous completion.
  ASSERT_NE(ERR_IO_PENDING, error);
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_TRUE(request_handle == NULL);
  ASSERT_EQ(2u, verifier.requests());
  ASSERT_EQ(1u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());
}

// Tests an inflight join.
TEST_F(CertVerifierTest, InflightJoin) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> google_cert(
      ImportCertFromFile(certs_dir, "google.single.der"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;
  CertVerifyResult verify_result2;
  TestCompletionCallback callback2;
  CertVerifier::RequestHandle request_handle2;

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result2,
                          &callback2, &request_handle2);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle2 != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  error = callback2.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(2u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(1u, verifier.inflight_joins());
}

// Tests cache entry expiration.
TEST_F(CertVerifierTest, ExpiredCacheEntry) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> google_cert(
      ImportCertFromFile(certs_dir, "google.single.der"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(1u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  // Before expiration, should have a cache hit.
  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  // Synchronous completion.
  ASSERT_NE(ERR_IO_PENDING, error);
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_TRUE(request_handle == NULL);
  ASSERT_EQ(2u, verifier.requests());
  ASSERT_EQ(1u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  // After expiration, should not have a cache hit.
  ASSERT_EQ(1u, verifier.GetCacheSize());
  current_time += base::TimeDelta::FromMinutes(60);
  time_service->set_current_time(current_time);
  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  ASSERT_EQ(0u, verifier.GetCacheSize());
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(3u, verifier.requests());
  ASSERT_EQ(1u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());
}

// Tests a full cache.
TEST_F(CertVerifierTest, FullCache) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> google_cert(
      ImportCertFromFile(certs_dir, "google.single.der"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(1u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  const unsigned kCacheSize = 256;

  for (unsigned i = 0; i < kCacheSize; i++) {
    std::string hostname = base::StringPrintf("www%d.example.com", i + 1);
    error = verifier.Verify(google_cert, hostname, 0, &verify_result,
                            &callback, &request_handle);
    ASSERT_EQ(ERR_IO_PENDING, error);
    ASSERT_TRUE(request_handle != NULL);
    error = callback.WaitForResult();
    ASSERT_TRUE(IsCertificateError(error));
  }
  ASSERT_EQ(kCacheSize + 1, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  ASSERT_EQ(kCacheSize, verifier.GetCacheSize());
  current_time += base::TimeDelta::FromMinutes(60);
  time_service->set_current_time(current_time);
  error = verifier.Verify(google_cert, "www999.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  ASSERT_EQ(kCacheSize, verifier.GetCacheSize());
  error = callback.WaitForResult();
  ASSERT_EQ(1u, verifier.GetCacheSize());
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(kCacheSize + 2, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());
}

// Tests that the callback of a canceled request is never made.
TEST_F(CertVerifierTest, CancelRequest) {
  CertVerifier verifier;

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> google_cert(
      ImportCertFromFile(certs_dir, "google.single.der"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);

  int error;
  CertVerifyResult verify_result;
  ExplodingCallback exploding_callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &exploding_callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  verifier.CancelRequest(request_handle);

  // Issue a few more requests to the worker pool and wait for their
  // completion, so that the task of the canceled request (which runs on a
  // worker thread) is likely to complete by the end of this test.
  TestCompletionCallback callback;
  for (int i = 0; i < 5; ++i) {
    error = verifier.Verify(google_cert, "www2.example.com", 0, &verify_result,
                            &callback, &request_handle);
    ASSERT_EQ(ERR_IO_PENDING, error);
    ASSERT_TRUE(request_handle != NULL);
    error = callback.WaitForResult();
    verifier.ClearCache();
  }
}

// Tests that a canceled request is not leaked.
TEST_F(CertVerifierTest, CancelRequestThenQuit) {
  CertVerifier verifier;

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> google_cert(
      ImportCertFromFile(certs_dir, "google.single.der"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  verifier.CancelRequest(request_handle);
  // Destroy |verifier| by going out of scope.
}

}  // namespace net
<commit_msg>Use a different certificate for testing the CertVerifier.<commit_after>// Copyright (c) 2011 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/cert_verifier.h"

#include "base/callback.h"
#include "base/file_path.h"
#include "base/stringprintf.h"
#include "net/base/cert_test_util.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/base/x509_certificate.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

class TestTimeService : public CertVerifier::TimeService {
 public:
  // CertVerifier::TimeService methods:
  virtual base::Time Now() { return current_time_; }

  void set_current_time(base::Time now) { current_time_ = now; }

 private:
  base::Time current_time_;
};

class CertVerifierTest : public testing::Test {
};

class ExplodingCallback : public CallbackRunner<Tuple1<int> > {
 public:
  virtual void RunWithParams(const Tuple1<int>& params) {
    FAIL();
  }
};

// Tests a cache hit, which should results in synchronous completion.
TEST_F(CertVerifierTest, CacheHit) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> test_cert(
      ImportCertFromFile(certs_dir, "ok_cert.pem"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(1u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  // Synchronous completion.
  ASSERT_NE(ERR_IO_PENDING, error);
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_TRUE(request_handle == NULL);
  ASSERT_EQ(2u, verifier.requests());
  ASSERT_EQ(1u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());
}

// Tests an inflight join.
TEST_F(CertVerifierTest, InflightJoin) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> test_cert(
      ImportCertFromFile(certs_dir, "ok_cert.pem"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;
  CertVerifyResult verify_result2;
  TestCompletionCallback callback2;
  CertVerifier::RequestHandle request_handle2;

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result2,
                          &callback2, &request_handle2);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle2 != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  error = callback2.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(2u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(1u, verifier.inflight_joins());
}

// Tests cache entry expiration.
TEST_F(CertVerifierTest, ExpiredCacheEntry) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> test_cert(
      ImportCertFromFile(certs_dir, "ok_cert.pem"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(1u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  // Before expiration, should have a cache hit.
  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  // Synchronous completion.
  ASSERT_NE(ERR_IO_PENDING, error);
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_TRUE(request_handle == NULL);
  ASSERT_EQ(2u, verifier.requests());
  ASSERT_EQ(1u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  // After expiration, should not have a cache hit.
  ASSERT_EQ(1u, verifier.GetCacheSize());
  current_time += base::TimeDelta::FromMinutes(60);
  time_service->set_current_time(current_time);
  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  ASSERT_EQ(0u, verifier.GetCacheSize());
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(3u, verifier.requests());
  ASSERT_EQ(1u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());
}

// Tests a full cache.
TEST_F(CertVerifierTest, FullCache) {
  TestTimeService* time_service = new TestTimeService;
  base::Time current_time = base::Time::Now();
  time_service->set_current_time(current_time);
  CertVerifier verifier(time_service);

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> test_cert(
      ImportCertFromFile(certs_dir, "ok_cert.pem"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  error = callback.WaitForResult();
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(1u, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  const unsigned kCacheSize = 256;

  for (unsigned i = 0; i < kCacheSize; i++) {
    std::string hostname = base::StringPrintf("www%d.example.com", i + 1);
    error = verifier.Verify(test_cert, hostname, 0, &verify_result,
                            &callback, &request_handle);
    ASSERT_EQ(ERR_IO_PENDING, error);
    ASSERT_TRUE(request_handle != NULL);
    error = callback.WaitForResult();
    ASSERT_TRUE(IsCertificateError(error));
  }
  ASSERT_EQ(kCacheSize + 1, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());

  ASSERT_EQ(kCacheSize, verifier.GetCacheSize());
  current_time += base::TimeDelta::FromMinutes(60);
  time_service->set_current_time(current_time);
  error = verifier.Verify(test_cert, "www999.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  ASSERT_EQ(kCacheSize, verifier.GetCacheSize());
  error = callback.WaitForResult();
  ASSERT_EQ(1u, verifier.GetCacheSize());
  ASSERT_TRUE(IsCertificateError(error));
  ASSERT_EQ(kCacheSize + 2, verifier.requests());
  ASSERT_EQ(0u, verifier.cache_hits());
  ASSERT_EQ(0u, verifier.inflight_joins());
}

// Tests that the callback of a canceled request is never made.
TEST_F(CertVerifierTest, CancelRequest) {
  CertVerifier verifier;

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> test_cert(
      ImportCertFromFile(certs_dir, "ok_cert.pem"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);

  int error;
  CertVerifyResult verify_result;
  ExplodingCallback exploding_callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &exploding_callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  verifier.CancelRequest(request_handle);

  // Issue a few more requests to the worker pool and wait for their
  // completion, so that the task of the canceled request (which runs on a
  // worker thread) is likely to complete by the end of this test.
  TestCompletionCallback callback;
  for (int i = 0; i < 5; ++i) {
    error = verifier.Verify(test_cert, "www2.example.com", 0, &verify_result,
                            &callback, &request_handle);
    ASSERT_EQ(ERR_IO_PENDING, error);
    ASSERT_TRUE(request_handle != NULL);
    error = callback.WaitForResult();
    verifier.ClearCache();
  }
}

// Tests that a canceled request is not leaked.
TEST_F(CertVerifierTest, CancelRequestThenQuit) {
  CertVerifier verifier;

  FilePath certs_dir = GetTestCertsDirectory();
  scoped_refptr<X509Certificate> test_cert(
      ImportCertFromFile(certs_dir, "ok_cert.pem"));
  ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);

  int error;
  CertVerifyResult verify_result;
  TestCompletionCallback callback;
  CertVerifier::RequestHandle request_handle;

  error = verifier.Verify(test_cert, "www.example.com", 0, &verify_result,
                          &callback, &request_handle);
  ASSERT_EQ(ERR_IO_PENDING, error);
  ASSERT_TRUE(request_handle != NULL);
  verifier.CancelRequest(request_handle);
  // Destroy |verifier| by going out of scope.
}

}  // namespace net
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007-2009 Digital Bazaar, Inc.  All rights reserved.
 */
#include "db/net/http/HttpHeader.h"

#include "db/util/StringTools.h"

#include <cstdlib>
#include <cstring>

using namespace std;
using namespace db::io;
using namespace db::net::http;
using namespace db::util;

// define CRLF
const char* HttpHeader::CRLF = "\r\n";

// define date format
const char* HttpHeader::sDateFormat = "%a, %d %b %Y %H:%M:%S GMT";

HttpHeader::HttpHeader()
{
   mVersion = NULL;
   mFieldsSize = 0;
}

HttpHeader::~HttpHeader()
{
   if(mVersion != NULL)
   {
      free(mVersion);
   }

   HttpHeader::clearFields();
}

bool HttpHeader::parseStartLine(const char* str, unsigned int length)
{
   // no start line
   return true;
}

void HttpHeader::getStartLine(string& line)
{
   // no start line
   line.erase();
}

bool HttpHeader::hasStartLine()
{
   // no start line
   return false;
}

void HttpHeader::setVersion(const char* version)
{
   if(mVersion != NULL)
   {
      free(mVersion);
   }
   mVersion = strdup(version);
}

const char* HttpHeader::getVersion()
{
   if(mVersion == NULL)
   {
      // default to HTTP/1.1
      mVersion = strdup("HTTP/1.1");
   }

   return mVersion;
}

void HttpHeader::setField(const char* name, long long value)
{
   char temp[25];
   sprintf(temp, "%lli", value);
   setField(name, temp);
}

void HttpHeader::setField(const char* name, const string& value)
{
   // update old field if possible
   FieldMap::iterator start = mFields.find(name);
   if(start != mFields.end())
   {
      // save name
      char* storedName = (char*)start->first;

      // clean up old fields
      FieldMap::iterator end = mFields.upper_bound(storedName);
      for(FieldMap::iterator i = start; i != end; i++)
      {
         mFieldsSize -= i->second.length();
      }
      mFields.erase(start, end);

      // set new field
      mFields.insert(make_pair(storedName, value));
      mFieldsSize += value.length();
   }
   else
   {
      // set new field
      mFieldsSize += strlen(name);
      mFieldsSize += value.length();
      char* fieldName = strdup(name);
      biCapitalize(fieldName);
      mFields.insert(make_pair(fieldName, value));
   }
}

void HttpHeader::addField(const char* name, const string& value)
{
   FieldMap::iterator i = mFields.find(name);
   if(i != mFields.end())
   {
      // add a new field
      mFieldsSize += strlen(name);
      mFieldsSize += value.length();
      mFields.insert(make_pair(i->first, value));
   }
   else
   {
      // set the field
      setField(name, value);
   }
}

void HttpHeader::removeField(const char* name)
{
   // erase field
   FieldMap::iterator start = mFields.find(name);
   if(start != mFields.end())
   {
      // store name for freeing
      char* storedName = (char*)start->first;

      // remove all field entries (may be more than one)
      int length = strlen(name);
      FieldMap::iterator end = mFields.upper_bound(name);
      for(FieldMap::iterator i = start; i != end; i++)
      {
         mFieldsSize -= length;
         mFieldsSize -= i->second.length();
      }

      // erase entire range
      mFields.erase(start, end);

      // free name
      free(storedName);
   }
}

void HttpHeader::clearFields()
{
   // save unique names to free (one char* per field name)
   size_t length = mFields.size();
   char* nameArray[length];
   size_t next = 0;
   for(FieldMap::iterator i = mFields.begin();
       i != mFields.end(); i = mFields.upper_bound(i->first))
   {
      // save the name to be freed
      nameArray[next] = (char*)i->first;
      next++;
   }

   // free names
   for(size_t n = 0; n < next; n++)
   {
      free(nameArray[n]);
   }

   mFieldsSize = 0;
   mFields.clear();
}

int HttpHeader::getFieldCount(const char* name)
{
   return mFields.count(name);
}

bool HttpHeader::getField(const char* name, long long& value, int index)
{
   bool rval = false;

   string str;
   if(getField(name, str, index))
   {
      char* endptr = NULL;
      value = strtoll(str.c_str(), &endptr, 10);
      rval = endptr != str.c_str() && *endptr == '\0';
   }

   return rval;
}

bool HttpHeader::getField(const char* name, string& value, int index)
{
   bool rval = false;

   // find field entry
   FieldMap::iterator i = mFields.find(name);
   if(i != mFields.end())
   {
      // count to correct value
      FieldMap::iterator end = mFields.upper_bound(i->first);
      for(int n = 0; i != end && n < index; n++, i++);

      if(i != end)
      {
         // get value
         value = i->second;
         rval = true;
      }
   }

   return rval;
}

bool HttpHeader::hasField(const char* name)
{
   bool rval = false;

   // find field entry
   FieldMap::iterator i = mFields.find(name);
   if(i != mFields.end())
   {
      rval = true;
   }

   return rval;
}

bool HttpHeader::parse(const string& str)
{
   bool rval = false;

   // clear fields
   clearFields();

   bool startLine = hasStartLine();
   const char* start = str.c_str();
   char* cr;
   char* colon;
   while((cr = strchr(start, '\r')) != NULL)
   {
      // look for CRLF
      if(strchr(cr, '\n') == cr + 1)
      {
         if(startLine)
         {
            rval = parseStartLine(start, cr - start);
            startLine = false;
         }
         else
         {
            // found a CRLF, now find colon
            if((colon = strchr(start, ':')) != NULL && colon < cr)
            {
               // get field name
               char name[(colon - start) + 1];
               memcpy(name, start, colon - start);
               name[colon - start] = 0;

               // skip whitespace
               colon++;
               for(; *colon == ' ' && colon < cr; colon++);

               // get field value
               char value[cr - colon + 1];
               strncpy(value, colon, cr - colon);
               value[(cr - colon)] = 0;

               // add field
               addField(name, value);
            }
         }

         start = cr + 2;
      }
      else
      {
         start = cr + 1;
      }
   }

   return rval;
}

string HttpHeader::toString()
{
   string str;
   str.reserve(512);

   // get the start line
   getStartLine(str);

   // determine total fields size:
   // (CRLF + fields size + fields * (": " + CRLF))
   char fields[2 + mFieldsSize + mFields.size() * 4];
   char* s = fields;

   // append CRLF if there is a start line
   if(str.length() > 0)
   {
      s[0] = '\r';
      s[1] = '\n';
      s += 2;
   }

   // append all fields
   int length;
   for(FieldMap::iterator i = mFields.begin(); i != mFields.end(); i++)
   {
      // get field name length
      length = strlen(i->first);

      // append name
      memcpy(s, i->first, length);
      s += length;

      // append delimiter
      s[0] = ':';
      s[1] = ' ';
      s += 2;

      // append value
      memcpy(s, i->second.c_str(), i->second.length());
      s += i->second.length();

      // append CRLF
      s[0] = '\r';
      s[1] = '\n';
      s += 2;
   }

   // add CRLF
   s[0] = '\r';
   s[1] = '\n';
   s += 2;

   // append fields
   str.append(fields, s - fields);

   return str;
}

bool HttpHeader::write(OutputStream* os)
{
   bool rval = true;

   // get the start line
   string line;
   getStartLine(line);

   // write the start line and CRLF if one exists
   if(line.length() > 0)
   {
      rval =
         os->write(line.c_str(), line.length()) &&
         os->write(CRLF, 2);
   }

   // write all fields
   for(FieldMap::iterator i = mFields.begin(); rval && i != mFields.end(); i++)
   {
      // write name, delimiter, value, and CRLF
      rval =
         os->write(i->first, strlen(i->first)) &&
         os->write(": ", 2) &&
         os->write(i->second.c_str(), i->second.length()) &&
         os->write(CRLF, 2);
   }

   // write ending CRLF
   rval = rval && os->write(CRLF, 2);

   return rval;
}

void HttpHeader::setDate(Date* date)
{
   // get GMT time zone
   TimeZone gmt = TimeZone::getTimeZone("GMT");
   string str;

   if(date == NULL)
   {
      // get current date
      Date now;
      now.format(str, sDateFormat, &gmt);
   }
   else
   {
      date->format(str, sDateFormat, &gmt);
   }

   // set date field
   setField("Date", str);
}

bool HttpHeader::getDate(Date& date)
{
   bool rval = false;

   string str;
   if(getField("Date", str))
   {
      // get GMT time zone
      TimeZone gmt = TimeZone::getTimeZone("GMT");
      rval = date.parse(str.c_str(), "%a, %d %b %Y %H:%M:%S", &gmt);
   }

   return rval;
}

bool HttpHeader::hasContent()
{
   long long length = 0;
   getField("Content-Length", length);
   return (length != 0 || hasField("Transfer-Encoding"));
}

void HttpHeader::writeTo(HttpHeader* header)
{
   // set version
   header->setVersion(getVersion());

   // add all fields
   for(FieldMap::iterator i = mFields.begin(); i != mFields.end(); i++)
   {
      header->addField(i->first, i->second);
   }
}

void HttpHeader::biCapitalize(char* name)
{
   // capitalize first letter and letters after hyphens
   // decapitalize other letters
   // NOTE: hardcoded version is faster than using toupper/tolower
   if(name != NULL && *name != '\0')
   {
      char* ptr = name;
      // cap first
      if(*ptr >= 'a' && *ptr <= 'z')
      {
         *ptr -= 'a' - 'A';
      }
      for(ptr++; *ptr != '\0'; ptr++)
      {
         // cap after '-'
         if(*(ptr - 1) == '-')
         {
            if(*ptr >= 'a' && *ptr <= 'z')
            {
               *ptr -= 'a' - 'A';
            }
         }
         // decap rest
         else if(*ptr >= 'A' && *ptr <= 'Z')
         {
            *ptr += 'a' - 'A';
         }
      }
   }
}
<commit_msg>Added special case for bi-capping TE header.<commit_after>/*
 * Copyright (c) 2007-2009 Digital Bazaar, Inc.  All rights reserved.
 */
#include "db/net/http/HttpHeader.h"

#include "db/util/StringTools.h"

#include <cstdlib>
#include <cstring>

using namespace std;
using namespace db::io;
using namespace db::net::http;
using namespace db::util;

// define CRLF
const char* HttpHeader::CRLF = "\r\n";

// define date format
const char* HttpHeader::sDateFormat = "%a, %d %b %Y %H:%M:%S GMT";

HttpHeader::HttpHeader()
{
   mVersion = NULL;
   mFieldsSize = 0;
}

HttpHeader::~HttpHeader()
{
   if(mVersion != NULL)
   {
      free(mVersion);
   }

   HttpHeader::clearFields();
}

bool HttpHeader::parseStartLine(const char* str, unsigned int length)
{
   // no start line
   return true;
}

void HttpHeader::getStartLine(string& line)
{
   // no start line
   line.erase();
}

bool HttpHeader::hasStartLine()
{
   // no start line
   return false;
}

void HttpHeader::setVersion(const char* version)
{
   if(mVersion != NULL)
   {
      free(mVersion);
   }
   mVersion = strdup(version);
}

const char* HttpHeader::getVersion()
{
   if(mVersion == NULL)
   {
      // default to HTTP/1.1
      mVersion = strdup("HTTP/1.1");
   }

   return mVersion;
}

void HttpHeader::setField(const char* name, long long value)
{
   char temp[25];
   sprintf(temp, "%lli", value);
   setField(name, temp);
}

void HttpHeader::setField(const char* name, const string& value)
{
   // update old field if possible
   FieldMap::iterator start = mFields.find(name);
   if(start != mFields.end())
   {
      // save name
      char* storedName = (char*)start->first;

      // clean up old fields
      FieldMap::iterator end = mFields.upper_bound(storedName);
      for(FieldMap::iterator i = start; i != end; i++)
      {
         mFieldsSize -= i->second.length();
      }
      mFields.erase(start, end);

      // set new field
      mFields.insert(make_pair(storedName, value));
      mFieldsSize += value.length();
   }
   else
   {
      // set new field
      mFieldsSize += strlen(name);
      mFieldsSize += value.length();
      char* fieldName = strdup(name);
      biCapitalize(fieldName);
      mFields.insert(make_pair(fieldName, value));
   }
}

void HttpHeader::addField(const char* name, const string& value)
{
   FieldMap::iterator i = mFields.find(name);
   if(i != mFields.end())
   {
      // add a new field
      mFieldsSize += strlen(name);
      mFieldsSize += value.length();
      mFields.insert(make_pair(i->first, value));
   }
   else
   {
      // set the field
      setField(name, value);
   }
}

void HttpHeader::removeField(const char* name)
{
   // erase field
   FieldMap::iterator start = mFields.find(name);
   if(start != mFields.end())
   {
      // store name for freeing
      char* storedName = (char*)start->first;

      // remove all field entries (may be more than one)
      int length = strlen(name);
      FieldMap::iterator end = mFields.upper_bound(name);
      for(FieldMap::iterator i = start; i != end; i++)
      {
         mFieldsSize -= length;
         mFieldsSize -= i->second.length();
      }

      // erase entire range
      mFields.erase(start, end);

      // free name
      free(storedName);
   }
}

void HttpHeader::clearFields()
{
   // save unique names to free (one char* per field name)
   size_t length = mFields.size();
   char* nameArray[length];
   size_t next = 0;
   for(FieldMap::iterator i = mFields.begin();
       i != mFields.end(); i = mFields.upper_bound(i->first))
   {
      // save the name to be freed
      nameArray[next] = (char*)i->first;
      next++;
   }

   // free names
   for(size_t n = 0; n < next; n++)
   {
      free(nameArray[n]);
   }

   mFieldsSize = 0;
   mFields.clear();
}

int HttpHeader::getFieldCount(const char* name)
{
   return mFields.count(name);
}

bool HttpHeader::getField(const char* name, long long& value, int index)
{
   bool rval = false;

   string str;
   if(getField(name, str, index))
   {
      char* endptr = NULL;
      value = strtoll(str.c_str(), &endptr, 10);
      rval = endptr != str.c_str() && *endptr == '\0';
   }

   return rval;
}

bool HttpHeader::getField(const char* name, string& value, int index)
{
   bool rval = false;

   // find field entry
   FieldMap::iterator i = mFields.find(name);
   if(i != mFields.end())
   {
      // count to correct value
      FieldMap::iterator end = mFields.upper_bound(i->first);
      for(int n = 0; i != end && n < index; n++, i++);

      if(i != end)
      {
         // get value
         value = i->second;
         rval = true;
      }
   }

   return rval;
}

bool HttpHeader::hasField(const char* name)
{
   bool rval = false;

   // find field entry
   FieldMap::iterator i = mFields.find(name);
   if(i != mFields.end())
   {
      rval = true;
   }

   return rval;
}

bool HttpHeader::parse(const string& str)
{
   bool rval = false;

   // clear fields
   clearFields();

   bool startLine = hasStartLine();
   const char* start = str.c_str();
   char* cr;
   char* colon;
   while((cr = strchr(start, '\r')) != NULL)
   {
      // look for CRLF
      if(strchr(cr, '\n') == cr + 1)
      {
         if(startLine)
         {
            rval = parseStartLine(start, cr - start);
            startLine = false;
         }
         else
         {
            // found a CRLF, now find colon
            if((colon = strchr(start, ':')) != NULL && colon < cr)
            {
               // get field name
               char name[(colon - start) + 1];
               memcpy(name, start, colon - start);
               name[colon - start] = 0;

               // skip whitespace
               colon++;
               for(; *colon == ' ' && colon < cr; colon++);

               // get field value
               char value[cr - colon + 1];
               strncpy(value, colon, cr - colon);
               value[(cr - colon)] = 0;

               // add field
               addField(name, value);
            }
         }

         start = cr + 2;
      }
      else
      {
         start = cr + 1;
      }
   }

   return rval;
}

string HttpHeader::toString()
{
   string str;
   str.reserve(512);

   // get the start line
   getStartLine(str);

   // determine total fields size:
   // (CRLF + fields size + fields * (": " + CRLF))
   char fields[2 + mFieldsSize + mFields.size() * 4];
   char* s = fields;

   // append CRLF if there is a start line
   if(str.length() > 0)
   {
      s[0] = '\r';
      s[1] = '\n';
      s += 2;
   }

   // append all fields
   int length;
   for(FieldMap::iterator i = mFields.begin(); i != mFields.end(); i++)
   {
      // get field name length
      length = strlen(i->first);

      // append name
      memcpy(s, i->first, length);
      s += length;

      // append delimiter
      s[0] = ':';
      s[1] = ' ';
      s += 2;

      // append value
      memcpy(s, i->second.c_str(), i->second.length());
      s += i->second.length();

      // append CRLF
      s[0] = '\r';
      s[1] = '\n';
      s += 2;
   }

   // add CRLF
   s[0] = '\r';
   s[1] = '\n';
   s += 2;

   // append fields
   str.append(fields, s - fields);

   return str;
}

bool HttpHeader::write(OutputStream* os)
{
   bool rval = true;

   // get the start line
   string line;
   getStartLine(line);

   // write the start line and CRLF if one exists
   if(line.length() > 0)
   {
      rval =
         os->write(line.c_str(), line.length()) &&
         os->write(CRLF, 2);
   }

   // write all fields
   for(FieldMap::iterator i = mFields.begin(); rval && i != mFields.end(); i++)
   {
      // write name, delimiter, value, and CRLF
      rval =
         os->write(i->first, strlen(i->first)) &&
         os->write(": ", 2) &&
         os->write(i->second.c_str(), i->second.length()) &&
         os->write(CRLF, 2);
   }

   // write ending CRLF
   rval = rval && os->write(CRLF, 2);

   return rval;
}

void HttpHeader::setDate(Date* date)
{
   // get GMT time zone
   TimeZone gmt = TimeZone::getTimeZone("GMT");
   string str;

   if(date == NULL)
   {
      // get current date
      Date now;
      now.format(str, sDateFormat, &gmt);
   }
   else
   {
      date->format(str, sDateFormat, &gmt);
   }

   // set date field
   setField("Date", str);
}

bool HttpHeader::getDate(Date& date)
{
   bool rval = false;

   string str;
   if(getField("Date", str))
   {
      // get GMT time zone
      TimeZone gmt = TimeZone::getTimeZone("GMT");
      rval = date.parse(str.c_str(), "%a, %d %b %Y %H:%M:%S", &gmt);
   }

   return rval;
}

bool HttpHeader::hasContent()
{
   long long length = 0;
   getField("Content-Length", length);
   return (length != 0 || hasField("Transfer-Encoding"));
}

void HttpHeader::writeTo(HttpHeader* header)
{
   // set version
   header->setVersion(getVersion());

   // add all fields
   for(FieldMap::iterator i = mFields.begin(); i != mFields.end(); i++)
   {
      header->addField(i->first, i->second);
   }
}

void HttpHeader::biCapitalize(char* name)
{
   // capitalize first letter and letters after hyphens
   // decapitalize other letters
   // NOTE: hardcoded version is faster than using toupper/tolower
   int length = 0;
   if(name != NULL && *name != '\0')
   {
      length++;
      char* ptr = name;
      // cap first
      if(*ptr >= 'a' && *ptr <= 'z')
      {
         *ptr -= 'a' - 'A';
      }
      for(ptr++; *ptr != '\0'; ptr++, length++)
      {
         // cap after '-'
         if(*(ptr - 1) == '-')
         {
            if(*ptr >= 'a' && *ptr <= 'z')
            {
               *ptr -= 'a' - 'A';
            }
         }
         // decap rest
         else if(*ptr >= 'A' && *ptr <= 'Z')
         {
            *ptr += 'a' - 'A';
         }
      }

      // special case TE header
      if(length == 2 && name[0] == 'T' && name[1] == 'e')
      {
         name[1] = 'E';
      }
   }
}
<|endoftext|>


<commit_before>// Copyright (c) 2008 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/ftp/ftp_network_transaction.h"

#include "net/base/client_socket.h"
#include "net/base/net_errors.h"
#include "net/ftp/ftp_network_session.h"

namespace net {

FtpNetworkTransaction::FtpNetworkTransaction(
    FtpNetworkSession* session, ClientSocketFactory* socket_factory)
#pragma warning(suppress: 4355)
    : io_callback_(this, &FtpNetworkTransaction::OnIOComplete),
      user_callback_(NULL),
      session_(session),
      socket_factory_(socket_factory),
      next_state_(STATE_NONE) {
}

FtpNetworkTransaction::~FtpNetworkTransaction() {
}

void FtpNetworkTransaction::Destroy() {
  delete this;
}

int FtpNetworkTransaction::Start(
    const FtpRequestInfo* request_info, CompletionCallback* callback) {
  request_ = request_info;

  next_state_ = STATE_CTRL_INIT;
  int rv = DoLoop(OK);
  if (rv == ERR_IO_PENDING)
    user_callback_ = callback;
  return rv;
}

int FtpNetworkTransaction::RestartWithAuth(
    const std::wstring& username, const std::wstring& password,
    CompletionCallback* callback) {
  return ERR_FAILED;
}

int FtpNetworkTransaction::Read(
    char* buf, int buf_len, CompletionCallback* callback) {
  return ERR_FAILED;
}

const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
  return NULL;
}

LoadState FtpNetworkTransaction::GetLoadState() const {
  return LOAD_STATE_IDLE;
}

uint64 FtpNetworkTransaction::GetUploadProgress() const {
  return 0;
}

void FtpNetworkTransaction::DoCallback(int rv) {
  DCHECK(rv != ERR_IO_PENDING);
  DCHECK(user_callback_);

  // Since Run may result in Read being called, clear callback_ up front.
  CompletionCallback* c = user_callback_;
  user_callback_ = NULL;
  c->Run(rv);
}

void FtpNetworkTransaction::OnIOComplete(int result) {
  int rv = DoLoop(result);
  if (rv != ERR_IO_PENDING)
    DoCallback(rv);
}

int FtpNetworkTransaction::DoLoop(int result) {
  DCHECK(next_state_ != STATE_NONE);

  int rv = result;
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_CTRL_INIT:
        DCHECK(rv == OK);
        rv = DoCtrlInit();
        break;
      case STATE_CTRL_INIT_COMPLETE:
        rv = DoCtrlInitComplete(rv);
        break;
      case STATE_CTRL_RESOLVE_HOST:
        DCHECK(rv == OK);
        rv = DoCtrlResolveHost();
        break;
      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
        rv = DoCtrlResolveHostComplete(rv);
        break;
      case STATE_CTRL_CONNECT:
        DCHECK(rv == OK);
        rv = DoCtrlConnect();
        break;
      case STATE_CTRL_CONNECT_COMPLETE:
        rv = DoCtrlConnectComplete(rv);
        break;
      case STATE_CTRL_WRITE:
        DCHECK(rv == OK);
        rv = DoCtrlWrite();
        break;
      case STATE_CTRL_WRITE_COMPLETE:
        rv = DoCtrlWriteComplete(rv);
        break;
      case STATE_CTRL_READ:
        DCHECK(rv == OK);
        rv = DoCtrlRead();
        break;
      case STATE_CTRL_READ_COMPLETE:
        rv = DoCtrlReadComplete(rv);
        break;
      case STATE_DATA_CONNECT:
        DCHECK(rv == OK);
        rv = DoDataConnect();
        break;
      case STATE_DATA_CONNECT_COMPLETE:
        rv = DoDataConnectComplete(rv);
        break;
      case STATE_DATA_READ:
        DCHECK(rv == OK);
        rv = DoDataRead();
        break;
      case STATE_DATA_READ_COMPLETE:
        rv = DoDataReadComplete(rv);
        break;
      default:
        NOTREACHED() << "bad state";
        rv = ERR_FAILED;
        break;
    }
  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

  return rv;
}

int FtpNetworkTransaction::DoCtrlInit() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlInitComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlResolveHost() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlConnect() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlWrite() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlRead() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataConnect() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataConnectComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataRead() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataReadComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

}  // namespace net
<commit_msg>Fix Linux build.<commit_after>// Copyright (c) 2008 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/ftp/ftp_network_transaction.h"

#include "base/compiler_specific.h"
#include "net/base/client_socket.h"
#include "net/base/net_errors.h"
#include "net/ftp/ftp_network_session.h"

namespace net {

FtpNetworkTransaction::FtpNetworkTransaction(
    FtpNetworkSession* session, ClientSocketFactory* socket_factory)
  :  ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(this,
                                         &FtpNetworkTransaction::OnIOComplete)),
      user_callback_(NULL),
      session_(session),
      socket_factory_(socket_factory),
      next_state_(STATE_NONE) {
}

FtpNetworkTransaction::~FtpNetworkTransaction() {
}

void FtpNetworkTransaction::Destroy() {
  delete this;
}

int FtpNetworkTransaction::Start(
    const FtpRequestInfo* request_info, CompletionCallback* callback) {
  request_ = request_info;

  next_state_ = STATE_CTRL_INIT;
  int rv = DoLoop(OK);
  if (rv == ERR_IO_PENDING)
    user_callback_ = callback;
  return rv;
}

int FtpNetworkTransaction::RestartWithAuth(
    const std::wstring& username, const std::wstring& password,
    CompletionCallback* callback) {
  return ERR_FAILED;
}

int FtpNetworkTransaction::Read(
    char* buf, int buf_len, CompletionCallback* callback) {
  return ERR_FAILED;
}

const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
  return NULL;
}

LoadState FtpNetworkTransaction::GetLoadState() const {
  return LOAD_STATE_IDLE;
}

uint64 FtpNetworkTransaction::GetUploadProgress() const {
  return 0;
}

void FtpNetworkTransaction::DoCallback(int rv) {
  DCHECK(rv != ERR_IO_PENDING);
  DCHECK(user_callback_);

  // Since Run may result in Read being called, clear callback_ up front.
  CompletionCallback* c = user_callback_;
  user_callback_ = NULL;
  c->Run(rv);
}

void FtpNetworkTransaction::OnIOComplete(int result) {
  int rv = DoLoop(result);
  if (rv != ERR_IO_PENDING)
    DoCallback(rv);
}

int FtpNetworkTransaction::DoLoop(int result) {
  DCHECK(next_state_ != STATE_NONE);

  int rv = result;
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_CTRL_INIT:
        DCHECK(rv == OK);
        rv = DoCtrlInit();
        break;
      case STATE_CTRL_INIT_COMPLETE:
        rv = DoCtrlInitComplete(rv);
        break;
      case STATE_CTRL_RESOLVE_HOST:
        DCHECK(rv == OK);
        rv = DoCtrlResolveHost();
        break;
      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
        rv = DoCtrlResolveHostComplete(rv);
        break;
      case STATE_CTRL_CONNECT:
        DCHECK(rv == OK);
        rv = DoCtrlConnect();
        break;
      case STATE_CTRL_CONNECT_COMPLETE:
        rv = DoCtrlConnectComplete(rv);
        break;
      case STATE_CTRL_WRITE:
        DCHECK(rv == OK);
        rv = DoCtrlWrite();
        break;
      case STATE_CTRL_WRITE_COMPLETE:
        rv = DoCtrlWriteComplete(rv);
        break;
      case STATE_CTRL_READ:
        DCHECK(rv == OK);
        rv = DoCtrlRead();
        break;
      case STATE_CTRL_READ_COMPLETE:
        rv = DoCtrlReadComplete(rv);
        break;
      case STATE_DATA_CONNECT:
        DCHECK(rv == OK);
        rv = DoDataConnect();
        break;
      case STATE_DATA_CONNECT_COMPLETE:
        rv = DoDataConnectComplete(rv);
        break;
      case STATE_DATA_READ:
        DCHECK(rv == OK);
        rv = DoDataRead();
        break;
      case STATE_DATA_READ_COMPLETE:
        rv = DoDataReadComplete(rv);
        break;
      default:
        NOTREACHED() << "bad state";
        rv = ERR_FAILED;
        break;
    }
  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

  return rv;
}

int FtpNetworkTransaction::DoCtrlInit() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlInitComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlResolveHost() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlConnect() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlWrite() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlRead() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataConnect() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataConnectComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataRead() {
  return ERR_FAILED;  // TODO(darin): implement me
}

int FtpNetworkTransaction::DoDataReadComplete(int result) {
  return ERR_FAILED;  // TODO(darin): implement me
}

}  // namespace net
<|endoftext|>


<commit_before><commit_msg>Validate offset / length of extra field in the message. Note these fields are decoded and set in a structure but unsused, so this is not a current security issue. This change just future-proofs the area in case these fields are used one day.<commit_after><|endoftext|>


<commit_before>/**
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/blockchain/orphan_pool.hpp>

#include <algorithm>
#include <cstddef>
#include <bitcoin/blockchain/block_detail.hpp>

namespace libbitcoin {
namespace blockchain {

orphan_pool::orphan_pool(size_t capacity)
{
	buffer_.reserve(capacity == 0 ? 1 : capacity);
}

// There is no validation whatsoever of the block up to this pont.
bool orphan_pool::add(block_detail::ptr block)
{
    const auto& header = block->actual()->header;

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_upgrade();

    // No duplicates allowed.
    if (exists(header))
    {
        mutex_.unlock_upgrade();
        //-----------------------------------------------------------------
        return false;
    }

    const auto old_size = buffer_.size();
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    mutex_.unlock_upgrade_and_lock();
    buffer_.push_back(block);
    mutex_.unlock();
    ///////////////////////////////////////////////////////////////////////////

    log::debug(LOG_BLOCKCHAIN)
        << "Orphan pool added block [" << encode_hash(block->hash())
        << "] previous [" << encode_hash(header.previous_block_hash)
        << "] old size (" << old_size << ").";

    return true;
}

void orphan_pool::remove(block_detail::ptr block)
{
    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_upgrade();

    const auto it = std::find(buffer_.begin(), buffer_.end(), block);

    if (it == buffer_.end())
    {
        mutex_.unlock_upgrade();
        //-----------------------------------------------------------------
        return;
    }

    const auto old_size = buffer_.size();
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    mutex_.unlock_upgrade_and_lock();
    buffer_.erase(it);
    mutex_.unlock();
    ///////////////////////////////////////////////////////////////////////////

    log::debug(LOG_BLOCKCHAIN)
        << "Orphan pool removed block [" << encode_hash(block->hash())
        << "] old size (" << old_size << ").";
}

// TODO: use hash table pool to eliminate this O(n^2) search.
void orphan_pool::filter(message::get_data::ptr message) const
{
    auto& inventories = message->inventories;

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    shared_lock lock(mutex_);

    for (auto it = inventories.begin(); it != inventories.end();)
        if (it->is_block_type() && exists(it->hash))
            it = inventories.erase(it);
        else
            ++it;
    ///////////////////////////////////////////////////////////////////////////
}

block_detail::list orphan_pool::trace(block_detail::ptr end) const
{
    block_detail::list trace;
    trace.reserve(buffer_.size());
    trace.push_back(end);
    auto& hash = end->actual()->header.previous_block_hash;

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_shared();

    for (auto it = find(hash); it != buffer_.end(); it = find(hash))
    {
        trace.push_back(*it);
        hash = (*it)->actual()->header.previous_block_hash;
    }

    mutex_.unlock_shared();
    ///////////////////////////////////////////////////////////////////////////

    BITCOIN_ASSERT(!trace.empty());
    std::reverse(trace.begin(), trace.end());
    trace.shrink_to_fit();
    return trace;
}

block_detail::list orphan_pool::unprocessed() const
{
    block_detail::list unprocessed;
    unprocessed.reserve(buffer_.size());

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_shared();

    // Earlier blocks enter pool first, so reversal helps avoid fragmentation.
    for (auto it = buffer_.rbegin(); it != buffer_.rend(); ++it)
        if (!(*it)->processed())
            unprocessed.push_back(*it);

    mutex_.unlock_shared();
    ///////////////////////////////////////////////////////////////////////////

    unprocessed.shrink_to_fit();
    return unprocessed;
}

// private
//-----------------------------------------------------------------------------

bool orphan_pool::exists(const hash_digest& hash) const
{
    const auto match = [&hash](const block_detail::ptr& entry)
    {
        return hash == entry->hash();
    };

    return std::any_of(buffer_.begin(), buffer_.end(), match);
}

bool orphan_pool::exists(const chain::header& header) const
{
    const auto match = [&header](const block_detail::ptr& entry)
    {
        return header == entry->actual()->header;
    };

    return std::any_of(buffer_.begin(), buffer_.end(), match);
}

orphan_pool::const_iterator orphan_pool::find(const hash_digest& hash) const
{
    const auto match = [&hash](const block_detail::ptr& entry)
    {
        return hash == entry->hash();
    };

    return std::find_if(buffer_.begin(), buffer_.end(), match);
}

} // namespace blockchain
} // namespace libbitcoin
<commit_msg>syn block error<commit_after>/**
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/blockchain/orphan_pool.hpp>

#include <algorithm>
#include <cstddef>
#include <bitcoin/blockchain/block_detail.hpp>

namespace libbitcoin {
namespace blockchain {

orphan_pool::orphan_pool(size_t capacity)
{
	buffer_.reserve(capacity == 0 ? 1 : capacity);
}

// There is no validation whatsoever of the block up to this pont.
bool orphan_pool::add(block_detail::ptr block)
{
    const auto& header = block->actual()->header;

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_upgrade();

    // No duplicates allowed.
    if (exists(header))
    {
        mutex_.unlock_upgrade();
        //-----------------------------------------------------------------
        return false;
    }

    const auto old_size = buffer_.size();
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    mutex_.unlock_upgrade_and_lock();
    buffer_.push_back(block);
    mutex_.unlock();
    ///////////////////////////////////////////////////////////////////////////

    log::debug(LOG_BLOCKCHAIN)
        << "Orphan pool added block [" << encode_hash(block->hash())
        << "] previous [" << encode_hash(header.previous_block_hash)
        << "] old size (" << old_size << ").";

    return true;
}

void orphan_pool::remove(block_detail::ptr block)
{
    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_upgrade();

    const auto it = std::find(buffer_.begin(), buffer_.end(), block);

    if (it == buffer_.end())
    {
        mutex_.unlock_upgrade();
        //-----------------------------------------------------------------
        return;
    }

    const auto old_size = buffer_.size();
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    mutex_.unlock_upgrade_and_lock();
    buffer_.erase(it);
    mutex_.unlock();
    ///////////////////////////////////////////////////////////////////////////

    log::debug(LOG_BLOCKCHAIN)
        << "Orphan pool removed block [" << encode_hash(block->hash())
        << "] old size (" << old_size << ").";
}

// TODO: use hash table pool to eliminate this O(n^2) search.
void orphan_pool::filter(message::get_data::ptr message) const
{
    auto& inventories = message->inventories;

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    shared_lock lock(mutex_);

    for (auto it = inventories.begin(); it != inventories.end();)
        if (it->is_block_type() && exists(it->hash))
            it = inventories.erase(it);
        else
            ++it;
    ///////////////////////////////////////////////////////////////////////////
}

block_detail::list orphan_pool::trace(block_detail::ptr end) const
{
    block_detail::list trace;
    trace.reserve(buffer_.size());
    trace.push_back(end);
    auto hash = end->actual()->header.previous_block_hash;

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_shared();

    for (auto it = find(hash); it != buffer_.end(); it = find(hash))
    {
        trace.push_back(*it);
        hash = (*it)->actual()->header.previous_block_hash;
    }

    mutex_.unlock_shared();
    ///////////////////////////////////////////////////////////////////////////

    BITCOIN_ASSERT(!trace.empty());
    std::reverse(trace.begin(), trace.end());
    trace.shrink_to_fit();
    return trace;
}

block_detail::list orphan_pool::unprocessed() const
{
    block_detail::list unprocessed;
    unprocessed.reserve(buffer_.size());

    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex_.lock_shared();

    // Earlier blocks enter pool first, so reversal helps avoid fragmentation.
    for (auto it = buffer_.rbegin(); it != buffer_.rend(); ++it)
        if (!(*it)->processed())
            unprocessed.push_back(*it);

    mutex_.unlock_shared();
    ///////////////////////////////////////////////////////////////////////////

    unprocessed.shrink_to_fit();
    return unprocessed;
}

// private
//-----------------------------------------------------------------------------

bool orphan_pool::exists(const hash_digest& hash) const
{
    const auto match = [&hash](const block_detail::ptr& entry)
    {
        return hash == entry->hash();
    };

    return std::any_of(buffer_.begin(), buffer_.end(), match);
}

bool orphan_pool::exists(const chain::header& header) const
{
    const auto match = [&header](const block_detail::ptr& entry)
    {
        return header == entry->actual()->header;
    };

    return std::any_of(buffer_.begin(), buffer_.end(), match);
}

orphan_pool::const_iterator orphan_pool::find(const hash_digest& hash) const
{
    const auto match = [&hash](const block_detail::ptr& entry)
    {
        return hash == entry->hash();
    };

    return std::find_if(buffer_.begin(), buffer_.end(), match);
}

} // namespace blockchain
} // namespace libbitcoin
<|endoftext|>