aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-09-23 00:56:54 +0200
committerdec05eba <dec05eba@protonmail.com>2020-09-23 01:00:37 +0200
commit6b347e7310c501b826785e9639d962ba1d448b4b (patch)
tree6d84b547078d009565a75ac2df42423c06c578b7 /src/plugins
parenta8e0846a7c111a8d5b5cf8592ecb9b9bbd15ce26 (diff)
Add matrix image upload
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/FileManager.cpp35
-rw-r--r--src/plugins/Matrix.cpp152
2 files changed, 158 insertions, 29 deletions
diff --git a/src/plugins/FileManager.cpp b/src/plugins/FileManager.cpp
index fc6205c..c68bb94 100644
--- a/src/plugins/FileManager.cpp
+++ b/src/plugins/FileManager.cpp
@@ -18,23 +18,40 @@ namespace QuickMedia {
return "";
}
+ static std::filesystem::file_time_type file_get_filetime_or(const std::filesystem::directory_entry &path, std::filesystem::file_time_type default_value) {
+ try {
+ return path.last_write_time();
+ } catch(const std::filesystem::filesystem_error &err) {
+ return default_value;
+ }
+ }
+
PluginResult FileManager::get_files_in_directory(BodyItems &result_items) {
+ std::vector<std::filesystem::directory_entry> paths;
try {
for(auto &p : std::filesystem::directory_iterator(current_dir)) {
- auto body_item = std::make_unique<BodyItem>(p.path().filename().string());
- if(p.is_regular_file()) {
- if(is_image_ext(get_ext(p.path()))) {
- body_item->thumbnail_is_local = true;
- body_item->thumbnail_url = p.path().string();
- }
- }
- result_items.push_back(std::move(body_item));
+ paths.push_back(p);
}
- return PluginResult::OK;
} catch(const std::filesystem::filesystem_error &err) {
fprintf(stderr, "Failed to list files in directory %s, error: %s\n", current_dir.c_str(), err.what());
return PluginResult::ERR;
}
+
+ std::sort(paths.begin(), paths.end(), [](const std::filesystem::directory_entry &path1, std::filesystem::directory_entry &path2) {
+ return file_get_filetime_or(path1, std::filesystem::file_time_type::min()) > file_get_filetime_or(path2, std::filesystem::file_time_type::min());
+ });
+
+ for(auto &p : paths) {
+ auto body_item = std::make_unique<BodyItem>(p.path().filename().string());
+ // TODO: Check file magic number instead of extension?
+ if(p.is_regular_file() && is_image_ext(get_ext(p.path()))) {
+ body_item->thumbnail_is_local = true;
+ body_item->thumbnail_url = p.path().string();
+ }
+ result_items.push_back(std::move(body_item));
+ }
+
+ return PluginResult::OK;
}
bool FileManager::set_current_directory(const std::string &path) {
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index ff854c5..e806f39 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -1,5 +1,6 @@
#include "../../plugins/Matrix.hpp"
#include "../../include/Storage.hpp"
+#include "../../include/ImageUtils.hpp"
#include <json/reader.h>
#include <json/writer.h>
#include <fcntl.h>
@@ -557,7 +558,16 @@ namespace QuickMedia {
return result.str();
}
- PluginResult Matrix::post_message(const std::string &room_id, const std::string &text) {
+ static const char* message_type_to_request_msg_type_str(MessageType msgtype) {
+ switch(msgtype) {
+ case MessageType::TEXT: return "m.text";
+ case MessageType::IMAGE: return "m.image";
+ case MessageType::VIDEO: return "m.video";
+ }
+ return "m.text";
+ }
+
+ PluginResult Matrix::post_message(const std::string &room_id, const std::string &body, const std::string &url, MessageType msgtype, MessageInfo *info) {
char random_characters[18];
if(!generate_random_characters(random_characters, sizeof(random_characters)))
return PluginResult::ERR;
@@ -566,27 +576,40 @@ namespace QuickMedia {
std::string formatted_body;
bool contains_formatted_text = false;
- string_split(text, '\n', [&formatted_body, &contains_formatted_text](const char *str, size_t size){
- if(size > 0 && str[0] == '>') {
- std::string line(str, size);
- html_escape_sequences(line);
- formatted_body += "<font color=\"#789922\">";
- formatted_body += line;
- formatted_body += "</font>";
- contains_formatted_text = true;
- } else {
- formatted_body.append(str, size);
- }
- return true;
- });
+ if(msgtype == MessageType::TEXT) {
+ string_split(body, '\n', [&formatted_body, &contains_formatted_text](const char *str, size_t size){
+ if(size > 0 && str[0] == '>') {
+ std::string line(str, size);
+ html_escape_sequences(line);
+ formatted_body += "<font color=\"#789922\">";
+ formatted_body += line;
+ formatted_body += "</font>";
+ contains_formatted_text = true;
+ } else {
+ formatted_body.append(str, size);
+ }
+ return true;
+ });
+ }
Json::Value request_data(Json::objectValue);
- request_data["msgtype"] = "m.text";
- request_data["body"] = text;
+ request_data["msgtype"] = message_type_to_request_msg_type_str(msgtype);
+ request_data["body"] = body;
if(contains_formatted_text) {
request_data["format"] = "org.matrix.custom.html";
request_data["formatted_body"] = std::move(formatted_body);
}
+ if(msgtype == MessageType::IMAGE) {
+ if(info) {
+ Json::Value info_json(Json::objectValue);
+ info_json["size"] = info->size;
+ info_json["w"] = info->w;
+ info_json["h"] = info->h;
+ info_json["mimetype"] = info->mimetype;
+ request_data["info"] = std::move(info_json);
+ }
+ request_data["url"] = url;
+ }
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
@@ -599,12 +622,12 @@ namespace QuickMedia {
{ "--data-binary", Json::writeString(builder, request_data) }
};
- char url[512];
- snprintf(url, sizeof(url), "%s/_matrix/client/r0/rooms/%s/send/m.room.message/m%ld.%.*s", homeserver.c_str(), room_id.c_str(), time(NULL), (int)random_readable_chars.size(), random_readable_chars.c_str());
- fprintf(stderr, "Post message to |%s|\n", url);
+ char request_url[512];
+ snprintf(request_url, sizeof(request_url), "%s/_matrix/client/r0/rooms/%s/send/m.room.message/m%ld.%.*s", homeserver.c_str(), room_id.c_str(), time(NULL), (int)random_readable_chars.size(), random_readable_chars.c_str());
+ fprintf(stderr, "Post message to |%s|\n", request_url);
std::string server_response;
- if(download_to_string(url, server_response, std::move(additional_args), use_tor, true) != DownloadResult::OK)
+ if(download_to_string(request_url, server_response, std::move(additional_args), use_tor, true) != DownloadResult::OK)
return PluginResult::NET_ERR;
if(server_response.empty())
@@ -630,6 +653,95 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ // Returns empty string on error
+ static const char* file_get_filename(const std::string &filepath) {
+ size_t index = filepath.rfind('/');
+ if(index == std::string::npos)
+ return "";
+ const char *filename = filepath.c_str() + index + 1;
+ if(filename[0] == '\0')
+ return "";
+ return filename;
+ }
+
+ static const char* image_type_to_mimetype(ImageType image_type) {
+ switch(image_type) {
+ case ImageType::PNG: return "image/png";
+ case ImageType::GIF: return "image/gif";
+ case ImageType::JPG: return "image/jpeg";
+ }
+ return "application/octet-stream";
+ }
+
+ PluginResult Matrix::post_file(const std::string &room_id, const std::string &filepath) {
+ int image_width, image_height;
+ ImageType image_type;
+ if(!image_get_resolution(filepath, &image_width, &image_height, &image_type)) {
+ fprintf(stderr, "Failed to get resolution of image: %s. Only image uploads are currently supported\n", filepath.c_str());
+ return PluginResult::ERR;
+ }
+
+ const char *mimetype = image_type_to_mimetype(image_type);
+
+ // TODO: What if the file changes after this? is the file size really needed?
+ size_t file_size;
+ if(file_get_size(filepath, &file_size) != 0) {
+ fprintf(stderr, "Failed to get size of image: %s\n", filepath.c_str());
+ return PluginResult::ERR;
+ }
+
+ // TODO: Check server file limit first: GET https://glowers.club/_matrix/media/r0/config
+ // and also have a sane limit client-side.
+ if(file_size > 100 * 1024 * 1024) {
+ fprintf(stderr, "Upload file size it too large!, max size is currently 100mb\n");
+ return PluginResult::ERR;
+ }
+
+ std::vector<CommandArg> additional_args = {
+ { "-X", "POST" },
+ { "-H", std::string("content-type: ") + mimetype },
+ { "-H", "Authorization: Bearer " + access_token },
+ { "--data-binary", "@" + filepath }
+ };
+
+ const char *filename = file_get_filename(filepath);
+
+ char url[512];
+ snprintf(url, sizeof(url), "%s/_matrix/media/r0/upload?filename=%s", homeserver.c_str(), filename);
+ fprintf(stderr, "Upload url: |%s|\n", url);
+
+ std::string server_response;
+ if(download_to_string(url, server_response, std::move(additional_args), use_tor, true) != DownloadResult::OK)
+ return PluginResult::NET_ERR;
+
+ if(server_response.empty())
+ return PluginResult::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[0], &server_response[server_response.size()], &json_root, &json_errors)) {
+ fprintf(stderr, "Matrix upload response parse error: %s\n", json_errors.c_str());
+ return PluginResult::ERR;
+ }
+
+ if(!json_root.isObject())
+ return PluginResult::ERR;
+
+ const Json::Value &content_uri_json = json_root["content_uri"];
+ if(!content_uri_json.isString())
+ return PluginResult::ERR;
+
+ fprintf(stderr, "Matrix upload, response content uri: %s\n", content_uri_json.asCString());
+ MessageInfo message_info;
+ message_info.size = file_size;
+ message_info.w = image_width;
+ message_info.h = image_height;
+ message_info.mimetype = mimetype;
+ return post_message(room_id, filename, content_uri_json.asString(), MessageType::IMAGE, &message_info);
+ }
+
static std::string parse_login_error_response(std::string json_str) {
if(json_str.empty())
return "Unknown error";