aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-09-14 18:37:00 +0200
committerdec05eba <dec05eba@protonmail.com>2020-09-14 18:37:34 +0200
commitd6131b8ba482414be76f2478aea90bd7a4a2379b (patch)
tree9ea48087f0db0d7c956b87d09a93d8b3a335551a /src
parent6208faf754b76a7c9a806220c97484ea2e9c0a2e (diff)
Add support for 4chan pass
Diffstat (limited to 'src')
-rw-r--r--src/QuickMedia.cpp130
-rw-r--r--src/SearchBar.cpp57
-rw-r--r--src/plugins/Fourchan.cpp92
-rw-r--r--src/plugins/Mangadex.cpp2
4 files changed, 243 insertions, 38 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 051fdc7..b464956 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -318,7 +318,7 @@ namespace QuickMedia {
plugin_logo.setSmooth(true);
}
- search_bar = std::make_unique<SearchBar>(font, plugin_logo, search_placeholder);
+ search_bar = std::make_unique<SearchBar>(font, &plugin_logo, search_placeholder);
search_bar->text_autosearch_delay = current_plugin->get_search_delay();
while(window.isOpen()) {
@@ -421,8 +421,9 @@ namespace QuickMedia {
}
} else if(handle_searchbar && event.type == sf::Event::TextEntered) {
search_bar->onTextEntered(event.text.unicode);
+ } else if(handle_searchbar) {
+ search_bar->on_event(event);
}
- search_bar->on_event(event);
}
static std::string base64_encode(const std::string &data) {
@@ -471,7 +472,8 @@ namespace QuickMedia {
enum class SearchSuggestionTab {
ALL,
HISTORY,
- RECOMMENDED
+ RECOMMENDED,
+ LOGIN
};
// Returns relative time as a string (approximation)
@@ -734,10 +736,30 @@ namespace QuickMedia {
body_size = sf::Vector2f(body_width, window_size.y - body_padding_vertical - related_videos_text_height);
}
+ class LoginTab {
+ public:
+ LoginTab(sf::Font &font) :
+ username(std::make_unique<SearchBar>(font, nullptr, "Token...")),
+ password(std::make_unique<SearchBar>(font, nullptr, "PIN...", true))
+ {
+
+ }
+ std::unique_ptr<SearchBar> username;
+ std::unique_ptr<SearchBar> password;
+ };
+
+ struct Tab {
+ Body *body;
+ std::unique_ptr<LoginTab> login_tab;
+ SearchSuggestionTab tab;
+ sf::Text *text;
+ };
+
void Program::search_suggestion_page() {
std::string update_search_text;
bool search_running = false;
bool typing = false;
+ bool is_fourchan = current_plugin->name == "4chan";
std::string autocomplete_text;
bool autocomplete_running = false;
@@ -747,6 +769,8 @@ namespace QuickMedia {
sf::Text all_tab_text("All", font, tab_text_size);
sf::Text history_tab_text("History", font, tab_text_size);
sf::Text recommended_tab_text("Recommended", font, tab_text_size);
+ sf::Text login_tab_text("Login", font, tab_text_size);
+ SearchBar *focused_login_input = nullptr;
if(current_plugin->name == "youtube") {
recommended_body = std::make_unique<Body>(this, &font, &bold_font);
@@ -754,17 +778,48 @@ namespace QuickMedia {
fill_recommended_items_from_json(load_recommended_json(current_plugin), recommended_body->items);
}
- struct Tab {
- Body *body;
- SearchSuggestionTab tab;
- sf::Text *text;
+ std::vector<Tab> tabs;
+ int selected_tab = 0;
+
+ auto login_submit_callback = [this, &tabs, &selected_tab](const std::string &text) -> bool {
+ if(!tabs[selected_tab].body) {
+ std::string username = tabs[selected_tab].login_tab->username->get_text();
+ std::string password = tabs[selected_tab].login_tab->password->get_text();
+ if(current_plugin->name == "4chan") {
+ std::string response_msg;
+ PluginResult result = static_cast<Fourchan*>(current_plugin)->login(username, password, response_msg);
+ if(result == PluginResult::NET_ERR) {
+ show_notification("4chan", "Login failed!", Urgency::CRITICAL);
+ } else if(result == PluginResult::ERR) {
+ std::string desc = "Login failed, reason: ";
+ if(response_msg.empty())
+ desc += "Unknown";
+ else
+ desc += response_msg;
+ show_notification("4chan", desc, Urgency::CRITICAL);
+ } else if(result == PluginResult::OK) {
+ show_notification("4chan", "Successfully logged in!", Urgency::LOW);
+ selected_tab = 0;
+ }
+ }
+ }
+ return false;
};
- std::vector<Tab> tabs = { Tab{body, SearchSuggestionTab::ALL, &all_tab_text}, Tab{&history_body, SearchSuggestionTab::HISTORY, &history_tab_text} };
+ tabs.push_back(Tab{body, nullptr, SearchSuggestionTab::ALL, &all_tab_text});
+ tabs.push_back(Tab{&history_body, nullptr, SearchSuggestionTab::HISTORY, &history_tab_text});
if(recommended_body)
- tabs.push_back(Tab{recommended_body.get(), SearchSuggestionTab::RECOMMENDED, &recommended_tab_text});
+ tabs.push_back(Tab{recommended_body.get(), nullptr, SearchSuggestionTab::RECOMMENDED, &recommended_tab_text});
+ if(is_fourchan) {
+ tabs.push_back(Tab{nullptr, std::make_unique<LoginTab>(font), SearchSuggestionTab::LOGIN, &login_tab_text});
+ focused_login_input = tabs.back().login_tab->username.get();
- int selected_tab = 0;
+ tabs.back().login_tab->username->caret_visible = true;
+ tabs.back().login_tab->password->caret_visible = false;
+
+ tabs.back().login_tab->username->onTextSubmitCallback = login_submit_callback;
+ tabs.back().login_tab->password->onTextSubmitCallback = login_submit_callback;
+ }
plugin_get_watch_history(current_plugin, history_body.items);
if(current_plugin->name == "youtube")
@@ -878,14 +933,14 @@ namespace QuickMedia {
//int fps = 0;
while (current_page == Page::SEARCH_SUGGESTION) {
while (window.pollEvent(event)) {
- base_event_handler(event, Page::EXIT, false);
+ base_event_handler(event, Page::EXIT, false, true, tabs[selected_tab].body != nullptr);
if(event.type == sf::Event::Resized || event.type == sf::Event::GainedFocus)
redraw = true;
else if(event.type == sf::Event::KeyPressed) {
if(event.key.code == sf::Keyboard::Up) {
- tabs[selected_tab].body->select_previous_item();
+ if(tabs[selected_tab].body ) tabs[selected_tab].body->select_previous_item();
} else if(event.key.code == sf::Keyboard::Down) {
- tabs[selected_tab].body->select_next_item();
+ if(tabs[selected_tab].body) tabs[selected_tab].body->select_next_item();
} else if(event.key.code == sf::Keyboard::Escape) {
current_page = Page::EXIT;
exit_code = 1;
@@ -896,7 +951,22 @@ namespace QuickMedia {
selected_tab = std::min((int)tabs.size() - 1, selected_tab + 1);
search_bar->clear();
} else if(event.key.code == sf::Keyboard::Tab) {
- search_bar->set_to_autocomplete();
+ if(tabs[selected_tab].body) search_bar->set_to_autocomplete();
+ }
+ }
+
+ if(!tabs[selected_tab].body) {
+ if(event.type == sf::Event::TextEntered)
+ focused_login_input->onTextEntered(event.text.unicode);
+ focused_login_input->on_event(event);
+
+ if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Tab) {
+ focused_login_input->caret_visible = false;
+ if(focused_login_input == tabs[selected_tab].login_tab->username.get())
+ focused_login_input = tabs[selected_tab].login_tab->password.get();
+ else
+ focused_login_input = tabs[selected_tab].login_tab->username.get();
+ focused_login_input->caret_visible = true;
}
}
}
@@ -907,7 +977,8 @@ namespace QuickMedia {
get_body_dimensions(window_size, search_bar.get(), body_pos, body_size, true);
}
- search_bar->update();
+ if(tabs[selected_tab].body)
+ search_bar->update();
if(!update_search_text.empty() && !search_running) {
search_suggestion_future = std::async(std::launch::async, [this, update_search_text]() {
@@ -959,10 +1030,19 @@ namespace QuickMedia {
//window.draw(tab_spacing_rect);
const float width_per_tab = window_size.x / tabs.size();
- const float tab_y = tab_spacer_height + std::floor(search_bar->getBottomWithoutShadow() + tab_height * 0.5f - (tab_text_size + 5.0f) * 0.5f);
sf::RectangleShape tab_background(sf::Vector2f(std::floor(width_per_tab), tab_height));
- tabs[selected_tab].body->draw(window, body_pos, body_size);
+ float tab_vertical_offset = search_bar->getBottomWithoutShadow();
+ if(tabs[selected_tab].body) {
+ tabs[selected_tab].body->draw(window, body_pos, body_size);
+ } else {
+ tabs[selected_tab].login_tab->username->draw(window, false);
+ tabs[selected_tab].login_tab->password->draw(window, false);
+ tabs[selected_tab].login_tab->password->set_vertical_position(tabs[selected_tab].login_tab->username->getBottomWithoutShadow());
+ tab_vertical_offset = tabs[selected_tab].login_tab->username->getBottomWithoutShadow() + tabs[selected_tab].login_tab->password->getBottomWithoutShadow();
+ }
+ const float tab_y = tab_spacer_height + std::floor(tab_vertical_offset + tab_height * 0.5f - (tab_text_size + 5.0f) * 0.5f);
+
int i = 0;
for(Tab &tab : tabs) {
if(i == selected_tab) {
@@ -970,7 +1050,7 @@ namespace QuickMedia {
} else {
tab_background.setFillColor(tab_unselected_color);
}
- tab_background.setPosition(std::floor(i * width_per_tab), tab_spacer_height + std::floor(search_bar->getBottomWithoutShadow()));
+ tab_background.setPosition(std::floor(i * width_per_tab), tab_spacer_height + std::floor(tab_vertical_offset));
window.draw(tab_background);
const float center = (i * width_per_tab) + (width_per_tab * 0.5f);
tab.text->setPosition(std::floor(center - tab.text->getLocalBounds().width * 0.5f), tab_y);
@@ -979,10 +1059,11 @@ namespace QuickMedia {
}
tab_drop_shadow.setSize(sf::Vector2f(window_size.x, 5.0f));
- tab_drop_shadow.setPosition(0.0f, std::floor(search_bar->getBottomWithoutShadow() + tab_height));
+ tab_drop_shadow.setPosition(0.0f, std::floor(tab_vertical_offset + tab_height));
window.draw(tab_drop_shadow);
}
- search_bar->draw(window, false);
+ if(tabs[selected_tab].body)
+ search_bar->draw(window, false);
// fps++;
// if(tt.getElapsedTime().asMilliseconds() >= 1000) {
@@ -2251,7 +2332,7 @@ namespace QuickMedia {
// Instead of using search bar to searching, use it for commenting.
// TODO: Have an option for the search bar to be multi-line.
search_bar->onTextUpdateCallback = nullptr;
- search_bar->onTextSubmitCallback = [&post_comment_future, &navigation_stage, &request_new_google_captcha_challenge, &comment_to_post, &captcha_post_id, &captcha_solved_time, &post_comment](const std::string &text) -> bool {
+ search_bar->onTextSubmitCallback = [&post_comment_future, &navigation_stage, &request_new_google_captcha_challenge, &comment_to_post, &captcha_post_id, &captcha_solved_time, &post_comment, &image_board](const std::string &text) -> bool {
if(text.empty())
return false;
@@ -2262,8 +2343,13 @@ namespace QuickMedia {
post_comment();
return true;
});
- } else {
+ } else if(image_board->get_pass_id().empty()) {
request_new_google_captcha_challenge();
+ } else if(!image_board->get_pass_id().empty()) {
+ post_comment_future = std::async(std::launch::async, [&post_comment]() -> bool {
+ post_comment();
+ return true;
+ });
}
return true;
};
diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp
index 803eaee..62a0196 100644
--- a/src/SearchBar.cpp
+++ b/src/SearchBar.cpp
@@ -13,7 +13,7 @@ const float PADDING_HORIZONTAL = 50.0f;
const float padding_vertical = 20.0f;
namespace QuickMedia {
- SearchBar::SearchBar(sf::Font &font, sf::Texture &plugin_logo, const std::string &placeholder) :
+ SearchBar::SearchBar(sf::Font &font, sf::Texture *plugin_logo, const std::string &placeholder, bool input_masked) :
onTextUpdateCallback(nullptr),
onTextSubmitCallback(nullptr),
onTextBeginTypingCallback(nullptr),
@@ -27,7 +27,10 @@ namespace QuickMedia {
updated_search(false),
updated_autocomplete(false),
draw_logo(false),
- needs_update(true)
+ needs_update(true),
+ input_masked(input_masked),
+ caret_visible(true),
+ vertical_pos(0.0f)
{
text.setFillColor(text_placeholder_color);
autocomplete_text.setFillColor(text_placeholder_color);
@@ -37,8 +40,8 @@ namespace QuickMedia {
shade.setFillColor(sf::Color(0, 85, 119));
//background.setOutlineThickness(1.0f);
//background.setOutlineColor(sf::Color(13, 15, 17));
- if(plugin_logo.getNativeHandle() != 0)
- plugin_logo_sprite.setTexture(plugin_logo, true);
+ if(plugin_logo && plugin_logo->getNativeHandle() != 0)
+ plugin_logo_sprite.setTexture(*plugin_logo, true);
}
void SearchBar::draw(sf::RenderWindow &window, bool draw_shadow) {
@@ -53,13 +56,23 @@ namespace QuickMedia {
window.draw(background);
// TODO: Render starting from the character after text length
window.draw(autocomplete_text);
- window.draw(text);
- if(show_placeholder || text.getString().isEmpty())
- caret.setPosition(text.getPosition() - sf::Vector2f(2.0f, 0.0f));
- else
- caret.setPosition(text.findCharacterPos(text.getString().getSize()));
+ if(input_masked && !show_placeholder) {
+ std::string masked_str(text.getString().getSize(), '*');
+ sf::Text masked_text(std::move(masked_str), *text.getFont(), text.getCharacterSize());
+ masked_text.setPosition(text.getPosition());
+ window.draw(masked_text);
+ caret.setPosition(masked_text.findCharacterPos(masked_text.getString().getSize()));
+ } else {
+ window.draw(text);
+ if(show_placeholder || text.getString().isEmpty())
+ caret.setPosition(text.getPosition() - sf::Vector2f(2.0f, 0.0f));
+ else
+ caret.setPosition(text.findCharacterPos(text.getString().getSize()));
+ }
- window.draw(caret);
+ if(caret_visible)
+ window.draw(caret);
+
if(draw_logo)
window.draw(plugin_logo_sprite);
}
@@ -104,19 +117,20 @@ namespace QuickMedia {
sf::Vector2f texture_size_f(texture_size.x, texture_size.y);
sf::Vector2f new_size = wrap_to_size(texture_size_f, sf::Vector2f(200.0f, one_line_height));
plugin_logo_sprite.setScale(get_ratio(texture_size_f, new_size));
- plugin_logo_sprite.setPosition(25.0f, padding_vertical);
+ plugin_logo_sprite.setPosition(25.0f, padding_vertical + vertical_pos);
offset_x = 25.0f + new_size.x + 25.0f;
}
const float width = std::floor(window_size.x - offset_x - padding_horizontal);
background.setSize(sf::Vector2f(width, rect_height));
shade.setSize(sf::Vector2f(window_size.x, padding_vertical + rect_height + padding_vertical));
- caret.setSize(sf::Vector2f(2.0f, text.getLocalBounds().height + 12.0f));
+ caret.setSize(sf::Vector2f(2.0f, text.getCharacterSize() + 8.0f));
background_shadow.setSize(sf::Vector2f(window_size.x, 5.0f));
- background.setPosition(offset_x, padding_vertical);
- background_shadow.setPosition(0.0f, std::floor(shade.getSize().y));
- sf::Vector2f font_position(std::floor(offset_x + background_margin_horizontal), std::floor(padding_vertical + background_margin_vertical));
+ background.setPosition(offset_x, padding_vertical + vertical_pos);
+ shade.setPosition(0.0f, vertical_pos);
+ background_shadow.setPosition(0.0f, std::floor(shade.getSize().y + vertical_pos));
+ sf::Vector2f font_position(std::floor(offset_x + background_margin_horizontal), std::floor(padding_vertical + background_margin_vertical + vertical_pos));
autocomplete_text.setPosition(font_position);
text.setPosition(font_position);
}
@@ -221,6 +235,13 @@ namespace QuickMedia {
autocomplete_text.setString(text);
}
+ void SearchBar::set_vertical_position(float vertical_pos) {
+ if(std::abs(this->vertical_pos - vertical_pos) > 1.0f) {
+ this->vertical_pos = vertical_pos;
+ needs_update = true;
+ }
+ }
+
void SearchBar::clear_autocomplete_if_text_not_substring() {
const sf::String &text_str = text.getString();
const sf::String &autocomplete_str = autocomplete_text.getString();
@@ -258,4 +279,10 @@ namespace QuickMedia {
float SearchBar::getBottomWithoutShadow() const {
return shade.getSize().y;
}
+
+ std::string SearchBar::get_text() const {
+ if(show_placeholder)
+ return "";
+ return text.getString();
+ }
} \ No newline at end of file
diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp
index 42bb54e..5932144 100644
--- a/src/plugins/Fourchan.cpp
+++ b/src/plugins/Fourchan.cpp
@@ -45,7 +45,34 @@ namespace QuickMedia {
thread_list_update_thread.join();
}
+ // Returns empty string on failure to read cookie
+ static std::string get_pass_id_from_cookies_file(const Path &cookies_filepath) {
+ std::string file_content;
+ if(file_get_content(cookies_filepath, file_content) != 0)
+ return "";
+
+ size_t pass_id_index = file_content.find("pass_id");
+ if(pass_id_index == std::string::npos)
+ return "";
+
+ pass_id_index += 7;
+ size_t line_end = file_content.find('\n', pass_id_index);
+ if(line_end == std::string::npos)
+ line_end = file_content.size();
+
+ return strip(file_content.substr(pass_id_index, line_end - pass_id_index));
+ }
+
PluginResult Fourchan::get_front_page(BodyItems &result_items) {
+ if(pass_id.empty()) {
+ Path cookies_filepath;
+ if(get_cookies_filepath(cookies_filepath, name) != 0) {
+ fprintf(stderr, "Failed to get 4chan cookies filepath\n");
+ } else {
+ pass_id = get_pass_id_from_cookies_file(cookies_filepath);
+ }
+ }
+
#if 0
std::string server_response;
if(download_to_string(fourchan_url + "boards.json", server_response, {}, use_tor) != DownloadResult::OK)
@@ -543,6 +570,20 @@ namespace QuickMedia {
CommandArg{"-H", "Content-Type: multipart/form-data; boundary=---------------------------119561554312148213571335532670"},
CommandArg{"-H", "Origin: https://boards.4chan.org"}
};
+
+ if(pass_id.empty()) {
+ form_data.push_back(FormData{"g-recaptcha-response", captcha_id});
+ } else {
+ Path cookies_filepath;
+ if(get_cookies_filepath(cookies_filepath, name) != 0) {
+ fprintf(stderr, "Failed to get 4chan cookies filepath\n");
+ return PostResult::ERR;
+ } else {
+ additional_args.push_back(CommandArg{"-c", cookies_filepath.data});
+ additional_args.push_back(CommandArg{"-b", cookies_filepath.data});
+ }
+ }
+
std::vector<CommandArg> form_data_args = create_command_args_from_form_data(form_data);
additional_args.insert(additional_args.end(), form_data_args.begin(), form_data_args.end());
@@ -573,4 +614,55 @@ namespace QuickMedia {
}
return body_items;
}
+
+ PluginResult Fourchan::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, name) != 0) {
+ fprintf(stderr, "Failed to get 4chan cookies filepath\n");
+ return PluginResult::ERR;
+ }
+
+ std::vector<CommandArg> additional_args = {
+ CommandArg{"-F", "id=" + token},
+ CommandArg{"-F", "pin=" + pin},
+ CommandArg{"-F", "xhr=1"},
+ CommandArg{"-c", cookies_filepath.data}
+ };
+
+ std::string response;
+ if(download_to_string("https://sys.4chan.org/auth", response, std::move(additional_args), use_tor, true) != DownloadResult::OK)
+ return PluginResult::NET_ERR;
+
+ Json::Value json_root;
+ Json::CharReaderBuilder json_builder;
+ std::unique_ptr<Json::CharReader> json_reader(json_builder.newCharReader());
+ std::string json_errors;
+ if(!json_reader->parse(&response[0], &response[response.size()], &json_root, &json_errors) || !json_root.isObject()) {
+ fprintf(stderr, "Youtube get front page error: %s\n", json_errors.c_str());
+ 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;
+ }
+ }
+
+ const std::string& Fourchan::get_pass_id() const {
+ return pass_id;
+ }
} \ No newline at end of file
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 10f689f..4afa89b 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -241,7 +241,7 @@ namespace QuickMedia {
}
bool Mangadex::save_mangadex_cookies(const std::string &url, const std::string &cookie_filepath) {
- CommandArg cookie_arg = { "-c", std::move(cookie_filepath) };
+ CommandArg cookie_arg = { "-c", cookie_filepath };
std::string server_response;
if(download_to_string(url, server_response, {std::move(cookie_arg)}, use_tor, true) != DownloadResult::OK)
return false;