レポジトリ種類: Mercurial

#include <string>
#include <iostream>
#include <filesystem>
#include <fstream>
#include <sstream>

#include "crow.h"

std::string escapeHtml(const std::string &input) {
  std::string escaped;
  for (char c : input) {
    switch (c) {
      case '&': escaped += "&amp;"; break;
      case '<': escaped += "&lt;"; break;
      case '>': escaped += "&gt;"; break;
      case '"': escaped += "&quot;"; break;
      case '\'': escaped += "&#39;"; break;
      default: escaped += c; break;
    }
  }

  return escaped;
}

std::string generateBreadcrumb(const std::string &urlpath) {
  std::stringstream breadcrumb;
  breadcrumb << "<nav><ul style=\"display: inline;\">"
    "<li style=\"display: inline; margin-right: 8px;\">"
    "<a href=\"/\">トップ</a> / </li>";
  std::string currentPath = "";
  size_t start = 0, end = 0;

  while ((end = urlpath.find('/', start)) != std::string::npos) {
    std::string part = urlpath.substr(start, end - start);
    currentPath += part + "/";
    breadcrumb << "<li style=\"display: inline; margin-right: 8px;\">"
               << "<a href=\"/" << currentPath.substr(0, currentPath.size() - 1)
               << "\">"
               << part << "</a> / "
               << "</li>";
    start = end + 1;
  }

  std::string lastPart = urlpath.substr(start);
  currentPath += lastPart;
  breadcrumb << "<li style=\"display: inline;\">" << lastPart << "</li>";

  breadcrumb << "</ul></nav>";
  return breadcrumb.str();
}

bool isImage(const std::string &path) {
  const std::string extensions[] = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"};
  for (const auto &ext : extensions) {
    if (path.size() >= ext.size() &&
        path.compare(path.size() - ext.size(), ext.size(), ext) == 0) {
      return true;
    }
  }

  return false;
}

int main() {
  std::string path = "/home/suwako/open-repos/";
  std::string repotype = "";

  crow::SimpleApp website;

  CROW_ROUTE(website, "/")([&]() -> crow::response {
    repotype = "";
    std::string res = "<html><head><meta content=\"text/html; charset=utf-8\" "
    "http-equiv=\"content-type\" />"
    "<style>html { color: #fcfcfc; background-color: #232023; } "
    "a { color: #ea79d8; }</style>"
    "</head><body>";

    res += "<h1>/</h1>";
    res += "<ul>";
    res += "<li><a href=\"/game\">ゲーム</a></li>";
    res += "<li><a href=\"/soft\">ソフト</a></li>";
    res += "<li><a href=\"/web\">ウェブ</a></li>";
    res += "</ul>";
    res += "</body></html>";

    crow::response out(200, res);
    out.add_header("Content-Type", "text/html; charset=utf-8");
    return out;
  });

  CROW_ROUTE(website, "/<path>")([&](const crow::request &req, std::string urlpath) 
      -> crow::response {
    std::string lspath = path + urlpath;

    std::cout << "FUCK " << urlpath << std::endl;
    if (urlpath == "soft" || urlpath == "game" || urlpath == "web") {
      repotype = "";
    }

    std::string title = "";
    std::vector<std::string> ret;

    std::string res = "<html><head><meta content=\"text/html; charset=utf-8\" "
    "http-equiv=\"content-type\" />"
    "<style>html { color: #fcfcfc; background-color: #232023; } "
    "a { color: #ea79d8; }</style>"
    "</head><body>";

    if (lspath.find(".svn") != std::string::npos || 
        lspath.find(".git") != std::string::npos || 
        lspath.find(".hg") != std::string::npos) {
      std::cerr << "アクセス禁止: " << lspath << std::endl;
      res += "<p>馬鹿野郎、ここは立入禁止だろう!</p>";
      res += "</body></html>";
      crow::response out(403, res);
      out.add_header("Content-Type", "text/html; charset=utf-8");
      return out;
    }

    try {
      if (!std::filesystem::exists(lspath)) {
        std::cerr << "見つかりません: " << lspath << std::endl;
        res += "<p>見つかりません。</p></body></html>";
        crow::response out(404, res);
        out.add_header("Content-Type", "text/html; charset=utf-8");
        return out;
      }

      if (std::filesystem::is_regular_file(lspath)) {
        if (isImage(lspath)) {
          crow::response imageResponse;
          std::ifstream file(lspath, std::ios::binary);
          if (!file.is_open()) {
            std::cerr << "エラー: ファイルを開くに失敗: " << lspath << std::endl;
            res += "<p>エラー: ファイルを開くに失敗。</p></body></html>";
            crow::response out(500, res);
            out.add_header("Content-Type", "text/html; charset=utf-8");
            return out;
          }
          std::ostringstream buffer;
          buffer << file.rdbuf();
          file.close();

          std::string contentType = "image/";
          if (lspath.ends_with(".jpg") || lspath.ends_with(".jpeg")) contentType += "jpeg";
          else if (lspath.ends_with(".png")) contentType += "png";
          else if (lspath.ends_with(".gif")) contentType += "gif";
          else if (lspath.ends_with(".bmp")) contentType += "bmp";
          else if (lspath.ends_with(".webp")) contentType += "webp";

          imageResponse.add_header("Content-Type", contentType);
          imageResponse.write(buffer.str());
          return imageResponse;
        }

        std::ifstream file(lspath);
        if (!file.is_open()) {
          std::cerr << "エラー: ファイルを開くに失敗: " << lspath << std::endl;
          res += "<p>エラー: ファイルを開くに失敗。</p></body></html>";
          crow::response out(500, res);
          out.add_header("Content-Type", "text/html; charset=utf-8");
          return out;
        }

        std::stringstream buffer;
        buffer << file.rdbuf();
        file.close();

        std::string content = escapeHtml(buffer.str());

        res += "<h1>" + title + "</h1>";
        res += generateBreadcrumb(urlpath);
        if (!repotype.empty()) res += "<h2>レポジトリ種類: " + repotype + "</h2>";
        res += "<pre><code>" + content + "</code></pre>";
        res += "</body></html>";
        crow::response out(200, res);
        out.add_header("Content-Type", "text/html; charset=utf-8");
        return out;
      }

      if (!lspath.empty() && lspath.back() != '/') lspath += '/';

      if (!std::filesystem::exists(lspath)) {
        std::cerr << "ディレクトリが見つかりません: " << lspath << std::endl;
        res += "<p>ディレクトリが見つかりません。</p></body></html>";
        crow::response out(404, res);
        out.add_header("Content-Type", "text/html; charset=utf-8");
        return out;
      }

      for (const auto &entry : std::filesystem::directory_iterator(lspath)) {
        std::string fullpath = entry.path().string();
        std::string showpath = fullpath.substr(lspath.length());

        if (showpath == ".hg") {
          repotype = "Mercurial";
          continue;
        }
        if (showpath == ".git") {
          repotype = "Git";
          continue;
        }
        if (showpath == ".svn") {
          repotype = "SVN";
          continue;
        }

        std::string outpath = "<a href=\"/" + urlpath + "/" + showpath + "\">"
                            + showpath + "</a>";

        ret.push_back(outpath);
      }
    } catch (const std::filesystem::filesystem_error &e) {
      std::cerr << "エラーが発生しました: " << e.what() << std::endl;
      res += "<p>エラーが発生しました。</p></body></html>";
      crow::response out(500, res);
      out.add_header("Content-Type", "text/html; charset=utf-8");
      return out;
    }

    res += "<h1>" + title + "</h1>";
    res += generateBreadcrumb(urlpath);
    if (!repotype.empty()) res += "<h2>レポジトリ種類: " + repotype + "</h2>";
    res += "<ul>";
    for (const auto &line : ret) {
      res += "<li>" + line + "</li>";
    }
    res += "</ul>";

    res += "</body></html>";

    crow::response out(200, res);
    out.add_header("Content-Type", "text/html; charset=utf-8");
    return out;
  });

  website.port(8040).run();
}