From c29cdb831b7586bb8421ca48e9e78a3a2183aca7 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Mon, 13 Apr 2026 02:55:36 +0800 Subject: feat: implement persistent cookie storage and migrate configuration files to ~/.ricochet directory --- src/core/browser.cc | 14 ++++++++++++-- src/net/http_client.cc | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/core/browser.cc b/src/core/browser.cc index e9f1fce..03c34bc 100644 --- a/src/core/browser.cc +++ b/src/core/browser.cc @@ -23,10 +23,12 @@ #include "ricochet/render/renderer.hh" #include "ricochet/tui/terminal.hh" #include +#include #include #include #include #include +#include namespace ricochet::core { @@ -47,6 +49,14 @@ struct HttpRequest namespace { +std::string get_config_path( const std::string& filename ) +{ + const char* home = std::getenv( "HOME" ); // NOLINT(concurrency-mt-unsafe) + const std::string dir = ( home != nullptr ) ? std::string( home ) + "/.ricochet" : ".ricochet"; + static_cast( mkdir( dir.c_str(), 0755 ) ); + return dir + "/" + filename; +} + std::string extract_title( const parser::DomNode& root_node ) { std::vector stack; @@ -93,7 +103,7 @@ void save_bookmark( const std::string& title, // NOLINT(bugprone-easily-swappabl if ( url == "ricochet://bookmarks" ) { return; } - std::ofstream file( "bookmarks.txt", std::ios::app ); + std::ofstream file( get_config_path( "bookmarks.txt" ), std::ios::app ); if ( !file.is_open() ) { return; } @@ -112,7 +122,7 @@ void save_bookmark( const std::string& title, // NOLINT(bugprone-easily-swappabl std::string generate_bookmarks_html() { - std::ifstream file( "bookmarks.txt" ); + std::ifstream file( get_config_path( "bookmarks.txt" ) ); std::string html = "My Bookmarks

Saved Bookmarks

    "; if ( !file.is_open() ) { html += "
  • No bookmarks found. Press 'b' on any page to add one!
  • "; diff --git a/src/net/http_client.cc b/src/net/http_client.cc index bbc5d17..5a56ac3 100644 --- a/src/net/http_client.cc +++ b/src/net/http_client.cc @@ -23,9 +23,12 @@ #include "ricochet/net/sys/socket.hh" #include +#include #include +#include #include #include +#include #include namespace ricochet::net { @@ -163,15 +166,55 @@ std::string extract_redirect_url( const std::string& raw_response, bool is_https return ""; } +std::string get_config_path( const std::string& filename ) +{ + const char* home = std::getenv( "HOME" ); // NOLINT(concurrency-mt-unsafe) + const std::string dir = ( home != nullptr ) ? std::string( home ) + "/.ricochet" : ".ricochet"; + static_cast( mkdir( dir.c_str(), 0755 ) ); + return dir + "/" + filename; +} + +void load_cookies_from_file( std::unordered_map& jar ) +{ + std::ifstream file( get_config_path( "cookies.txt" ) ); + if ( !file.is_open() ) { + return; + } + std::string line; + while ( std::getline( file, line ) ) { + const std::size_t tab_pos = line.find( '\t' ); + if ( tab_pos != std::string::npos ) { + jar[line.substr( 0, tab_pos )] = line.substr( tab_pos + 1 ); + } + } +} + std::unordered_map& get_cookie_jar() { static std::unordered_map jar; + static bool initialized = false; + if ( !initialized ) { + load_cookies_from_file( jar ); + initialized = true; + } return jar; } +void save_cookies_to_file( const std::unordered_map& jar ) +{ + std::ofstream file( get_config_path( "cookies.txt" ), std::ios::trunc ); + if ( !file.is_open() ) { + return; + } + for ( const auto& [k, v] : jar ) { + file << k << '\t' << v << '\n'; + } +} + void update_cookies( const std::string& raw_response ) { std::size_t pos = 0; + bool updated = false; while ( ( pos = raw_response.find( "Set-Cookie: ", pos ) ) != std::string::npos ) { pos += 12; const std::size_t end_pos = raw_response.find( '\r', pos ); @@ -189,8 +232,12 @@ void update_cookies( const std::string& raw_response ) const std::string key = cookie_pair.substr( 0, eq_pos ); const std::string val = cookie_pair.substr( eq_pos + 1 ); get_cookie_jar()[key] = val; + updated = true; } } + if ( updated ) { + save_cookies_to_file( get_cookie_jar() ); + } } std::string build_cookie_header() -- cgit v1.2.3