From 60f22a9cba69a8443ed1442c5294a0102ed6f1a3 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 28 Jul 2021 15:50:26 +0200 Subject: 4chan: add timeout for posting without pass, handle noop captcha challenge --- src/QuickMedia.cpp | 122 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 30 deletions(-) (limited to 'src/QuickMedia.cpp') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index f4cc2dd..b52678d 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -437,7 +437,7 @@ namespace QuickMedia { } } else if(strcmp(argv[i], "-e") == 0) { if(i < argc - 1) { - parent_window = strtol(argv[i + 1], nullptr, 0); + parent_window = strtoll(argv[i + 1], nullptr, 0); if(parent_window == None && errno == EINVAL) { fprintf(stderr, "Invalid -e argument. Argument has to be a number\n"); usage(); @@ -1001,7 +1001,7 @@ namespace QuickMedia { std::string id_str = base64_url_decode(filename); char *endptr = nullptr; errno = 0; - long id = strtol(id_str.c_str(), &endptr, 10); + long id = strtoll(id_str.c_str(), &endptr, 10); if(endptr != id_str.c_str() && errno == 0) legacy_manga_ids.push_back(id); return true; @@ -3663,6 +3663,15 @@ namespace QuickMedia { window_size.y = window.getSize().y; } + static bool get_image_board_last_posted_filepath(const char *plugin_name, Path &path) { + Path dir = get_storage_dir().join(plugin_name); + if(create_directory_recursive(dir) != 0) + return false; + path = std::move(dir); + path.join("last_posted_time"); + return true; + } + void Program::image_board_thread_page(ImageBoardThreadPage *thread_page, Body *thread_body) { // TODO: Instead of using stage here, use different pages for each stage enum class NavigationStage { @@ -3686,6 +3695,9 @@ namespace QuickMedia { std::string attached_image_url; ImageBoardCaptchaChallenge captcha_challenge; + captcha_texture.setSmooth(true); + captcha_bg_texture.setSmooth(true); + const float captcha_slide_padding_x = std::floor(4.0f * get_ui_scale()); const float captcha_slide_padding_y = std::floor(4.0f * get_ui_scale()); sf::Color background_color_darker = get_current_theme().background_color; @@ -3700,11 +3712,26 @@ namespace QuickMedia { std::string captcha_post_id; std::string captcha_solution; - sf::Clock captcha_solved_time; std::string comment_to_post; const int captcha_solution_text_height = 18 * get_ui_scale(); sf::Text captcha_solution_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD), captcha_solution_text_height); - int solved_captcha_ttl = 120; + int solved_captcha_ttl = 0; + int64_t last_posted_time = get_unix_time_monotonic(); + int64_t seconds_until_post_again = 60; // TODO: Timeout for other imageboards + bool has_post_timeout = thread_page->get_pass_id().empty(); + + bool has_posted_before = false; + Path last_posted_time_filepath; + if(get_image_board_last_posted_filepath(plugin_name, last_posted_time_filepath)) { + std::string d; + if(file_get_content(last_posted_time_filepath, d) == 0) { + last_posted_time = strtoll(d.c_str(), nullptr, 10); + has_posted_before = true; + } + } + + if(!has_posted_before) + last_posted_time -= (seconds_until_post_again + 1); bool redraw = true; @@ -3714,7 +3741,7 @@ namespace QuickMedia { std::string selected_file_for_upload; - auto post_comment = [&comment_input, &selected_file_for_upload, &navigation_stage, &thread_page, &captcha_post_id, &captcha_solution](std::string comment_to_post, std::string file_to_upload) { + auto post_comment = [this, &comment_input, &selected_file_for_upload, &navigation_stage, &thread_page, &captcha_post_id, &captcha_solution, &last_posted_time, &has_post_timeout](std::string comment_to_post, std::string file_to_upload) { comment_input.set_editable(false); PostResult post_result = thread_page->post_comment(captcha_post_id, captcha_solution, comment_to_post, file_to_upload); if(post_result == PostResult::OK) { @@ -3724,9 +3751,17 @@ namespace QuickMedia { // TODO: Append posted comment to the thread so the user can see their posted comment. // TODO: Asynchronously update the thread periodically to show new comments. selected_file_for_upload.clear(); // TODO: Remove from here, this is async + has_post_timeout = thread_page->get_pass_id().empty(); + last_posted_time = get_unix_time_monotonic(); + + Path last_posted_time_filepath; + if(get_image_board_last_posted_filepath(plugin_name, last_posted_time_filepath)) + file_overwrite_atomic(last_posted_time_filepath, std::to_string(last_posted_time)); } else if(post_result == PostResult::TRY_AGAIN) { - show_notification("QuickMedia", "Invalid captcha, try again"); - // TODO: Check if the response contains a new captcha instead of requesting a new one manually + show_notification("QuickMedia", "Please wait before you post again"); + navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA; + } else if(post_result == PostResult::INVALID_CAPTCHA) { + show_notification("QuickMedia", "Invalid captcha, please try again"); navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA; } else if(post_result == PostResult::BANNED) { show_notification("QuickMedia", "Failed to post comment because you are banned", Urgency::CRITICAL); @@ -3755,16 +3790,19 @@ namespace QuickMedia { bool frame_skip_text_entry = false; - comment_input.on_submit_callback = [&frame_skip_text_entry, &comment_input, &navigation_stage, &comment_to_post, &captcha_post_id, &captcha_solved_time, &solved_captcha_ttl, &selected_file_for_upload, &thread_page](std::string text) -> bool { + comment_input.on_submit_callback = [&frame_skip_text_entry, &comment_input, &navigation_stage, &comment_to_post, &captcha_post_id, &selected_file_for_upload, &thread_page, &has_post_timeout, &seconds_until_post_again, &last_posted_time](std::string text) -> bool { if(text.empty() && selected_file_for_upload.empty()) return false; + + if(has_post_timeout && seconds_until_post_again - (get_unix_time_monotonic() - last_posted_time) > 0) + return false; comment_input.set_editable(false); frame_skip_text_entry = true; assert(navigation_stage == NavigationStage::REPLYING); comment_to_post = std::move(text); - if((!captcha_post_id.empty() && captcha_solved_time.getElapsedTime().asSeconds() < solved_captcha_ttl) || !thread_page->get_pass_id().empty()) { + if(!captcha_post_id.empty() || !thread_page->get_pass_id().empty()) { navigation_stage = NavigationStage::POSTING_COMMENT; } else if(thread_page->get_pass_id().empty()) { navigation_stage = NavigationStage::REQUESTING_CAPTCHA; @@ -3873,6 +3911,7 @@ namespace QuickMedia { if(task_result == TaskResult::TRUE) { attached_image_texture = std::make_unique(); if(attached_image_texture->loadFromImage(image)) { + attached_image_texture->setSmooth(true); attached_image_sprite.setTexture(*attached_image_texture, true); navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; } else { @@ -4048,29 +4087,34 @@ namespace QuickMedia { captcha_solution_text.setString(""); captcha_slide = 0.0f; - bool failed = false; - sf::Image image; - if(!load_image_from_memory(image, captcha_challenge.img_data.data(), captcha_challenge.img_data.size()) || !captcha_texture.loadFromImage(image)) { - show_notification("QuickMedia", "Failed to load captcha image", Urgency::CRITICAL); - failed = true; - } - captcha_challenge.img_data = std::string(); + if(captcha_post_id == "noop") { // TODO: Fix for other imageboard than 4chan in the future + captcha_solution.clear(); + navigation_stage = NavigationStage::POSTING_COMMENT; + } else { + bool failed = false; + sf::Image image; + if(!load_image_from_memory(image, captcha_challenge.img_data.data(), captcha_challenge.img_data.size()) || !captcha_texture.loadFromImage(image)) { + show_notification("QuickMedia", "Failed to load captcha image", Urgency::CRITICAL); + failed = true; + } + captcha_challenge.img_data = std::string(); - has_captcha_bg = !failed && !captcha_challenge.bg_data.empty(); - sf::Image bg_Image; - if(has_captcha_bg && (!load_image_from_memory(bg_Image, captcha_challenge.bg_data.data(), captcha_challenge.bg_data.size()) || !captcha_bg_texture.loadFromImage(bg_Image))) { - show_notification("QuickMedia", "Failed to load captcha image", Urgency::CRITICAL); - failed = true; - } - captcha_challenge.bg_data = std::string(); + has_captcha_bg = !failed && !captcha_challenge.bg_data.empty(); + sf::Image bg_Image; + if(has_captcha_bg && (!load_image_from_memory(bg_Image, captcha_challenge.bg_data.data(), captcha_challenge.bg_data.size()) || !captcha_bg_texture.loadFromImage(bg_Image))) { + show_notification("QuickMedia", "Failed to load captcha image", Urgency::CRITICAL); + failed = true; + } + captcha_challenge.bg_data = std::string(); - if(failed) { - navigation_stage = NavigationStage::VIEWING_COMMENTS; - } else { - navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA; - captcha_sprite.setTexture(captcha_texture, true); - if(has_captcha_bg) - captcha_bg_sprite.setTexture(captcha_bg_texture, true); + if(failed) { + navigation_stage = NavigationStage::VIEWING_COMMENTS; + } else { + navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA; + captcha_sprite.setTexture(captcha_texture, true); + if(has_captcha_bg) + captcha_bg_sprite.setTexture(captcha_bg_texture, true); + } } } else if(task_result == TaskResult::CANCEL) { comment_input.set_editable(false); @@ -4270,6 +4314,24 @@ namespace QuickMedia { comment_input.draw(window); thread_body->draw(window, body_pos, body_size); } + + if((navigation_stage == NavigationStage::REPLYING || navigation_stage == NavigationStage::VIEWING_COMMENTS) && has_post_timeout) { + int64_t time_left_until_post_again = seconds_until_post_again - (get_unix_time_monotonic() - last_posted_time); + if(time_left_until_post_again > 0) { + sf::RectangleShape time_left_bg(comment_input_shade.getSize()); + time_left_bg.setPosition(comment_input_shade.getPosition()); + time_left_bg.setFillColor(sf::Color(0, 0, 0, 100)); + window.draw(time_left_bg); + + sf::Text time_left_text("Wait " + std::to_string(time_left_until_post_again) + " second(s) before posting again", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(18 * get_ui_scale())); + time_left_text.setPosition(time_left_bg.getPosition() + + sf::Vector2f( + std::floor(time_left_bg.getSize().x * 0.5f - time_left_text.getLocalBounds().width * 0.5f), + std::floor(time_left_bg.getSize().y * 0.5f - time_left_text.getLocalBounds().height * 0.5f))); + window.draw(time_left_text); + } + } + AsyncImageLoader::get_instance().update(); window.display(); } -- cgit v1.2.3