From 3ac82fad6bc883d979090e8cd56f3611703d6e14 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 1 Mar 2022 15:13:35 +0100 Subject: Make login a bit generalized, readd 4chan login (not tested) --- src/QuickMedia.cpp | 227 +++++++++++++++++++++++++-------------------- src/plugins/Fourchan.cpp | 159 ++++++++++++++++++++++--------- src/plugins/ImageBoard.cpp | 7 -- src/plugins/Page.cpp | 9 ++ 4 files changed, 253 insertions(+), 149 deletions(-) (limited to 'src') 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(this, true), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "4chan") == 0) { auto boards_page = std::make_unique(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(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 login_inputs) { + if(login_inputs.empty()) + return; + + std::lock_guard lock(login_inputs_mutex); + for(const LoginInput &login_input : login_inputs) { + auto search_bar = std::make_unique(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 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 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 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 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&) 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(nullptr, &rounded_rectangle_shader, "Username", SearchBarType::Text)); + login_inputs.inputs.push_back(std::make_unique(nullptr, &rounded_rectangle_shader, "Password", SearchBarType::Password)); + login_inputs.inputs.push_back(std::make_unique(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(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 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 #include #include @@ -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 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 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 &result_tabs) { - result_tabs.push_back(Tab{create_body(false, true), std::make_unique(program, args.title, args.url), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); + result_tabs.push_back(Tab{create_body(false, true), std::make_unique(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&) { + 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 &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(program, board_id, args.url), nullptr}); + result_tabs.push_back(Tab{std::move(body), std::make_unique(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 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 -- cgit v1.2.3