diff options
Diffstat (limited to 'src/SimpleHandlers.cpp')
-rw-r--r-- | src/SimpleHandlers.cpp | 171 |
1 files changed, 170 insertions, 1 deletions
diff --git a/src/SimpleHandlers.cpp b/src/SimpleHandlers.cpp index ee801d4..eba9331 100644 --- a/src/SimpleHandlers.cpp +++ b/src/SimpleHandlers.cpp @@ -2,7 +2,14 @@ #include "Response.hpp" -#include <fstream> +#include <array> +#include <cctype> +#include <memory> +#include <string> +#include <iomanip> +#include <sstream> +#include <iostream> +#include <stdexcept> using namespace Response; @@ -87,3 +94,165 @@ json Handler::sayHandler(const std::string &arguments, const std::string &sessio (void) payload; return simple_response(arguments, session, true); } + +std::string url_encode(std::string const &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (char c : value) { + // Keep alphanumeric and other accepted characters intact + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + escaped << c; + continue; + } + + // Any other characters are percent-encoded + escaped << std::uppercase; + escaped << '%' << std::setw(2) << int((unsigned char) c); + escaped << std::nouppercase; + } + + return escaped.str(); +} + +std::string exec(const char* cmd) { + std::array<char, 128> buffer{}; + std::string result; + std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} + +static std::string const base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +std::string base64_encode(unsigned char const *input, unsigned int const len) { + std::string ret; + size_t i = 0; + unsigned char bytes[3]; + unsigned char sextets[4]; + + while (i <= (len - 3)) { + bytes[0] = *(input++); + bytes[1] = *(input++); + bytes[2] = *(input++); + + sextets[0] = (bytes[0] & 0xfc) >> 2; // Cuts last two bits off of first byte + sextets[1] = ((bytes[0] & 0x03) << 4) + ((bytes[1] & 0xf0) + >> 4); // Takes last two bits from first byte and adds it to first 4 bits of 2nd byte + sextets[2] = ((bytes[1] & 0x0f) << 2) + ((bytes[2] & 0xc0) + >> 6); // Takes last 4 bits of 2nd byte and adds it to first 2 bits of third byte + sextets[3] = bytes[2] & 0x3f; // takes last 6 bits of third byte + + for (unsigned char sextet : sextets) { + ret += base64_chars[sextet]; + } + + i += 3; // increases to go to third byte + } + + if (i != len) { + size_t k = 0; + size_t j = len - i; // Find index of last byte + while (k < j) { // Sets first bytes + bytes[k] = *(input++); + ++k; + } + + while (j < 3) { // Set last bytes to 0x00 + bytes[j] = '\0'; + ++j; + } + + sextets[0] = (bytes[0] & 0xfc) >> 2; // Cuts last two bits off of first byte + sextets[1] = ((bytes[0] & 0x03) << 4) + ((bytes[1] & 0xf0) + >> 4); // Takes last two bits from first byte and adds it to first 4 bits of 2nd byte + sextets[2] = ((bytes[1] & 0x0f) << 2) + ((bytes[2] & 0xc0) + >> 6); // Takes last 4 bits of 2nd byte and adds it to first 2 bits of third byte + // No last one is needed, because if there were 4, then (i == len) == true + + for (j = 0; j < (len - i) + 1; ++j) { // Gets sextets that include data + ret += base64_chars[sextets[j]]; // Appends them to string + } + + while ((j++) < 4) // Appends remaining ='s + ret += '='; + + } + + return ret; +} + +std::string base64_encode_file(std::string const &path) { + std::vector<char> temp; + + std::ifstream infile; + infile.open(path, std::ios::binary); // Open file in binary mode + if (infile.is_open()) { + while (!infile.eof()) { + char c = (char) infile.get(); + temp.push_back(c); + } + infile.close(); + } else { + std::cerr << path << " Error: " << strerror(errno) << std::endl; + return "File could not be opened"; + } + std::string ret(temp.begin(), temp.end() - 1); + ret = base64_encode((unsigned const char *) ret.c_str(), ret.size()); + + return ret; +} + +json Handler::latexRenderHandler(std::string const &arguments, std::string const &session, void *payload) { + (void) payload; + + std::string png_file = exec("mktemp"); + png_file.pop_back(); // remove trailing new line + std::string jpg_file = png_file + ".jpg"; + + //std::cout << ("png_file " + png_file) << exec(("png_file " + png_file).c_str()) << std::endl; + + std::string result = exec(("curl -s 'https://latex.codecogs.com/png.latex?" + url_encode(arguments) + "' " + "-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0' " + "-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' " + "-H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'DNT: 1' -H 'Connection: keep-alive' " + "-H 'Upgrade-Insecure-Requests: 1' --output " + png_file).c_str()); + + //std::cout << ("png_file " + png_file) << exec(("png_file " + png_file).c_str()) << std::endl; + + if (!result.empty()) + return simple_response("Error getting image: " + result + "\n" + "Please report to an admin", session, false); + + // convert to jpg, mainly to loose background transparency ( :( ), so the images work in dark theme clients too + result = exec(("convert " + png_file + " " + jpg_file).c_str()); + + if (!result.empty()) + return simple_response("Error converting image: " + result + "\n" + "Please report to an admin", session, false); + + std::vector<json> reply_vec; + std::vector<json> annotations; + + annotations.emplace_back( + create_annotation( + Reply::AnnotationType::attachment, + "latex.png;png;" + base64_encode_file(jpg_file))); + + reply_vec.emplace_back(create_text("", std::move(annotations))); + + // single shot try to remove the files + std::remove(jpg_file.c_str()); + std::remove(png_file.c_str()); + + return create_response(std::move(reply_vec), session, true); +} |