282 lines
8.9 KiB
C++
282 lines
8.9 KiB
C++
/**
|
|
This file is a part of r0nk, atlas_moon, and rexy's matrix client
|
|
Copyright (C) 2019 rexy712
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "matrix/client.hpp"
|
|
#include "matrix/fat_strings.hpp"
|
|
#include "raii/filerd.hpp"
|
|
#include "raii/static_string.hpp"
|
|
#include "raii/rjp_ptr.hpp"
|
|
#include "raii/util.hpp"
|
|
|
|
#ifdef HAS_FREEIMAGE
|
|
# include "matrix/image_thumbnail_maker.hpp"
|
|
#endif
|
|
|
|
#ifdef HAS_FFMPEG
|
|
# define THUMBSIZE 500
|
|
# include "matrix/video_thumbnail_maker.hpp"
|
|
#endif
|
|
|
|
namespace matrix{
|
|
|
|
//Ctor
|
|
client::client(const std::shared_ptr<internal::session_info>& ses):
|
|
connection(ses){}
|
|
|
|
//local getter
|
|
const raii::rjp_string& client::access_token(void)const{
|
|
return m_ses->access_token;
|
|
}
|
|
const raii::rjp_string& client::userid(void)const{
|
|
return m_ses->userid;
|
|
}
|
|
const raii::string& client::useragent(void)const{
|
|
return m_ses->useragent;
|
|
}
|
|
|
|
//networked setter
|
|
void client::set_display_name(const raii::string_base& newname){
|
|
raii::string reply = _put_curl(raii::string("{\"displayname\":\"" + newname + "\"}"), m_ses->urls.displayname(), raii::curl_llist());
|
|
}
|
|
void client::set_profile_picture(const raii::string_base& media_url){
|
|
raii::string reply = _put_curl(raii::string("{\"avatar_url\":\"" + media_url + "\"}"), m_ses->urls.profile_picture(), raii::curl_llist());
|
|
}
|
|
|
|
//networked getter
|
|
raii::rjp_string client::get_display_name(void)const{
|
|
return _get_and_find(m_ses->urls.displayname(), "displayname"_ss);
|
|
}
|
|
raii::rjp_string client::get_profile_picture(void)const{
|
|
return _get_and_find(m_ses->urls.profile_picture(), "avatar_url"_ss);
|
|
}
|
|
raii::rjp_string client::room_alias_to_id(const raii::string_base& alias)const{
|
|
auto tmp = m_curl.encode(alias, alias.length());
|
|
return _get_and_find(raii::string(m_ses->urls.alias_lookup() + tmp), "room_id"_ss);
|
|
}
|
|
std::vector<raii::rjp_string> client::list_rooms(void)const{
|
|
std::vector<raii::rjp_string> ret;
|
|
raii::string reply = _get_curl(m_ses->urls.room_list());
|
|
if(!reply)
|
|
return ret;
|
|
|
|
raii::rjp_ptr root(rjp_parse(reply));
|
|
if(!root)
|
|
return ret;
|
|
|
|
RJP_search_res res = rjp_search_member(root.get(), "joined_rooms", 0);
|
|
if(!res.value)
|
|
return ret;
|
|
|
|
for(RJP_value* v = rjp_get_element(res.value);v;v = rjp_next_element(v)){
|
|
ret.emplace_back(v);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//room membership
|
|
raii::string client::create_room(const raii::string_base& name, const raii::string_base& alias)const{
|
|
raii::string postdata;
|
|
if(alias)
|
|
postdata = "{\"name\": \"" + raii::json_escape(name) + "\",\"room_alias_name\": \"" + raii::json_escape(alias) + "\"}";
|
|
else
|
|
postdata = "{\"name\": \"" + raii::json_escape(name) + "\"}";
|
|
|
|
return _post_curl(postdata, m_ses->urls.create_room(), raii::curl_llist());
|
|
}
|
|
roomcxn client::spawn_room(const raii::string_base& roomid)const{
|
|
return roomcxn(m_ses, roomid);
|
|
}
|
|
roomcxn client::spawn_room(raii::string&& roomid)const{
|
|
return roomcxn(m_ses, std::move(roomid));
|
|
}
|
|
|
|
//other network
|
|
void client::logout(void){
|
|
_get_curl(m_ses->urls.logout(m_ses->homeserver, m_ses->access_token));
|
|
}
|
|
|
|
|
|
//upload media
|
|
file_info client::upload_file(const raii::string_base& filename)const{
|
|
return upload_file(filename, raii::static_string());
|
|
}
|
|
file_info client::upload_file(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
raii::filerd fd(filename);
|
|
if(!fd) return {};
|
|
|
|
return file_info{_upload_file(fd, raii::curl_llist{}), alias ? alias : filename, {}, fd.length()};
|
|
}
|
|
image_info client::upload_image(const raii::string_base& filename)const{
|
|
return upload_image(filename, raii::static_string());
|
|
}
|
|
#ifdef HAS_FREEIMAGE
|
|
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
image_info ret;
|
|
FREE_IMAGE_FORMAT type = fipImage::identifyFIF(filename.get());
|
|
ret.mimetype = FreeImage_GetFIFMimeType(type);
|
|
raii::curl_llist header(raii::string("Content-Type: " + ret.mimetype));
|
|
|
|
{
|
|
raii::filerd fd(filename, "rb");
|
|
if(!fd)
|
|
return ret;
|
|
|
|
ret.filesize = fd.length();
|
|
ret.fileurl = _upload_file(fd, header);
|
|
}
|
|
fipImage image;
|
|
image.load(filename.get());
|
|
if(!image.isValid())
|
|
return {};
|
|
ret.width = image.getWidth();
|
|
ret.height = image.getHeight();
|
|
ret.filename = alias ? alias : filename;
|
|
std::vector<char> thumb_data = internal::_load_image_thumbnail_info(image, ret, type);
|
|
|
|
if(thumb_data.size())
|
|
ret.thumburl = _post_and_find(raii::static_string(thumb_data.data(), thumb_data.size()), m_ses->urls.file_upload(), header, "content_uri"_ss);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#else //HAS_FREEIMAGE
|
|
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
image_info ret = {};
|
|
ret = upload_file(filename, alias);
|
|
return ret;
|
|
}
|
|
#endif //HAS_FREEIMAGE
|
|
video_info client::upload_video(const raii::string_base& filename)const{
|
|
return upload_video(filename, raii::static_string());
|
|
}
|
|
audio_info client::upload_audio(const raii::string_base& filename)const{
|
|
return upload_audio(filename, raii::static_string());
|
|
}
|
|
#ifdef HAS_FFMPEG
|
|
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
internal::initlibav();
|
|
video_info ret = {};
|
|
|
|
libav::fmt::input_context in(filename);
|
|
if(!in) return {};
|
|
|
|
int stream_index = -1;
|
|
for(size_t i = 0;i < in->nb_streams;++i){
|
|
if(in->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
|
|
stream_index = i;
|
|
break;
|
|
}
|
|
}
|
|
if(stream_index == -1) return {};
|
|
|
|
libavcodec::context src_ctx(in, stream_index);
|
|
if(!src_ctx) return {};
|
|
|
|
src_ctx.open();
|
|
if(!src_ctx.is_open()) return {};
|
|
|
|
size_t thumbw, thumbh;
|
|
internal::_calc_video_thumbnail_dims(THUMB_SIZE, src_ctx->width, src_ctx->height, &thumbw, &thumbh);
|
|
libav::packet packet = internal::_create_video_thumbnail(in, src_ctx, stream_index, thumbw, thumbh);
|
|
|
|
if(packet){
|
|
raii::curl_llist header("Content-Type: image/jpeg");
|
|
ret.thumb_width = thumbw;
|
|
ret.thumb_height = thumbh;
|
|
ret.thumbsize = packet->size;
|
|
ret.thumburl = _post_and_find(raii::static_string((char*)packet->data, packet->size), m_ses->urls.file_upload(), header, "content_uri"_ss);
|
|
packet.reset();
|
|
}
|
|
|
|
ret.width = src_ctx->width;
|
|
ret.height = src_ctx->height;
|
|
ret.mimetype = internal::get_libav_mimetype(in);
|
|
src_ctx.reset();
|
|
in.reset();
|
|
|
|
raii::curl_llist header(raii::string("Content-Type:" + ret.mimetype));
|
|
header += "Transfer-Encoding: chunked";
|
|
raii::filerd fd(filename);
|
|
if(!fd) return {};
|
|
ret.filesize = fd.length();
|
|
ret.fileurl = _upload_file(fd, header);
|
|
ret.filename = alias ? alias : filename;
|
|
return ret;
|
|
}
|
|
audio_info client::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
audio_info ret = {};
|
|
|
|
libav::fmt::input_context in(filename);
|
|
ret.mimetype = internal::get_libav_mimetype(in);
|
|
in.reset();
|
|
|
|
raii::curl_llist header(raii::string("Content-Type:" + ret.mimetype));
|
|
raii::filerd fd(filename);
|
|
if(!fd) return {};
|
|
ret.filesize = fd.length();
|
|
ret.fileurl = _upload_file(fd, header);
|
|
ret.filename = alias ? alias : filename;
|
|
|
|
return ret;
|
|
}
|
|
#else //HAS_FFMPEG
|
|
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
video_info ret = {};
|
|
ret = upload_file(filename, alias);
|
|
return ret;
|
|
}
|
|
audio_info client::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
|
|
audio_info ret = {};
|
|
ret = upload_file(filename, alias);
|
|
return ret;
|
|
}
|
|
#endif //HAS_FFMPEG
|
|
|
|
|
|
/*******************************
|
|
Internal functions
|
|
********************************/
|
|
|
|
raii::rjp_string client::_upload_file(raii::filerd& fp, const raii::curl_llist& header)const{
|
|
raii::string fileurl;
|
|
m_curl.postreq();
|
|
m_curl.setopt(CURLOPT_POSTFIELDS, NULL);
|
|
m_curl.setopt(CURLOPT_READDATA, (void*)fp.get());
|
|
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)fp.length());
|
|
m_curl.setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)fp.length());
|
|
m_curl.seturl(m_ses->urls.file_upload());
|
|
m_curl.setheader(header);
|
|
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
|
m_curl.setopt(CURLOPT_WRITEDATA, &fileurl);
|
|
CURLcode cres = m_curl.perform();
|
|
m_curl.setopt(CURLOPT_READDATA, NULL);
|
|
if(cres != CURLE_OK)
|
|
return {};
|
|
|
|
if(!fileurl)
|
|
return {};
|
|
|
|
raii::rjp_ptr root(rjp_parse(fileurl));
|
|
if(!root)
|
|
return {};
|
|
RJP_search_res res = rjp_search_member(root.get(), "content_uri", 0);
|
|
|
|
return res.value;
|
|
}
|
|
}
|