aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Fourchan.cpp
blob: 0c3bffb4b3c04966251e0408395fb8e55c7655b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include "../../plugins/Fourchan.hpp"
#include <json/reader.h>
#include <string.h>

// API documentation: https://github.com/4chan/4chan-API

static const std::string fourchan_url = "https://a.4cdn.org/";
static const std::string fourchan_image_url = "https://i.4cdn.org/";

// Legacy recaptcha command: curl 'https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc' -H 'Referer: https://boards.4channel.org/' -H 'Cookie: CONSENT=YES'

/*
Answering recaptcha:
curl 'https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
-H 'Referer: https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'
-H 'Content-Type: application/x-www-form-urlencoded'
--data 'c=03AOLTBLQ66PjSi9s8S-R1vUS2Jgm-Z_ghEejvvjAaeF3FoR9MiM0zHhCxuertrCo7MAcFUEqcIg4l2WJzVtrJhJVLkncF12OzCaeIvbm46hgDZDZjLD89-LMn1Zs0TP37P-Hd4cuRG8nHuEBXc2ZBD8CVX-6HAs9VBgSmsgQeKF1PWm1tAMBccJhlh4rAOkpjzaEXMMGOe17N0XViwDYZxLGhe4H8IAG2KNB1fb4rz4YKJTPbL30_FvHw7zkdFtojjWiqVW0yCN6N192dhfd9oKz2r9pGRrR6N4AkkX-L0DsBD4yNK3QRsQn3dB1fs3JRZPAh1yqUqTQYhOaqdggyc1EwL8FZHouGRkHTOcCmLQjyv6zuhi6CJbg&response=1&response=4&response=5&response=7'
*/

/* Posting message:
curl 'https://sys.4chan.org/bant/post'
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
-H 'Referer: https://boards.4chan.org/'
-H 'Content-Type: multipart/form-data; boundary=---------------------------119561554312148213571335532670'
-H 'Origin: https://boards.4chan.org'
-H 'Cookie: __cfduid=d4bd4932e46bc3272fae4ce7a4e2aac511546800687; 4chan_pass=_SsBuZaATt3dIqfVEWlpemhU5XLQ6i9RC'
--data-binary $'-----------------------------119561554312148213571335532670\r\nContent-Disposition: form-data; name="resto"\r\n\r\n8640736\r\n-----------------------------119561554312148213571335532670\r\nContent-Disposition: form-data; name="com"\r\n\r\n>>8640771\r\nShe looks finnish\r\n-----------------------------119561554312148213571335532670\r\nContent-Disposition: form-data; name="mode"\r\n\r\nregist\r\n-----------------------------119561554312148213571335532670\r\nContent-Disposition: form-data; name="pwd"\r\n\r\n_SsBuZaATt3dIqfVEWlpemhU5XLQ6i9RC\r\n-----------------------------119561554312148213571335532670\r\nContent-Disposition: form-data; name="g-recaptcha-response"\r\n\r\n03AOLTBLS5lshp5aPj5pG6xdVMQ0pHuHxAtJoCEYuPLNKYlsRWNCPQegjB9zgL-vwdGMzjcT-L9iW4bnQ5W3TqUWHOVqtsfnx9GipLUL9o2XbC6r9zy-EEiPde7l6J0WcZbr9nh_MGcUpKl6RGaZoYB3WwXaDq74N5hkmEAbqM_CBtbAVVlQyPmemI2HhO2J6K0yFVKBrBingtIZ6-oXBXZ4jC4rT0PeOuVaH_gf_EBjTpb55ueaPmTbeLGkBxD4-wL1qA8F8h0D8c\r\n-----------------------------119561554312148213571335532670--\r\n'
*/

namespace QuickMedia {
    PluginResult Fourchan::get_front_page(BodyItems &result_items) {
        std::string server_response;
        if(download_to_string(fourchan_url + "boards.json", server_response) != 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(server_response.data(), server_response.data() + server_response.size(), &json_root, &json_errors)) {
            fprintf(stderr, "4chan front page json error: %s\n", json_errors.c_str());
            return PluginResult::ERR;
        }

        const Json::Value &boards = json_root["boards"];
        if(boards.isArray()) {
            for(const Json::Value &board : boards) {
                const Json::Value &board_id = board["board"]; // /g/, /a/, /b/ etc
                const Json::Value &board_title = board["title"];
                const Json::Value &board_description = board["meta_description"];
                if(board_id.isString() && board_title.isString() && board_description.isString()) {
                    std::string board_description_str = board_description.asString();
                    html_unescape_sequences(board_description_str);
                    auto body_item = std::make_unique<BodyItem>(board_title.asString());
                    body_item->url = board_id.asString();
                    result_items.emplace_back(std::move(body_item));
                }
            }
        }

        return PluginResult::OK;
    }

    SearchResult Fourchan::search(const std::string &url, BodyItems &result_items) {
        return SearchResult::OK;
    }

    SuggestionResult Fourchan::update_search_suggestions(const std::string &text, BodyItems &result_items) {
        return SuggestionResult::OK;
    }

    static bool string_ends_with(const std::string &str, const std::string &ends_with_str) {
        size_t len = ends_with_str.size();
        return len == 0 || (str.size() >= len && memcmp(&str[str.size() - len], ends_with_str.data(), len) == 0);
    }

    PluginResult Fourchan::get_content_list(const std::string &url, BodyItems &result_items) {
        std::string server_response;
        if(download_to_string(fourchan_url + url + "/catalog.json", server_response) != 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(server_response.data(), server_response.data() + server_response.size(), &json_root, &json_errors)) {
            fprintf(stderr, "4chan catalog json error: %s\n", json_errors.c_str());
            return PluginResult::ERR;
        }

        if(json_root.isArray()) {
            for(const Json::Value &page_data : json_root) {
                if(!page_data.isObject())
                    continue;

                const Json::Value &threads = page_data["threads"];
                if(!threads.isArray())
                    continue;

                for(const Json::Value &thread : threads) {
                    if(!thread.isObject())
                        continue;

                    const Json::Value &com = thread["com"];
                    if(!com.isString())
                        continue;

                    const Json::Value &thread_num = thread["no"];
                    if(!thread_num.isNumeric())
                        continue;

                    auto body_item = std::make_unique<BodyItem>(com.asString());
                    body_item->url = std::to_string(thread_num.asInt64());

                    const Json::Value &ext = thread["ext"];
                    const Json::Value &tim = thread["tim"];
                    if(tim.isNumeric() && ext.isString()) {
                        std::string ext_str = ext.asString();
                        if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg") {
                        } else {
                            fprintf(stderr, "TODO: Support file extension: %s\n", ext_str.c_str());
                        }
                        // "s" means small, that's the url 4chan uses for thumbnails.
                        // thumbnails always has .jpg extension even if they are gifs or webm.
                        body_item->thumbnail_url = fourchan_image_url + url + "/" + std::to_string(tim.asInt64()) + "s.jpg";
                    }
                    
                    result_items.emplace_back(std::move(body_item));
                }
            }
        }

        return PluginResult::OK;
    }

    PluginResult Fourchan::get_content_details(const std::string &list_url, const std::string &url, BodyItems &result_items) {
        std::string server_response;
        if(download_to_string(fourchan_url + list_url + "/thread/" + url + ".json", server_response) != 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(server_response.data(), server_response.data() + server_response.size(), &json_root, &json_errors)) {
            fprintf(stderr, "4chan thread json error: %s\n", json_errors.c_str());
            return PluginResult::ERR;
        }

        const Json::Value &posts = json_root["posts"];
        if(posts.isArray()) {
            for(const Json::Value &post : posts) {
                if(!post.isObject())
                    continue;

                const Json::Value &com = post["com"];
                if(!com.isString())
                    continue;

                const Json::Value &post_num = post["no"];
                if(!post_num.isNumeric())
                    continue;

                auto body_item = std::make_unique<BodyItem>(com.asString());
                body_item->url = std::to_string(post_num.asInt64());

                const Json::Value &ext = post["ext"];
                const Json::Value &tim = post["tim"];
                if(tim.isNumeric() && ext.isString()) {
                    std::string ext_str = ext.asString();
                    if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg") {
                    } else {
                        fprintf(stderr, "TODO: Support file extension: %s\n", ext_str.c_str());
                    }
                    // "s" means small, that's the url 4chan uses for thumbnails.
                    // thumbnails always has .jpg extension even if they are gifs or webm.
                    body_item->thumbnail_url = fourchan_image_url + list_url + "/" + std::to_string(tim.asInt64()) + "s.jpg";
                }
                
                result_items.emplace_back(std::move(body_item));
            }
        }

        return PluginResult::OK;
    }
}