diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8987ab7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +include(FetchContent) + +project(password-analyzer CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmacro-prefix-map=${CMAKE_SOURCE_DIR}=.") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=mold -Wno-unused-command-line-argument") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined") + +message(STATUS "[DEPS] Processing spdlog") +FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog + GIT_TAG v1.12.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + OVERRIDE_FIND_PACKAGE +) +FetchContent_MakeAvailable(spdlog) + +message(STATUS "[DEPS] Processing Catch2") +find_package(Catch2 3 CONFIG) +if (NOT Catch2_FOUND) + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.4.0 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(Catch2) + list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) +endif () + +message(STATUS "[DEPS] Processing MagicEnum") +FetchContent_Declare( + magic_enum + GIT_REPOSITORY https://github.com/Neargye/magic_enum + GIT_TAG v0.9.3 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + OVERRIDE_FIND_PACKAGE +) +FetchContent_MakeAvailable(magic_enum) + +message(STATUS "[DEPS] Processing generator") +file(DOWNLOAD + "https://raw.githubusercontent.com/lewissbaker/generator/master/include/__generator.hpp" + "${CMAKE_BINARY_DIR}/external/generator.hpp") +include_directories(password-analyzer PRIVATE ${CMAKE_BINARY_DIR}/external) + +set(COMMON_SOURCES + defs.hpp + utils.cpp +) +set(COMMON_LIBRARIES + magic_enum::magic_enum + spdlog::spdlog +) + +add_executable(password-analyzer ${COMMON_SOURCES} analyzer.cpp) +target_link_libraries(password-analyzer PRIVATE ${COMMON_LIBRARIES}) diff --git a/defs.hpp b/defs.hpp new file mode 100644 index 0000000..b8250d7 --- /dev/null +++ b/defs.hpp @@ -0,0 +1,24 @@ +#ifndef DEFS_HPP +#define DEFS_HPP + +#include +#if __has_include() + #include +#else + #include "generator.hpp" +#endif + +#include "spdlog/spdlog.h" +#include "magic_enum.hpp" + +using std::operator""s; +using std::operator""sv; + +enum class DataSource { CSDN, YAHOO }; + +bool is_num(char c); +bool is_alpha(char c); + +std::generator passwords(const DataSource &source); + +#endif // DEFS_HPP diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..119e82b --- /dev/null +++ b/utils.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +#include "defs.hpp" + +bool is_num(char c) { return c >= '0' && c <= '9'; } +bool is_alpha(char c) { return std::isalpha(c); } + +static std::vector data_buf[2]; +enum class BufStatus { INIT, FILLING, DONE }; +static BufStatus data_buf_ok[2] = {BufStatus::INIT, BufStatus::INIT}; + +std::generator passwords(const DataSource &source) { + if (data_buf_ok[magic_enum::enum_integer(source)] == BufStatus::DONE) { + for (auto const &password : data_buf[magic_enum::enum_integer(source)]) { + co_yield password; + } + co_return; + } + + if (data_buf_ok[magic_enum::enum_integer(source)] == BufStatus::INIT) + data_buf_ok[magic_enum::enum_integer(source)] = BufStatus::FILLING; + + auto filename = "data/"s + (source == DataSource::CSDN ? "www.csdn.net.sql"s : "plaintxt_yahoo.txt"s); + std::ifstream is(filename); + + if (!is.is_open()) { + spdlog::error("failed to open {}", filename); + co_return; + } + + switch (source) { + case DataSource::CSDN: { + constexpr auto delim{" # "sv}; + std::string line; + while (std::getline(is, line)) { + auto segments = line | std::views::split(delim); + auto second = *std::ranges::next(segments.begin(), 1); + auto password = std::string(second.begin(), second.end()); + + if (data_buf_ok[magic_enum::enum_integer(source)] == BufStatus::FILLING) + data_buf[magic_enum::enum_integer(source)].push_back(password); + + co_yield password; + } + } break; + case DataSource::YAHOO: { + constexpr auto delim{":"sv}; + std::string line; + while (std::getline(is, line)) { + std::erase(line, '\r'); + if (line.empty() || !is_num(line[0])) continue; + if (is_num(line[0]) && line[1] == '.') continue; + + auto segments = line | std::views::split(delim); + auto third = *std::ranges::next(segments.begin(), 2); + auto password = std::string(third.begin(), third.end()); + + if (password.empty()) continue; + if (data_buf_ok[magic_enum::enum_integer(source)] == BufStatus::FILLING) + data_buf[magic_enum::enum_integer(source)].push_back(password); + + co_yield password; + } + } break; + default: std::unreachable(); + } + + data_buf_ok[magic_enum::enum_integer(source)] = BufStatus::DONE; + + is.close(); + co_return; +} \ No newline at end of file