aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorniklas <niklas@niklashalle.net>2020-11-12 14:47:18 +0100
committerniklas <niklas@niklashalle.net>2020-11-12 14:47:18 +0100
commit9a87a3500b835d1bbe29132fee3eef7ada973a5e (patch)
tree8c04de2e81355b69d2b01b5eb9e78294ad66de3f
parent0b0dba6c4b9d267c51b00f577db5bb7bf1726ac8 (diff)
parentee89b895d4f04baa1ea493fe916b3d499c4b4ec4 (diff)
downloadn_core-9a87a3500b835d1bbe29132fee3eef7ada973a5e.tar.gz
n_core-9a87a3500b835d1bbe29132fee3eef7ada973a5e.zip
Merge branch 'master' of git.niklashalle.net:n_core into HEAD
-rw-r--r--README.md9
-rw-r--r--include/AnnotationTypes.hpp1
-rw-r--r--include/SimpleHandlers.hpp2
-rw-r--r--src/Response.cpp2
-rw-r--r--src/SimpleHandlers.cpp171
-rw-r--r--src/main.cpp5
6 files changed, 186 insertions, 4 deletions
diff --git a/README.md b/README.md
index c132183..c59ecd4 100644
--- a/README.md
+++ b/README.md
@@ -74,10 +74,15 @@ No extra content.
The text should be printed underlined.
No extra content.
+##### `attachment`
+The text is the caption of an attached file.
+Extra content: String of content: "`file_name`;`file_type`;`file_content`", where `file_content` is base64 encoded.
+E.g.: "image.png;png;iVBORw0KGgoAAAANSUhEUgAAABwAAAAMBAMAAACD9cA8AAAALVBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAOrOgAAAADnRSTlMAELt2Mu9Eic1mIqtU3XZzvlQAAAAJcEhZcwAADsQAAA7EAZUrDhsAAACJSURBVAgdY2BgFGCAARCTyQDGAzP5HOBcENMVzgMx0zsqGXjmGDBwzpwFYjJoMjBsZkhjOMlQCWIyvGLgvMBQxVDhIABkMrAFMPDqTE5gWPv0AJDJwL6BQW4ByLTlD4BMBo4JMnJADscBhgAgk4HXwYDXgeEINwMbkDZg4LFZwFVzUoAt5ySICQDlYB5evEnwqgAAAABJRU5ErkJggg=="
+
##### `command`
-The text should be formated in a way to underlying platform of the bridge would recognize it (e.g. prefix with '/').
+The text should be formatted in a way to underlying platform of the bridge would recognize it (e.g. prefix with '/').
Extra content: the description of the command.
##### `link`
The text should be formatted as a link.
-Extra content: the alt text for the uri - the uri should be the text itself, so that clients ignoring this annotation still show the real uri.
+Extra content: the alt text for the uri - the uri should be the text itself, so that clients ignoring this annotation still show the real uri. \ No newline at end of file
diff --git a/include/AnnotationTypes.hpp b/include/AnnotationTypes.hpp
index 66d0b9d..8fba983 100644
--- a/include/AnnotationTypes.hpp
+++ b/include/AnnotationTypes.hpp
@@ -14,6 +14,7 @@ namespace Reply {
DECL_ENUM_ELEMENT(none),
DECL_ENUM_ELEMENT(bold),
DECL_ENUM_ELEMENT(italics),
+ DECL_ENUM_ELEMENT(attachment),
DECL_ENUM_ELEMENT(strikethrough),
DECL_ENUM_ELEMENT(underline),
DECL_ENUM_ELEMENT(command),
diff --git a/include/SimpleHandlers.hpp b/include/SimpleHandlers.hpp
index aef2372..1121685 100644
--- a/include/SimpleHandlers.hpp
+++ b/include/SimpleHandlers.hpp
@@ -11,5 +11,7 @@ namespace Handler {
json wikiHandler(std::string const &arguments, std::string const &session, void *payload);
+ json latexRenderHandler(std::string const &arguments, std::string const &session, void *payload);
+
json klingerHandler(std::string const &arguments, std::string const &session, void *payload);
} \ No newline at end of file
diff --git a/src/Response.cpp b/src/Response.cpp
index 286de52..53b0d8c 100644
--- a/src/Response.cpp
+++ b/src/Response.cpp
@@ -44,4 +44,4 @@ Response::json Response::create_response(std::vector<json> &&reply, std::string
response["session"] = session;
response["reply"] = std::move(reply);
return response;
-} \ No newline at end of file
+}
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);
+}
diff --git a/src/main.cpp b/src/main.cpp
index db05590..6b3ceb3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -40,6 +40,11 @@ int main() {
"{adlershof, nord, sued} Shows the daily menu of the mensa at various places.");
}});
regular_commands.emplace_back(
+ Handler::CommandHandler{"latex", Handler::latexRenderHandler, [createPlainDescriptionFromText]() {
+ return createPlainDescriptionFromText(
+ "Renders the given string to a png");
+ }});
+ regular_commands.emplace_back(
Handler::CommandHandler{"klinger", Handler::klingerHandler, [createPlainDescriptionFromText]() {
return createPlainDescriptionFromText("Greats in french. Bonjour!");
}});