diff options
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | include/QuickMedia.hpp | 7 | ||||
-rw-r--r-- | include/Tab.hpp | 11 | ||||
-rw-r--r-- | plugins/Fourchan.hpp | 37 | ||||
-rw-r--r-- | plugins/ImageBoard.hpp | 1 | ||||
-rw-r--r-- | plugins/Page.hpp | 11 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 227 | ||||
-rw-r--r-- | src/plugins/Fourchan.cpp | 159 | ||||
-rw-r--r-- | src/plugins/ImageBoard.cpp | 7 | ||||
-rw-r--r-- | src/plugins/Page.cpp | 9 |
10 files changed, 318 insertions, 155 deletions
@@ -44,7 +44,6 @@ Remove related videos that have already been watched (except the first related v Add F5 to refresh page. Allow choosing which translation/scanlation to use on mangadex. Right now it uses the latest one, which is most likely to be the best. Retry download if it fails. -Readd 4chan login page. Modify sfml to use GL_COMPRESSED_LUMINANCE and other texture compression modes (in mgl::Texture). This reduces memory usage by half. Decrease memory usage even further (mostly in matrix /sync when part of large rooms) by using rapidjson SAX style API to stream json string into SAX style parsing. Sometimes we fail to get images in mangadex, most common reason being that the manga is licensed and we can't view the manga on mangadex. QuickMedia should implement mangaplus and redirect us to mangaplus plugin to view the manga, or simply show that we cant view the manga because its licensed. @@ -221,4 +220,5 @@ Local anime bookmark. Local anime history. Replace youtube subscriptions page (rss) with youtube api. This allows us to get get video status for videos (and description). Add watch progress to youtube (and maybe also lbry and peertube?). -Ctrl+R set chapter to finished reading in chapters page.
\ No newline at end of file +Ctrl+R set chapter to finished reading in chapters page. +Zero clear password memory after use.
\ No newline at end of file diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index 6856f05..f93b47c 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -74,6 +74,11 @@ namespace QuickMedia { AsyncTask<BodyItems> next_page_future; std::string body_item_url_before_refresh; }; + + struct LoginInput { + std::string placeholder; + SearchBarType type; + }; class Program { public: @@ -83,6 +88,7 @@ namespace QuickMedia { std::unique_ptr<Body> create_body(bool plain_text_list = false, bool prefer_card_view = false); std::unique_ptr<SearchBar> create_search_bar(const std::string &placeholder, int search_delay); + void add_login_inputs(Tab *tab, std::vector<LoginInput> login_inputs); bool load_manga_content_storage(const char *service_name, const std::string &manga_title, const std::string &manga_url, const std::string &manga_id); @@ -215,5 +221,6 @@ namespace QuickMedia { std::unique_ptr<VideoPlayer> video_player; bool use_youtube_dl = false; int video_max_height = 0; + std::mutex login_inputs_mutex; }; } diff --git a/include/Tab.hpp b/include/Tab.hpp index ccb8c85..b277f88 100644 --- a/include/Tab.hpp +++ b/include/Tab.hpp @@ -1,5 +1,6 @@ #pragma once +#include <vector> #include <memory> namespace QuickMedia { @@ -7,9 +8,19 @@ namespace QuickMedia { class Page; class SearchBar; + struct LoginInputs { + std::vector<std::unique_ptr<SearchBar>> inputs; + int focused_input = 0; + bool needs_refresh = false; + }; + struct Tab { + Tab(std::unique_ptr<Body> body, std::unique_ptr<Page> page, std::unique_ptr<SearchBar> search_bar, LoginInputs login_inputs = {}) : + body(std::move(body)), page(std::move(page)), search_bar(std::move(search_bar)), login_inputs(std::move(login_inputs)) {} + std::unique_ptr<Body> body; std::unique_ptr<Page> page; // Only null when current page has |is_single_page()| set to true std::unique_ptr<SearchBar> search_bar; // Nullable + LoginInputs login_inputs; }; }
\ No newline at end of file diff --git a/plugins/Fourchan.hpp b/plugins/Fourchan.hpp index 05fb784..83fb293 100644 --- a/plugins/Fourchan.hpp +++ b/plugins/Fourchan.hpp @@ -11,24 +11,55 @@ namespace QuickMedia { void get_boards(BodyItems &result_items); const std::string resources_root; + std::string pass_id; + }; + + class FourchanLoginPage : public LazyFetchPage { + public: + FourchanLoginPage(Program *program, std::string title, FourchanBoardsPage *boards_page, std::vector<Tab> *tabs, size_t tab_index) : + LazyFetchPage(program), title(std::move(title)), boards_page(boards_page), tabs(tabs), tab_index(tab_index) {} + const char* get_title() const override { return title.c_str(); } + PluginResult submit(const SubmitArgs&, std::vector<Tab>&) override; + PluginResult lazy_fetch(BodyItems &result_items) override; + + bool submit_is_async() const override { return true; } + bool allow_submit_no_selection() const override { return true; } + bool lazy_fetch_is_loader() override { return true; } + void login_finish(); + + const LoginInputs *login_inputs; + private: + enum class LoggedIn { + Unknown, + Yes, + No + }; + + std::string title; + FourchanBoardsPage *boards_page; + std::vector<Tab> *tabs; + size_t tab_index; + LoggedIn logged_in = LoggedIn::Unknown; }; class FourchanThreadListPage : public LazyFetchPage { public: - FourchanThreadListPage(Program *program, std::string title, std::string board_id) : LazyFetchPage(program), title(std::move(title)), board_id(std::move(board_id)) {} + FourchanThreadListPage(Program *program, std::string title, std::string board_id, std::string pass_id) : + LazyFetchPage(program), title(std::move(title)), board_id(std::move(board_id)), pass_id(std::move(pass_id)) {} const char* get_title() const override { return title.c_str(); } PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; const std::string title; const std::string board_id; + std::string pass_id; }; class FourchanThreadPage : public ImageBoardThreadPage { public: - FourchanThreadPage(Program *program, std::string board_id, std::string thread_id) : ImageBoardThreadPage(program, std::move(board_id), std::move(thread_id)) {} + FourchanThreadPage(Program *program, std::string board_id, std::string thread_id, std::string pass_id) : + ImageBoardThreadPage(program, std::move(board_id), std::move(thread_id)), pass_id(std::move(pass_id)) {} - PluginResult login(const std::string &token, const std::string &pin, std::string &response_msg) override; PostResult post_comment(const std::string &captcha_id, const std::string &captcha_solution, const std::string &comment, const std::string &filepath = "") override; const std::string& get_pass_id() override; PluginResult request_captcha_challenge(ImageBoardCaptchaChallenge &challenge_response) override; diff --git a/plugins/ImageBoard.hpp b/plugins/ImageBoard.hpp index b6d84a1..157ac1c 100644 --- a/plugins/ImageBoard.hpp +++ b/plugins/ImageBoard.hpp @@ -32,7 +32,6 @@ namespace QuickMedia { void copy_to_clipboard(const BodyItem *body_item) override; bool autoplay_next_item() override { return true; } - virtual PluginResult login(const std::string &token, const std::string &pin, std::string &response_msg); // If |filepath| is empty then no file is uploaded virtual PostResult post_comment(const std::string &captcha_id, const std::string &captcha_solution, const std::string &comment, const std::string &filepath = "") = 0; virtual const std::string& get_pass_id(); diff --git a/plugins/Page.hpp b/plugins/Page.hpp index f5b4b3a..da6405c 100644 --- a/plugins/Page.hpp +++ b/plugins/Page.hpp @@ -197,4 +197,15 @@ namespace QuickMedia { Page *redirect_page; bool local_thumbnail; }; + + class LoginPage : public Page { + public: + LoginPage(Program *program) : Page(program) {} + bool submit_is_async() const override { return true; } + bool allow_submit_no_selection() const override { return true; } + void login_finish(); + bool logged_in() const; + private: + bool login_finished = false; + }; }
\ No newline at end of file diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 15a7f50..d49aa32 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1176,11 +1176,20 @@ namespace QuickMedia { tabs.push_back(Tab{std::move(categories_sukebei_body), std::make_unique<NyaaSiCategoryPage>(this, true), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "4chan") == 0) { auto boards_page = std::make_unique<FourchanBoardsPage>(this, resources_root); + FourchanBoardsPage *boards_page_ptr = boards_page.get(); + auto boards_body = create_body(); BodyItems body_items; boards_page->get_boards(body_items); boards_body->set_items(std::move(body_items)); tabs.push_back(Tab{std::move(boards_body), std::move(boards_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + + auto login_page = std::make_unique<FourchanLoginPage>(this, "4chan pass login", boards_page_ptr, &tabs, 1); + FourchanLoginPage *login_page_ptr = login_page.get(); + + tabs.push_back(Tab{ create_body(), std::move(login_page), nullptr, {} }); + login_page_ptr->login_inputs = &tabs.back().login_inputs; + page_loop(tabs); } else if(strcmp(plugin_name, "hotexamples") == 0) { auto body = create_body(); BodyItems body_items; @@ -1700,6 +1709,24 @@ namespace QuickMedia { return search_bar; } + void Program::add_login_inputs(Tab *tab, std::vector<LoginInput> login_inputs) { + if(login_inputs.empty()) + return; + + std::lock_guard<std::mutex> lock(login_inputs_mutex); + for(const LoginInput &login_input : login_inputs) { + auto search_bar = std::make_unique<SearchBar>(nullptr, &rounded_rectangle_shader, login_input.placeholder, login_input.type); + search_bar->padding_top = 0.0f; + search_bar->padding_bottom = 0.0f; + search_bar->padding_x = 0.0f; + search_bar->caret_visible = false; + tab->login_inputs.inputs.push_back(std::move(search_bar)); + } + + tab->login_inputs.inputs.front()->caret_visible = true; + tab->login_inputs.needs_refresh = true; + } + bool Program::load_manga_content_storage(const char *service_name, const std::string &manga_title, const std::string &manga_url, const std::string &manga_id) { Path content_storage_dir = get_storage_dir().join(service_name); this->manga_id = manga_id; @@ -1996,7 +2023,7 @@ namespace QuickMedia { const int selected_tab = ui_tabs.get_selected(); auto selected_item = tabs[selected_tab].body->get_selected_shared(); - if(!selected_item && search_text.empty()) + if(!selected_item && search_text.empty() && !tabs[selected_tab].page->allow_submit_no_selection()) return; if(tabs[selected_tab].page->allow_submit_no_selection() && (window.is_key_pressed(mgl::Keyboard::LControl) || window.is_key_pressed(mgl::Keyboard::RControl))) @@ -2267,6 +2294,22 @@ namespace QuickMedia { } } + RoundedRectangle login_inputs_background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, &rounded_rectangle_shader); + for(auto &tab : tabs) { + for(auto &login_input : tab.login_inputs.inputs) { + login_input->padding_top = 0.0f; + login_input->padding_bottom = 0.0f; + login_input->padding_x = 0.0f; + login_input->caret_visible = false; + } + + if(!tab.login_inputs.inputs.empty()) + tab.login_inputs.inputs.front()->caret_visible = true; + } + const float login_input_padding_x = std::floor(20.0f * get_config().scale * get_config().spacing_scale); + const float login_input_padding_y = std::floor(20.0f * get_config().scale * get_config().spacing_scale); + const float login_input_spacing_y = std::floor(20.0f * get_config().scale * get_config().spacing_scale); + mgl::Event event; mgl::Clock frame_timer; @@ -2340,8 +2383,22 @@ namespace QuickMedia { redraw = true; } else if(event.key.code == mgl::Keyboard::Tab && !event.key.control) { set_search_bar_to_body_item_text(tabs[selected_tab].body->get_selected(), tabs[selected_tab].search_bar.get()); + + std::lock_guard<std::mutex> lock(login_inputs_mutex); + if(!tabs[selected_tab].login_inputs.inputs.empty()) { + for(auto &login_input : tabs[selected_tab].login_inputs.inputs) { + login_input->caret_visible = false; + } + tabs[selected_tab].login_inputs.focused_input = (tabs[selected_tab].login_inputs.focused_input + 1) % tabs[selected_tab].login_inputs.inputs.size(); + tabs[selected_tab].login_inputs.inputs[tabs[selected_tab].login_inputs.focused_input]->caret_visible = true; + idle_active_handler(); + } } } + + std::lock_guard<std::mutex> lock(login_inputs_mutex); + if(!tabs[selected_tab].login_inputs.inputs.empty()) + tabs[selected_tab].login_inputs.inputs[tabs[selected_tab].login_inputs.focused_input]->on_event(window, event); } update_idle_state(); handle_x11_events(); @@ -2351,8 +2408,9 @@ namespace QuickMedia { const int selected_tab = ui_tabs.get_selected(); - if(redraw) { + if(redraw || tabs[selected_tab].login_inputs.needs_refresh) { redraw = false; + tabs[selected_tab].login_inputs.needs_refresh = false; if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->onWindowResize(window_size.to_vec2f()); // TODO: Dont show tabs if there is only one tab get_body_dimensions(window_size, tabs[selected_tab].search_bar.get(), body_pos, body_size, true); @@ -2382,6 +2440,20 @@ namespace QuickMedia { gradient_points[3].position.x = 0.0f; gradient_points[3].position.y = body_pos.y + gradient_height; } + + std::lock_guard<std::mutex> lock(login_inputs_mutex); + const int num_inputs = tabs[selected_tab].login_inputs.inputs.size(); + const int first_input_height = tabs[selected_tab].login_inputs.inputs.empty() ? 0 : tabs[selected_tab].login_inputs.inputs.front()->getBottomWithoutShadow(); + login_inputs_background.set_size(mgl::vec2f( + std::min((float)window_size.x, std::max(640.0f, window_size.x * 0.5f)), + num_inputs * first_input_height + login_input_padding_y * 2.0f + login_input_spacing_y * std::max(0, num_inputs - 1))); + login_inputs_background.set_position(window_size.to_vec2f() * 0.5f - login_inputs_background.get_size() * 0.5f); + + mgl::vec2f pos = login_inputs_background.get_position() + mgl::vec2f(login_input_padding_x, login_input_padding_y); + for(auto &login_input : tabs[selected_tab].login_inputs.inputs) { + login_input->set_position(pos); + pos.y += login_input->getBottomWithoutShadow() + login_input_spacing_y; + } } if(tab_associated_data[selected_tab].fetching_next_page_running) { @@ -2573,6 +2645,17 @@ namespace QuickMedia { window.clear(get_theme().background_color); page_loop_render(window, tabs, selected_tab, tab_associated_data[selected_tab], json_chapters, ui_tabs); + { + std::lock_guard<std::mutex> lock(login_inputs_mutex); + if(!tabs[selected_tab].login_inputs.inputs.empty()) { + login_inputs_background.draw(window); + for(auto &login_input : tabs[selected_tab].login_inputs.inputs) { + login_input->update(); + login_input->draw(window, login_inputs_background.get_size() - mgl::vec2f(login_input_padding_x * 2.0f, 0.0f), false); + } + } + } + if(tabs[selected_tab].body->get_num_items() > 0) { if(tabs[selected_tab].body->attach_side == AttachSide::TOP && !tabs[selected_tab].body->is_bottom_cut_off()) on_reached_end(); @@ -4817,116 +4900,62 @@ namespace QuickMedia { } } - void Program::chat_login_page() { - assert(strcmp(plugin_name, "matrix") == 0); - - SearchBar login_input(nullptr, &rounded_rectangle_shader, "Username", SearchBarType::Text); - SearchBar password_input(nullptr, &rounded_rectangle_shader, "Password", SearchBarType::Password); - SearchBar homeserver_input(nullptr, &rounded_rectangle_shader, "Homeserver", SearchBarType::Text); - - const int num_inputs = 3; - SearchBar *inputs[num_inputs] = { &login_input, &password_input, &homeserver_input }; - int focused_input = 0; - - RoundedRectangle background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, &rounded_rectangle_shader); + class MatrixLoginPage : public LoginPage { + public: + MatrixLoginPage(Program *program, std::string title, Matrix *matrix) : + LoginPage(program), title(std::move(title)), matrix(matrix) {} + const char* get_title() const override { return title.c_str(); } - auto text_submit_callback = [this, inputs](const std::string&) { - for(int i = 0; i < num_inputs; ++i) { - if(inputs[i]->get_text().empty()) { + PluginResult submit(const SubmitArgs&, std::vector<Tab>&) override { + for(const auto &login_input : login_inputs->inputs) { + if(login_input->get_text().empty()) { show_notification("QuickMedia", "All fields need to be filled in", Urgency::CRITICAL); - return; + return PluginResult::OK; } } - run_task_with_loading_screen([this, inputs](){ - std::string homeserver = inputs[2]->get_text(); - if(!string_starts_with(homeserver, "http://") && !string_starts_with(homeserver, "https://")) - homeserver = "https://" + homeserver; + std::string homeserver = login_inputs->inputs[2]->get_text(); + if(!string_starts_with(homeserver, "http://") && !string_starts_with(homeserver, "https://")) + homeserver = "https://" + homeserver; - std::string err_msg; - if(matrix->login(inputs[0]->get_text(), inputs[1]->get_text(), homeserver, err_msg) == PluginResult::OK) { - current_page = PageType::CHAT; - return true; - } else { - // TODO: Do a proper check for this - if(err_msg.find("Failed to parse") != std::string::npos) - show_notification("QuickMedia", "Failed to login, error: " + err_msg + ". Did you perhaps specify an invalid homeserver?", Urgency::CRITICAL); - else - show_notification("QuickMedia", "Failed to login, error: " + err_msg, Urgency::CRITICAL); - return false; - } - }); - }; - - for(int i = 0; i < num_inputs; ++i) { - inputs[i]->padding_top = 0.0f; - inputs[i]->padding_bottom = 0.0f; - inputs[i]->padding_x = 0.0f; - inputs[i]->caret_visible = false; - inputs[i]->onTextSubmitCallback = text_submit_callback; + std::string err_msg; + if(matrix->login(login_inputs->inputs[0]->get_text(), login_inputs->inputs[1]->get_text(), homeserver, err_msg) == PluginResult::OK) { + login_finish(); + return PluginResult::OK; + } else { + // TODO: Do a proper check for this + if(err_msg.find("Failed to parse") != std::string::npos) + show_notification("QuickMedia", "Failed to login, error: " + err_msg + ". Did you perhaps specify an invalid homeserver?", Urgency::CRITICAL); + else + show_notification("QuickMedia", "Failed to login, error: " + err_msg, Urgency::CRITICAL); + return PluginResult::OK; + } } - inputs[focused_input]->caret_visible = true; - const float padding_x = std::floor(20.0f * get_config().scale * get_config().spacing_scale); - const float padding_y = std::floor(20.0f * get_config().scale * get_config().spacing_scale); - const float spacing_y = std::floor(20.0f * get_config().scale * get_config().spacing_scale); - mgl::vec2f body_pos; - mgl::vec2f body_size; - bool redraw = true; - mgl::Event event; - - while (current_page == PageType::CHAT_LOGIN && window.is_open()) { - while (window.poll_event(event)) { - common_event_handler(event); - event_idle_handler(event); + const LoginInputs *login_inputs; + private: + std::string title; + Matrix *matrix; + }; - if(event.type == mgl::Event::Resized) { - window_size.x = event.size.width; - window_size.y = event.size.height; - redraw = true; - idle_active_handler(); - } else if(event.type == mgl::Event::GainedFocus) { - redraw = true; - } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Tab) { - for(int i = 0; i < num_inputs; ++i) { - inputs[i]->caret_visible = false; - } - focused_input = (focused_input + 1) % num_inputs; - inputs[focused_input]->caret_visible = true; - idle_active_handler(); - } - inputs[focused_input]->on_event(window, event); - } - update_idle_state(); - handle_x11_events(); + void Program::chat_login_page() { + assert(strcmp(plugin_name, "matrix") == 0); - if(current_page != PageType::CHAT_LOGIN) - break; + LoginInputs login_inputs; + login_inputs.inputs.push_back(std::make_unique<SearchBar>(nullptr, &rounded_rectangle_shader, "Username", SearchBarType::Text)); + login_inputs.inputs.push_back(std::make_unique<SearchBar>(nullptr, &rounded_rectangle_shader, "Password", SearchBarType::Password)); + login_inputs.inputs.push_back(std::make_unique<SearchBar>(nullptr, &rounded_rectangle_shader, "Homeserver", SearchBarType::Text)); - if(redraw) { - redraw = false; - get_body_dimensions(window_size, nullptr, body_pos, body_size); - background.set_size(mgl::vec2f( - std::min((float)window_size.x, std::max(640.0f, window_size.x * 0.5f)), - num_inputs * inputs[0]->getBottomWithoutShadow() + padding_y * 2.0f + spacing_y * std::max(0, num_inputs - 1))); - background.set_position(window_size.to_vec2f() * 0.5f - background.get_size() * 0.5f); + auto login_page = std::make_unique<MatrixLoginPage>(this, "Matrix login", matrix); + MatrixLoginPage *login_page_ptr = login_page.get(); - mgl::vec2f pos = background.get_position() + mgl::vec2f(padding_x, padding_y); - for(int i = 0; i < num_inputs; ++i) { - inputs[i]->set_position(pos); - pos.y += inputs[i]->getBottomWithoutShadow() + spacing_y; - } - } + std::vector<Tab> tabs; + tabs.push_back(Tab{ create_body(), std::move(login_page), nullptr, std::move(login_inputs) }); + login_page_ptr->login_inputs = &tabs.back().login_inputs; + page_loop(tabs); - window.clear(get_theme().background_color); - background.draw(window); - for(int i = 0; i < num_inputs; ++i) { - inputs[i]->update(); - inputs[i]->draw(window, background.get_size() - mgl::vec2f(padding_x * 2.0f, 0.0f), false); - } - AsyncImageLoader::get_instance().update(); - window.display(); - } + if(login_page_ptr->logged_in()) + current_page = PageType::CHAT; } struct ChatTab { diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp index d3d5a5c..51a3cc5 100644 --- a/src/plugins/Fourchan.cpp +++ b/src/plugins/Fourchan.cpp @@ -5,6 +5,7 @@ #include "../../include/NetUtils.hpp" #include "../../include/Notification.hpp" #include "../../external/cppcodec/base64_rfc4648.hpp" +#include "../../include/QuickMedia.hpp" #include <HtmlParser.h> #include <json/reader.h> #include <string.h> @@ -35,6 +36,69 @@ namespace QuickMedia { return strip(file_content.substr(pass_id_index, line_end - pass_id_index)); } + static bool is_logged_in() { + Path cookies_filepath; + if(get_cookies_filepath(cookies_filepath, SERVICE_NAME) != 0) { + fprintf(stderr, "Failed to get 4chan cookies filepath\n"); + return false; + } + + std::vector<CommandArg> additional_args = { + CommandArg{"-c", cookies_filepath.data}, + CommandArg{"-b", cookies_filepath.data} + }; + + std::string website_data; + if(download_to_string("https://sys.4chan.org/auth", website_data, additional_args) != DownloadResult::OK) { + show_notification("QuickMedia", "Failed to check if you are logged in", Urgency::CRITICAL); + return false; + } + + // TODO: Check if this is correct + return website_data.find("field-id") == std::string::npos; + } + + static bool login(Page *page, const std::string &token, const std::string &pin, std::string &pass_id, std::string &response_msg) { + response_msg.clear(); + + Path cookies_filepath; + if(get_cookies_filepath(cookies_filepath, SERVICE_NAME) != 0) { + fprintf(stderr, "Failed to get 4chan cookies filepath\n"); + return false; + } + + std::vector<CommandArg> additional_args = { + CommandArg{"--form-string", "id=" + token}, + CommandArg{"--form-string", "pin=" + pin}, + CommandArg{"--form-string", "xhr=1"}, + CommandArg{"-c", cookies_filepath.data} + }; + + Json::Value json_root; + DownloadResult result = page->download_json(json_root, "https://sys.4chan.org/auth", std::move(additional_args), true); + if(result != DownloadResult::OK) return false; + + if(!json_root.isObject()) + return false; + + const Json::Value &status_json = json_root["status"]; + if(!status_json.isNumeric()) + return false; + + const Json::Value &message_json = json_root["message"]; + if(message_json.isString()) + response_msg = message_json.asString(); + + if(status_json.asInt64() == 1) { + pass_id = get_pass_id_from_cookies_file(cookies_filepath); + if(pass_id.empty()) + return false; + return true; + } else { + return false; + } + } + struct CommentPiece { enum class Type { TEXT, @@ -230,7 +294,7 @@ namespace QuickMedia { } PluginResult FourchanBoardsPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) { - result_tabs.push_back(Tab{create_body(false, true), std::make_unique<FourchanThreadListPage>(program, args.title, args.url), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + result_tabs.push_back(Tab{create_body(false, true), std::make_unique<FourchanThreadListPage>(program, args.title, args.url, pass_id), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); return PluginResult::OK; } @@ -271,6 +335,56 @@ namespace QuickMedia { } } + PluginResult FourchanLoginPage::submit(const SubmitArgs &args, std::vector<Tab>&) { + if(args.url == "logout") { + Path cookies_filepath; + if(get_cookies_filepath(cookies_filepath, SERVICE_NAME) == 0) + remove(cookies_filepath.data.c_str()); + + boards_page->pass_id.clear(); + logged_in = LoggedIn::No; + needs_refresh = true; + return PluginResult::OK; + } + + for(const auto &login_input : login_inputs->inputs) { + if(login_input->get_text().empty()) { + show_notification("QuickMedia", "All fields need to be filled in", Urgency::CRITICAL); + return PluginResult::OK; + } + } + + std::string err_msg; + if(login(this, login_inputs->inputs[0]->get_text(), login_inputs->inputs[1]->get_text(), boards_page->pass_id, err_msg)) { + login_finish(); + return PluginResult::OK; + } else { + show_notification("QuickMedia", "Failed to login, error: " + err_msg, Urgency::CRITICAL); + return PluginResult::OK; + } + } + + PluginResult FourchanLoginPage::lazy_fetch(BodyItems &result_items) { + if(logged_in == LoggedIn::Yes || (logged_in == LoggedIn::Unknown && is_logged_in())) { + logged_in = LoggedIn::Yes; + auto logout_body_item = BodyItem::create("Logout"); + logout_body_item->url = "logout"; + result_items.push_back(std::move(logout_body_item)); + } else { + logged_in = LoggedIn::No; + program->add_login_inputs(&tabs->at(tab_index), { + { "Token", SearchBarType::Text }, + { "PIN", SearchBarType::Password } + }); + } + return PluginResult::OK; + } + + void FourchanLoginPage::login_finish() { + logged_in = LoggedIn::Yes; + needs_refresh = true; + } + // TODO: Merge with lazy fetch PluginResult FourchanThreadListPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) { Json::Value json_root; @@ -366,7 +480,7 @@ namespace QuickMedia { auto body = create_body(false); body->set_items(std::move(result_items)); - result_tabs.push_back(Tab{std::move(body), std::make_unique<FourchanThreadPage>(program, board_id, args.url), nullptr}); + result_tabs.push_back(Tab{std::move(body), std::make_unique<FourchanThreadPage>(program, board_id, args.url, pass_id), nullptr}); return PluginResult::OK; } @@ -454,47 +568,6 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult FourchanThreadPage::login(const std::string &token, const std::string &pin, std::string &response_msg) { - response_msg.clear(); - - Path cookies_filepath; - if(get_cookies_filepath(cookies_filepath, SERVICE_NAME) != 0) { - fprintf(stderr, "Failed to get 4chan cookies filepath\n"); - return PluginResult::ERR; - } - - std::vector<CommandArg> additional_args = { - CommandArg{"--form-string", "id=" + token}, - CommandArg{"--form-string", "pin=" + pin}, - CommandArg{"--form-string", "xhr=1"}, - CommandArg{"-c", cookies_filepath.data} - }; - - Json::Value json_root; - DownloadResult result = download_json(json_root, "https://sys.4chan.org/auth", std::move(additional_args), true); - if(result != DownloadResult::OK) return download_result_to_plugin_result(result); - - if(!json_root.isObject()) - return PluginResult::ERR; - - const Json::Value &status_json = json_root["status"]; - if(!status_json.isNumeric()) - return PluginResult::ERR; - - const Json::Value &message_json = json_root["message"]; - if(message_json.isString()) - response_msg = message_json.asString(); - - if(status_json.asInt64() == 1) { - pass_id = get_pass_id_from_cookies_file(cookies_filepath); - if(pass_id.empty()) - return PluginResult::ERR; - return PluginResult::OK; - } else { - return PluginResult::ERR; - } - } - static std::string file_get_filename(const std::string &filepath) { size_t index = filepath.rfind('/'); if(index == std::string::npos) diff --git a/src/plugins/ImageBoard.cpp b/src/plugins/ImageBoard.cpp index b027b9e..e1ee8fa 100644 --- a/src/plugins/ImageBoard.cpp +++ b/src/plugins/ImageBoard.cpp @@ -5,13 +5,6 @@ namespace QuickMedia { set_clipboard(body_item->get_title()); } - PluginResult ImageBoardThreadPage::login(const std::string &token, const std::string &pin, std::string &response_msg) { - (void)token; - (void)pin; - response_msg = "Login is not supported on this image board"; - return PluginResult::ERR; - } - const std::string& ImageBoardThreadPage::get_pass_id() { static std::string empty_str; return empty_str; diff --git a/src/plugins/Page.cpp b/src/plugins/Page.cpp index 654c983..f5867e3 100644 --- a/src/plugins/Page.cpp +++ b/src/plugins/Page.cpp @@ -137,4 +137,13 @@ namespace QuickMedia { return PluginResult::OK; } + + void LoginPage::login_finish() { + login_finished = true; + program->set_go_to_previous_page(); + } + + bool LoginPage::logged_in() const { + return login_finished; + } }
\ No newline at end of file |