Restructured the matrix codebase and fixed a few segfault issues in the raii strings
This commit is contained in:
parent
02a43ba1b9
commit
7816c54230
@ -19,298 +19,11 @@
|
||||
#ifndef MATRIX_HPP
|
||||
#define MATRIX_HPP
|
||||
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/filerd.hpp"
|
||||
#include "raii/rjp_ptr.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "matrix/client.hpp"
|
||||
#include "matrix/syncer.hpp"
|
||||
#include "matrix/session.hpp"
|
||||
|
||||
#define HAS_FREEIMAGE
|
||||
#define HAS_FFMPEG
|
||||
|
||||
namespace matrix{
|
||||
struct auth_data{
|
||||
raii::rjp_string name;
|
||||
raii::rjp_string pass;
|
||||
raii::rjp_string homeserver;
|
||||
raii::rjp_string access_token;
|
||||
|
||||
operator bool(void)const{
|
||||
return (name && pass && homeserver);
|
||||
}
|
||||
};
|
||||
|
||||
//upload info structs
|
||||
struct file_info{
|
||||
raii::rjp_string fileurl;
|
||||
raii::string filename;
|
||||
raii::string mimetype;
|
||||
size_t filesize;
|
||||
};
|
||||
struct image_info : file_info{
|
||||
size_t width;
|
||||
size_t height;
|
||||
|
||||
raii::rjp_string thumburl;
|
||||
size_t thumb_width;
|
||||
size_t thumb_height;
|
||||
size_t thumbsize;
|
||||
};
|
||||
struct video_info : public image_info{};
|
||||
struct audio_info : public file_info{};
|
||||
|
||||
//message handling structs
|
||||
//enumerate message type but also give a string representation
|
||||
class msgtype{
|
||||
private:
|
||||
const char* m_str;
|
||||
const int m_num;
|
||||
public:
|
||||
constexpr msgtype(const char* s, int n):
|
||||
m_str(s), m_num(n){}
|
||||
constexpr msgtype(const msgtype&) = default;
|
||||
~msgtype(void) = default;
|
||||
constexpr msgtype& operator=(const msgtype&) = default;
|
||||
|
||||
constexpr bool operator==(const msgtype& m){
|
||||
return m_num == m.m_num;
|
||||
}
|
||||
constexpr bool operator!=(const msgtype& m){
|
||||
return m_num != m.m_num;
|
||||
}
|
||||
constexpr const char* str(void)const{
|
||||
return m_str;
|
||||
}
|
||||
constexpr operator int(void)const{
|
||||
return m_num;
|
||||
}
|
||||
};
|
||||
//class enumeration
|
||||
struct msg{
|
||||
private:
|
||||
enum _types{
|
||||
_text, _audio, _video, _file, _image, _other
|
||||
};
|
||||
public:
|
||||
constexpr static msgtype text = msgtype("text", _text);
|
||||
constexpr static msgtype audio = msgtype("audio", _audio);
|
||||
constexpr static msgtype video = msgtype("video", _video);
|
||||
constexpr static msgtype file = msgtype("file", _file);
|
||||
constexpr static msgtype image = msgtype("image", _image);
|
||||
constexpr static msgtype other = msgtype("other", _other);
|
||||
|
||||
constexpr static const msgtype& from_str(const char* str){
|
||||
if(!strcmp(str, "m.text")){
|
||||
return text;
|
||||
}else if(!strcmp(str, "m.audio")){
|
||||
return audio;
|
||||
}else if(!strcmp(str, "m.video")){
|
||||
return video;
|
||||
}else if(!strcmp(str, "m.file")){
|
||||
return file;
|
||||
}else if(!strcmp(str, "m.image")){
|
||||
return image;
|
||||
}else{
|
||||
return other;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct event_info
|
||||
{
|
||||
const raii::rjp_string roomid;
|
||||
const raii::rjp_string sender;
|
||||
const raii::rjp_string eventid;
|
||||
const raii::rjp_string eventtype;
|
||||
const int serverts;
|
||||
const int age;
|
||||
};
|
||||
struct msg_info : public event_info
|
||||
{
|
||||
const msgtype type = msg::other;
|
||||
const raii::rjp_string body;
|
||||
};
|
||||
struct membership_info : public event_info
|
||||
{
|
||||
const raii::rjp_string recipient;
|
||||
};
|
||||
|
||||
//main class
|
||||
class client
|
||||
{
|
||||
private:
|
||||
class mat_url_list
|
||||
{
|
||||
private:
|
||||
raii::string m_create_room;
|
||||
raii::string m_file_upload;
|
||||
raii::string m_room_list;
|
||||
raii::string m_login;
|
||||
raii::string m_alias_lookup;
|
||||
raii::string m_whoami;
|
||||
raii::string m_displayname;
|
||||
raii::string m_profile_picture;
|
||||
|
||||
public:
|
||||
mat_url_list(void) = default;
|
||||
mat_url_list(const raii::string_base& homeserver);
|
||||
mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid);
|
||||
mat_url_list(const mat_url_list&) = default;
|
||||
mat_url_list(mat_url_list&&) = default;
|
||||
mat_url_list& operator=(const mat_url_list&) = default;
|
||||
mat_url_list& operator=(mat_url_list&&) = default;
|
||||
|
||||
const raii::string& create_room(void)const;
|
||||
const raii::string& file_upload(void)const;
|
||||
const raii::string& room_list(void)const;
|
||||
const raii::string& login(void)const;
|
||||
const raii::string& alias_lookup(void)const;
|
||||
const raii::string& whoami(void)const;
|
||||
const raii::string& displayname(void)const;
|
||||
const raii::string& profile_picture(void)const;
|
||||
raii::string logout(const raii::string_base& homeserver, const raii::string_base& access_token)const;
|
||||
raii::string join_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string leave_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const;
|
||||
raii::string read_receipt(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::string send(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string redact(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::string power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string presence(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid)const;
|
||||
raii::string typing(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& userid)const;
|
||||
raii::string kick(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string ban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string unban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string invite(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string room_members(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string password(const raii::string_base& homeserver, const raii::string_base& access_token)const;
|
||||
|
||||
|
||||
void repopulate_accesstoken(const raii::string_base& homeserver, const raii::string_base& access_token);
|
||||
void repopulate_userid(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid);
|
||||
void repopulate(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid);
|
||||
void invalidate_accesstoken(void);
|
||||
|
||||
private:
|
||||
void _initial_populate(const raii::string_base& homeserver);
|
||||
static constexpr const char* s_proto = "https://";
|
||||
static constexpr const char* s_revision = "r0";
|
||||
};
|
||||
|
||||
private:
|
||||
mutable raii::curler m_curl; //https access
|
||||
raii::string m_useragent; //useragent to identify our application
|
||||
raii::string m_homeserver; //name of our homeserver
|
||||
raii::rjp_string m_access_token; //authentication
|
||||
raii::rjp_string m_userid; //userid including homeserver
|
||||
mat_url_list m_urls;
|
||||
|
||||
raii::rjp_string m_next_batch; //string which tracks where we are in the server history
|
||||
|
||||
std::function<void(const client&,const msg_info&)> m_message_callback;
|
||||
std::function<void(const client&, const membership_info&)> m_membership_callback;
|
||||
std::function<void(const client&, const raii::rjp_ptr&)> m_raw_callback;
|
||||
|
||||
public:
|
||||
client(const auth_data& a, const raii::string_base& useragent);
|
||||
client(const auth_data& a, raii::string&& useragent);
|
||||
client(const client& b) = default;
|
||||
client(client&& b) = default;
|
||||
~client(void) = default;
|
||||
|
||||
client& operator=(const client&) = default;
|
||||
client& operator=(client&&) = default;
|
||||
|
||||
//local getter
|
||||
const raii::rjp_string& access_token(void)const;
|
||||
const raii::rjp_string& userid(void)const;
|
||||
const raii::string& useragent(void)const;
|
||||
|
||||
//local setter
|
||||
void set_useragent(const raii::string_base&);
|
||||
void set_useragent(raii::string&&);
|
||||
|
||||
//networked setter
|
||||
void set_display_name(const raii::string_base&);
|
||||
void set_profile_picture(const raii::string_base&);
|
||||
|
||||
|
||||
//networked getter
|
||||
raii::rjp_string get_display_name(void)const;
|
||||
raii::rjp_string get_profile_picture(void)const;
|
||||
raii::rjp_string room_alias_to_id(const raii::string_base& alias)const;
|
||||
std::vector<raii::rjp_string> list_rooms(void)const;
|
||||
|
||||
//other networked operations
|
||||
raii::string create_room(const raii::string_base& name, const raii::string_base& alias)const;
|
||||
bool join_room(const raii::string_base& roomid)const;
|
||||
bool leave_room(const raii::string_base& roomid)const;
|
||||
|
||||
bool accept_invite(const membership_info& invite)const;
|
||||
bool reject_invite(const membership_info& invite)const;
|
||||
|
||||
//upload media
|
||||
file_info upload_file(const raii::string_base& filename)const;
|
||||
file_info upload_file(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
image_info upload_image(const raii::string_base& filename)const;
|
||||
image_info upload_image(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
video_info upload_video(const raii::string_base& filename)const;
|
||||
video_info upload_video(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
audio_info upload_audio(const raii::string_base& filename)const;
|
||||
audio_info upload_audio(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
|
||||
//send messages
|
||||
raii::rjp_string send_image(const raii::string_base& room, const image_info& image)const;
|
||||
raii::rjp_string send_video(const raii::string_base& room, const video_info& video)const;
|
||||
raii::rjp_string send_message(const raii::string_base& room, const raii::string_base& text)const;
|
||||
raii::rjp_string send_audio(const raii::string_base& room, const audio_info& audio)const;
|
||||
raii::rjp_string send_file(const raii::string_base& room, const file_info& file)const;
|
||||
void send_typing(const raii::string_base& room, bool active, int timeout = 5000)const;
|
||||
|
||||
raii::rjp_string redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const;
|
||||
raii::rjp_string redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
|
||||
template<class Func>
|
||||
void set_message_callback(Func&& f){
|
||||
m_message_callback = std::forward<Func>(f);
|
||||
}
|
||||
template<class Func>
|
||||
void set_membership_callback(Func&& f){
|
||||
m_membership_callback = std::forward<Func>(f);
|
||||
}
|
||||
template<class Func>
|
||||
void set_raw_callback(Func&& f){
|
||||
m_raw_callback = std::forward<Func>(f);
|
||||
}
|
||||
raii::string sync(size_t timeout);
|
||||
void logout(void);
|
||||
|
||||
protected:
|
||||
void _handle_membership_events(RJP_value* rooms);
|
||||
void _handle_other_membership(RJP_value* join);
|
||||
void _handle_invites(RJP_value* invites);
|
||||
void _handle_messages(RJP_value* messages);
|
||||
void _send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::rjp_string _upload_file(raii::filerd& fp, const raii::curl_llist& header)const;
|
||||
raii::rjp_string _send_message(const raii::string_base& room, const raii::string_base& msg)const;
|
||||
static size_t _post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
|
||||
raii::string _get_curl(const raii::string_base& url)const;
|
||||
raii::string _post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
|
||||
raii::string _put_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
|
||||
raii::rjp_string _post_and_find(const raii::string_base& data, const raii::string_base& url, const raii::curl_llist& header, const raii::string_base& target)const;
|
||||
raii::rjp_string _get_and_find(const raii::string_base& url, const raii::string_base& search)const;
|
||||
raii::rjp_string _curl_reply_search(const raii::string_base& reply, const raii::string_base& search)const;
|
||||
void _set_curl_defaults(void)const;
|
||||
raii::string _request_access_token(const auth_data& a)const;
|
||||
void _get_new_access_token(const auth_data& a);
|
||||
void _acquire_access_token(const auth_data& a);
|
||||
};
|
||||
|
||||
|
||||
auth_data parse_auth_data(RJP_value* root);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
40
include/matrix/auth.hpp
Normal file
40
include/matrix/auth.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_AUTH_HPP
|
||||
#define MATRIX_AUTH_HPP
|
||||
|
||||
#include "raii/rjp_string.hpp"
|
||||
|
||||
namespace matrix{
|
||||
|
||||
struct auth_data{
|
||||
raii::string name;
|
||||
raii::string pass;
|
||||
raii::string homeserver;
|
||||
raii::string useragent;
|
||||
raii::string access_token;
|
||||
|
||||
operator bool(void)const{
|
||||
return (name && pass && homeserver);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
107
include/matrix/client.hpp
Normal file
107
include/matrix/client.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_CLIENT_HPP
|
||||
#define MATRIX_CLIENT_HPP
|
||||
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/filerd.hpp"
|
||||
#include "matrix/session_info.hpp"
|
||||
#include "matrix/upload_info.hpp"
|
||||
#include "matrix/event_info.hpp"
|
||||
#include "matrix/client_base.hpp"
|
||||
#include <vector> //vector
|
||||
#include <memory> //shared_ptr
|
||||
#include <cstdlib> //size_t
|
||||
|
||||
namespace matrix{
|
||||
class session;
|
||||
//main class
|
||||
class client : public internal::client_base
|
||||
{
|
||||
friend class ::matrix::session;
|
||||
private:
|
||||
const std::shared_ptr<internal::session_info> m_ses;
|
||||
|
||||
public:
|
||||
client(std::shared_ptr<internal::session_info>&);
|
||||
client(const client& b) = default;
|
||||
client(client&& b) = default;
|
||||
~client(void) = default;
|
||||
|
||||
client& operator=(const client&) = default;
|
||||
client& operator=(client&&) = default;
|
||||
|
||||
//local getter
|
||||
const raii::rjp_string& access_token(void)const;
|
||||
const raii::rjp_string& userid(void)const;
|
||||
const raii::string& useragent(void)const;
|
||||
|
||||
//networked setter
|
||||
void set_display_name(const raii::string_base&);
|
||||
void set_profile_picture(const raii::string_base&);
|
||||
|
||||
|
||||
//networked getter
|
||||
raii::rjp_string get_display_name(void)const;
|
||||
raii::rjp_string get_profile_picture(void)const;
|
||||
raii::rjp_string room_alias_to_id(const raii::string_base& alias)const;
|
||||
std::vector<raii::rjp_string> list_rooms(void)const;
|
||||
|
||||
//room membership
|
||||
raii::string create_room(const raii::string_base& name, const raii::string_base& alias)const;
|
||||
bool join_room(const raii::string_base& roomid)const;
|
||||
bool leave_room(const raii::string_base& roomid)const;
|
||||
|
||||
bool accept_invite(const membership_info& invite)const;
|
||||
bool reject_invite(const membership_info& invite)const;
|
||||
|
||||
//other network
|
||||
void logout(void);
|
||||
|
||||
//upload media
|
||||
file_info upload_file(const raii::string_base& filename)const;
|
||||
file_info upload_file(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
image_info upload_image(const raii::string_base& filename)const;
|
||||
image_info upload_image(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
video_info upload_video(const raii::string_base& filename)const;
|
||||
video_info upload_video(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
audio_info upload_audio(const raii::string_base& filename)const;
|
||||
audio_info upload_audio(const raii::string_base& filename, const raii::string_base& alias)const;
|
||||
|
||||
//send messages
|
||||
raii::rjp_string send_image(const raii::string_base& room, const image_info& image)const;
|
||||
raii::rjp_string send_video(const raii::string_base& room, const video_info& video)const;
|
||||
raii::rjp_string send_message(const raii::string_base& room, const raii::string_base& text)const;
|
||||
raii::rjp_string send_audio(const raii::string_base& room, const audio_info& audio)const;
|
||||
raii::rjp_string send_file(const raii::string_base& room, const file_info& file)const;
|
||||
void send_typing(const raii::string_base& room, bool active, int timeout = 5000)const;
|
||||
|
||||
raii::rjp_string redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const;
|
||||
raii::rjp_string redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
|
||||
private:
|
||||
void _send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::rjp_string _upload_file(raii::filerd& fp, const raii::curl_llist& header)const;
|
||||
raii::rjp_string _send_message(const raii::string_base& room, const raii::string_base& msg)const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
55
include/matrix/client_base.hpp
Normal file
55
include/matrix/client_base.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_CLIENT_BASE_HPP
|
||||
#define MATRIX_CLIENT_BASE_HPP
|
||||
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
|
||||
namespace matrix::internal{
|
||||
|
||||
class client_base
|
||||
{
|
||||
protected:
|
||||
mutable raii::curler m_curl;
|
||||
protected:
|
||||
client_base(void);
|
||||
client_base(const client_base&) = default;
|
||||
client_base(client_base&&) = default;
|
||||
~client_base(void) = default;
|
||||
client_base& operator=(const client_base&) = default;
|
||||
client_base& operator=(client_base&&) = default;
|
||||
|
||||
void _set_curl_useragent(const raii::string_base& useragent);
|
||||
|
||||
protected:
|
||||
static size_t _post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
|
||||
raii::string _get_curl(const raii::string_base& url)const;
|
||||
raii::string _post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
|
||||
raii::string _put_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const;
|
||||
raii::rjp_string _post_and_find(const raii::string_base& data, const raii::string_base& url, const raii::curl_llist& header, const raii::string_base& target)const;
|
||||
raii::rjp_string _get_and_find(const raii::string_base& url, const raii::string_base& search)const;
|
||||
raii::rjp_string _curl_reply_search(const raii::string_base& reply, const raii::string_base& search)const;
|
||||
void _set_curl_defaults(const raii::string_base& useragent)const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
103
include/matrix/event_info.hpp
Normal file
103
include/matrix/event_info.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_EVENT_INFO_HPP
|
||||
#define MATRIX_EVENT_INFO_HPP
|
||||
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include <cstring> //strcmp
|
||||
|
||||
namespace matrix{
|
||||
//message handling structs
|
||||
//enumerate message type but also give a string representation
|
||||
class msgtype{
|
||||
private:
|
||||
const char* m_str;
|
||||
const int m_num;
|
||||
public:
|
||||
constexpr msgtype(const char* s, int n):
|
||||
m_str(s), m_num(n){}
|
||||
constexpr msgtype(const msgtype&) = default;
|
||||
~msgtype(void) = default;
|
||||
constexpr msgtype& operator=(const msgtype&) = default;
|
||||
|
||||
constexpr bool operator==(const msgtype& m){
|
||||
return m_num == m.m_num;
|
||||
}
|
||||
constexpr bool operator!=(const msgtype& m){
|
||||
return m_num != m.m_num;
|
||||
}
|
||||
constexpr const char* str(void)const{
|
||||
return m_str;
|
||||
}
|
||||
constexpr operator int(void)const{
|
||||
return m_num;
|
||||
}
|
||||
};
|
||||
//class enumeration
|
||||
struct msg{
|
||||
private:
|
||||
enum _types{
|
||||
_text, _audio, _video, _file, _image, _other
|
||||
};
|
||||
public:
|
||||
constexpr static msgtype text = msgtype("text", _text);
|
||||
constexpr static msgtype audio = msgtype("audio", _audio);
|
||||
constexpr static msgtype video = msgtype("video", _video);
|
||||
constexpr static msgtype file = msgtype("file", _file);
|
||||
constexpr static msgtype image = msgtype("image", _image);
|
||||
constexpr static msgtype other = msgtype("other", _other);
|
||||
|
||||
constexpr static const msgtype& from_str(const char* str){
|
||||
if(!strcmp(str, "m.text")){
|
||||
return text;
|
||||
}else if(!strcmp(str, "m.audio")){
|
||||
return audio;
|
||||
}else if(!strcmp(str, "m.video")){
|
||||
return video;
|
||||
}else if(!strcmp(str, "m.file")){
|
||||
return file;
|
||||
}else if(!strcmp(str, "m.image")){
|
||||
return image;
|
||||
}else{
|
||||
return other;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct event_info
|
||||
{
|
||||
const raii::rjp_string roomid;
|
||||
const raii::rjp_string sender;
|
||||
const raii::rjp_string eventid;
|
||||
const raii::rjp_string eventtype;
|
||||
const int serverts;
|
||||
const int age;
|
||||
};
|
||||
struct msg_info : public event_info
|
||||
{
|
||||
const msgtype type = msg::other;
|
||||
const raii::rjp_string body;
|
||||
};
|
||||
struct membership_info : public event_info
|
||||
{
|
||||
const raii::rjp_string recipient;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
73
include/matrix/image_thumbnail_maker.hpp
Normal file
73
include/matrix/image_thumbnail_maker.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
This file is a part of 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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_IMAGE_THUMBNAIL_MAKER_HPP
|
||||
#define MATRIX_IMAGE_THUMBNAIL_MAKER_HPP
|
||||
|
||||
#include <FreeImagePlus.h>
|
||||
#include <vector>
|
||||
#include <cstdlib> //size_t
|
||||
#include "matrix/upload_info.hpp"
|
||||
|
||||
#define THUMBSIZE 500
|
||||
|
||||
namespace matrix::internal{
|
||||
|
||||
static std::vector<char> _create_image_thumbnail(fipImage& image, FREE_IMAGE_FORMAT type, size_t target_size){
|
||||
image.makeThumbnail(target_size);
|
||||
FreeImageIO fileout;
|
||||
std::vector<char> buffer;
|
||||
fileout.write_proc = [](void* ptr, unsigned int size, unsigned int nmemb, void* fp) -> unsigned int{
|
||||
std::vector<char>& buffer = *reinterpret_cast<std::vector<char>*>(fp);
|
||||
buffer.insert(buffer.end(), (char*)ptr, ((char*)ptr)+size*nmemb);
|
||||
return size*nmemb;
|
||||
};
|
||||
switch(type){
|
||||
case FIF_JPEG:
|
||||
return image.saveToHandle(type, &fileout, &buffer, JPEG_QUALITYGOOD | JPEG_SUBSAMPLING_411) ? buffer : std::vector<char>();
|
||||
case FIF_PNG:
|
||||
return image.saveToHandle(type, &fileout, &buffer, PNG_Z_BEST_COMPRESSION) ? buffer : std::vector<char>();
|
||||
default:
|
||||
return image.saveToHandle(type, &fileout, &buffer) ? buffer : std::vector<char>();
|
||||
};
|
||||
}
|
||||
static std::vector<char> _load_image_thumbnail_info(fipImage& image, image_info& info, FREE_IMAGE_FORMAT type){
|
||||
//create and upload thumbnail
|
||||
if(info.width > THUMB_SIZE || info.height > THUMB_SIZE){
|
||||
std::vector<char> thumb_data = _create_image_thumbnail(image, type, THUMB_SIZE);
|
||||
if(!thumb_data.size()){
|
||||
info.thumb_width = info.width;
|
||||
info.thumb_height = info.height;
|
||||
info.thumbsize = info.filesize;
|
||||
}else{
|
||||
info.thumb_width = image.getWidth();
|
||||
info.thumb_height = image.getHeight();
|
||||
info.thumbsize = thumb_data.size();
|
||||
return thumb_data;
|
||||
}
|
||||
}else{
|
||||
info.thumb_width = info.width;
|
||||
info.thumb_height = info.height;
|
||||
info.thumbsize = info.filesize;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
57
include/matrix/session.hpp
Normal file
57
include/matrix/session.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_SESSION_HPP
|
||||
#define MATRIX_SESSION_HPP
|
||||
|
||||
#include "matrix/client.hpp"
|
||||
#include "matrix/syncer.hpp"
|
||||
#include "matrix/session_info.hpp"
|
||||
#include "matrix/auth.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include <memory> //shared_ptr
|
||||
#include <utility> //pair
|
||||
|
||||
namespace matrix{
|
||||
class session
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<internal::session_info> m_ses;
|
||||
client m_client;
|
||||
syncer m_sync;
|
||||
public:
|
||||
session(const auth_data&);
|
||||
//local setter
|
||||
void set_useragent(const raii::string_base&);
|
||||
void set_useragent(raii::string&&);
|
||||
|
||||
client& get_client(void);
|
||||
const client& get_client(void)const;
|
||||
syncer& get_syncer(void);
|
||||
const syncer& get_syncer(void)const;
|
||||
|
||||
void invalidate(void);
|
||||
private:
|
||||
void _populate_session_info(const auth_data& a);
|
||||
raii::rjp_string _get_userid(void);
|
||||
raii::string _request_access_token(const raii::string_base& name, const raii::string_base& pass, const raii::string_base& loginurl)const;
|
||||
std::pair<raii::rjp_string,raii::rjp_string> _get_new_access_token(const raii::string_base& name, const raii::string_base& pass, const raii::string_base& loginurl);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
36
include/matrix/session_info.hpp
Normal file
36
include/matrix/session_info.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_SESSION_INFO_HPP
|
||||
#define MATRIX_SESSION_INFO_HPP
|
||||
|
||||
#include "raii/string.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "matrix/url_list.hpp"
|
||||
|
||||
namespace matrix::internal{
|
||||
struct session_info{
|
||||
raii::string useragent; //useragent to identify our application
|
||||
raii::string homeserver; //name of our homeserver
|
||||
raii::rjp_string access_token; //authentication
|
||||
raii::rjp_string userid; //userid including homeserver
|
||||
mat_url_list urls;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
78
include/matrix/syncer.hpp
Normal file
78
include/matrix/syncer.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_SYNCER_HPP
|
||||
#define MATRIX_SYNCER_HPP
|
||||
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/rjp_ptr.hpp"
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include "matrix/event_info.hpp"
|
||||
#include "matrix/session_info.hpp"
|
||||
#include "matrix/client_base.hpp"
|
||||
#include <cstdlib> //size_t
|
||||
#include <utility> //forward
|
||||
#include <functional> //function
|
||||
#include <memory> //shared_ptr
|
||||
|
||||
namespace matrix{
|
||||
class session;
|
||||
class syncer : public internal::client_base
|
||||
{
|
||||
friend class ::matrix::session;
|
||||
private:
|
||||
const std::shared_ptr<internal::session_info> m_ses;
|
||||
|
||||
std::function<void(const msg_info&)> m_message_callback;
|
||||
std::function<void(const membership_info&)> m_membership_callback;
|
||||
std::function<void(const raii::rjp_ptr&)> m_raw_callback;
|
||||
raii::rjp_string m_next_batch; //string which tracks where we are in the server history
|
||||
|
||||
public:
|
||||
syncer(std::shared_ptr<internal::session_info>&);
|
||||
syncer(const syncer& b) = default;
|
||||
syncer(syncer&& b) = default;
|
||||
~syncer(void) = default;
|
||||
|
||||
syncer& operator=(const syncer&) = default;
|
||||
syncer& operator=(syncer&&) = default;
|
||||
|
||||
template<class Func>
|
||||
void set_message_callback(Func&& f){
|
||||
m_message_callback = std::forward<Func>(f);
|
||||
}
|
||||
template<class Func>
|
||||
void set_membership_callback(Func&& f){
|
||||
m_membership_callback = std::forward<Func>(f);
|
||||
}
|
||||
template<class Func>
|
||||
void set_raw_callback(Func&& f){
|
||||
m_raw_callback = std::forward<Func>(f);
|
||||
}
|
||||
raii::string sync(size_t timeout);
|
||||
|
||||
private:
|
||||
void _handle_membership_events(RJP_value* rooms);
|
||||
void _handle_other_membership(RJP_value* join);
|
||||
void _handle_invites(RJP_value* invites);
|
||||
void _handle_messages(RJP_value* messages);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
54
include/matrix/upload_info.hpp
Normal file
54
include/matrix/upload_info.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_UPLOAD_INFO_HPP
|
||||
#define MATRIX_UPLOAD_INFO_HPP
|
||||
|
||||
#include "raii/rjp_string.hpp"
|
||||
#include "raii/string.hpp"
|
||||
#include <cstdlib> //size_t
|
||||
|
||||
namespace matrix{
|
||||
|
||||
struct file_info{
|
||||
raii::rjp_string fileurl;
|
||||
raii::string filename;
|
||||
raii::string mimetype;
|
||||
size_t filesize;
|
||||
};
|
||||
struct image_info : public file_info{
|
||||
using file_info::operator=;
|
||||
size_t width;
|
||||
size_t height;
|
||||
|
||||
raii::rjp_string thumburl;
|
||||
size_t thumb_width;
|
||||
size_t thumb_height;
|
||||
size_t thumbsize;
|
||||
};
|
||||
struct video_info : public image_info{
|
||||
using image_info::operator=;
|
||||
};
|
||||
struct audio_info : public file_info{
|
||||
using file_info::operator=;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
86
include/matrix/url_list.hpp
Normal file
86
include/matrix/url_list.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef MATRIX_URL_LIST_HPP
|
||||
#define MATRIX_URL_LIST_HPP
|
||||
|
||||
#include "raii/string.hpp"
|
||||
|
||||
namespace matrix{
|
||||
class mat_url_list
|
||||
{
|
||||
private:
|
||||
raii::string m_create_room;
|
||||
raii::string m_file_upload;
|
||||
raii::string m_room_list;
|
||||
raii::string m_alias_lookup;
|
||||
raii::string m_whoami;
|
||||
raii::string m_displayname;
|
||||
raii::string m_profile_picture;
|
||||
|
||||
public:
|
||||
mat_url_list(void) = default;
|
||||
mat_url_list(const raii::string_base& homeserver);
|
||||
mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid);
|
||||
mat_url_list(const mat_url_list&) = default;
|
||||
mat_url_list(mat_url_list&&) = default;
|
||||
mat_url_list& operator=(const mat_url_list&) = default;
|
||||
mat_url_list& operator=(mat_url_list&&) = default;
|
||||
|
||||
static raii::string stat_whoami(const raii::string_base& homeserver, const raii::string_base& access_token);
|
||||
static raii::string login(const raii::string_base& homeserver);
|
||||
|
||||
const raii::string& create_room(void)const;
|
||||
const raii::string& file_upload(void)const;
|
||||
const raii::string& room_list(void)const;
|
||||
const raii::string& alias_lookup(void)const;
|
||||
const raii::string& whoami(void)const;
|
||||
const raii::string& displayname(void)const;
|
||||
const raii::string& profile_picture(void)const;
|
||||
raii::string logout(const raii::string_base& homeserver, const raii::string_base& access_token)const;
|
||||
raii::string join_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string leave_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const;
|
||||
raii::string read_receipt(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::string send(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string redact(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const;
|
||||
raii::string power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string presence(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid)const;
|
||||
raii::string typing(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& userid)const;
|
||||
raii::string kick(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string ban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string unban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string invite(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string room_members(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const;
|
||||
raii::string password(const raii::string_base& homeserver, const raii::string_base& access_token)const;
|
||||
|
||||
|
||||
void repopulate_accesstoken(const raii::string_base& homeserver, const raii::string_base& access_token);
|
||||
void repopulate_userid(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid);
|
||||
void repopulate(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid);
|
||||
void invalidate_accesstoken(void);
|
||||
|
||||
private:
|
||||
void _initial_populate(const raii::string_base& homeserver);
|
||||
static constexpr const char* s_proto = "https://";
|
||||
static constexpr const char* s_revision = "r0";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
117
include/matrix/video_thumbnail_maker.hpp
Normal file
117
include/matrix/video_thumbnail_maker.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
This file is a part of 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 "libav/frame.hpp"
|
||||
# include "libav/codec/context.hpp"
|
||||
# include "libav/fmt/context.hpp"
|
||||
# include "libav/packet.hpp"
|
||||
|
||||
extern "C"{
|
||||
# include <libswscale/swscale.h> //sws_scale
|
||||
# include <libavutil/imgutils.h> //av_image_alloc
|
||||
}
|
||||
|
||||
#include <cstdlib> //size_t
|
||||
|
||||
namespace matrix::internal{
|
||||
static libavcodec::context _get_libav_jpg_context(AVRational src, size_t w, size_t h){
|
||||
AVCodec* jpg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
|
||||
libavcodec::context jctx(jpg_codec);
|
||||
jctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
|
||||
jctx->width = w;
|
||||
jctx->height = h;
|
||||
jctx->codec_id = AV_CODEC_ID_MJPEG;
|
||||
jctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
if(src.num == 0 || src.den == 0)
|
||||
jctx->time_base = AVRational{1, 30};
|
||||
else
|
||||
jctx->time_base = src;
|
||||
jctx.open();
|
||||
return jctx;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static void _change_color_range(SwsContext* sws_ctx){
|
||||
int dummy[4];
|
||||
int src_range, dest_range;
|
||||
int br, con, sat;
|
||||
sws_getColorspaceDetails(sws_ctx, (int**)&dummy, &src_range, (int**)&dummy, &dest_range, &br, &con, &sat);
|
||||
const int* coefs = sws_getCoefficients(SWS_CS_DEFAULT);
|
||||
src_range = 2;
|
||||
sws_setColorspaceDetails(sws_ctx, coefs, src_range, coefs, dest_range, br, con, sat);
|
||||
}
|
||||
static void _calc_video_thumbnail_dims(size_t target, size_t src_w, size_t src_h, size_t* w, size_t* h){
|
||||
size_t target_width = src_w;
|
||||
size_t target_height = src_h;
|
||||
if(target_width > target_height){
|
||||
if(target_width > target){
|
||||
target_height = target_height * ((float)target / target_width);
|
||||
target_width = target;
|
||||
}
|
||||
}else{
|
||||
if(target_height > target){
|
||||
target_width = target_width * ((float)target / target_height);
|
||||
target_height = target;
|
||||
}
|
||||
}
|
||||
*w = target_width;
|
||||
*h = target_height;
|
||||
}
|
||||
static libav::packet _create_video_thumbnail(libavfmt::input_context& src, libavcodec::context& src_ctx, int index, size_t targw, size_t targh){
|
||||
libavcodec::context jpg_context = _get_libav_jpg_context(src_ctx->time_base, targw, targh);
|
||||
libav::frame src_frame(src_ctx->width, src_ctx->height, src_ctx->pix_fmt);
|
||||
libav::frame jpg_frame(src_ctx->width, src_ctx->height, jpg_context->pix_fmt);
|
||||
|
||||
libav::packet packet;
|
||||
|
||||
SwsContext* sws_ctx = sws_getContext(src_ctx->width, src_ctx->height, src_ctx->pix_fmt,
|
||||
targw, targh, AV_PIX_FMT_YUV420P,
|
||||
SWS_BILINEAR, NULL, NULL, NULL);
|
||||
//_change_color_range(sws_ctx);
|
||||
|
||||
av_image_alloc(jpg_frame->data, jpg_frame->linesize, targw, targh, jpg_context->pix_fmt, 32);
|
||||
jpg_frame.set_freep();
|
||||
while(av_read_frame(src, packet) >= 0){
|
||||
if(packet->stream_index == index){
|
||||
avcodec_send_packet(src_ctx, packet);
|
||||
if(avcodec_receive_frame(src_ctx, src_frame) >= 0)
|
||||
break;
|
||||
}
|
||||
av_packet_unref(packet);
|
||||
}
|
||||
sws_scale(sws_ctx, src_frame->data, src_frame->linesize, 0, src_ctx->height, jpg_frame->data, jpg_frame->linesize);
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
if(avcodec_send_frame(jpg_context, jpg_frame) < 0){
|
||||
return libav::packet(nullptr);
|
||||
}
|
||||
if(avcodec_receive_packet(jpg_context, packet) < 0){
|
||||
return libav::packet(nullptr);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
static raii::string get_libav_mimetype(const libav::fmt::input_context& ctx){
|
||||
const char* first = strstr(ctx->iformat->name, ",");
|
||||
if(first)
|
||||
return raii::string("video/" + raii::static_string(ctx->iformat->name, first - ctx->iformat->name));
|
||||
return raii::string("video/" + raii::static_string(ctx->iformat->name));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -117,6 +117,7 @@ namespace raii{
|
||||
const char& operator[](size_t i)const;
|
||||
|
||||
protected:
|
||||
string_base& _copy_string(const char* s, size_t len);
|
||||
template<class Tup, size_t I = 0>
|
||||
static void _assign(char* dest, Tup&& t, size_t offset){
|
||||
memcpy(dest+offset, std::get<I>(t), std::get<I+1>(t));
|
||||
|
||||
2
makefile
2
makefile
@ -14,7 +14,7 @@
|
||||
#Copyright 2018-2019 rexy712
|
||||
|
||||
|
||||
SOURCE_DIRS:=src src/raii
|
||||
SOURCE_DIRS:=src src/raii src/matrix
|
||||
OBJDIR:=obj
|
||||
DEPDIR:=$(OBJDIR)/dep
|
||||
INCLUDE_DIRS:=include
|
||||
|
||||
882
src/matrix.cpp
882
src/matrix.cpp
@ -1,882 +0,0 @@
|
||||
/**
|
||||
This file is a part of 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.hpp"
|
||||
|
||||
#include <FreeImagePlus.h>
|
||||
|
||||
#include "raii/curl_llist.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
#include "raii/filerd.hpp"
|
||||
#include "fat_strings.hpp"
|
||||
#include "raii/util.hpp"
|
||||
|
||||
#include "libav/frame.hpp"
|
||||
#include "libav/codec/context.hpp"
|
||||
#include "libav/fmt/context.hpp"
|
||||
#include "libav/packet.hpp"
|
||||
|
||||
extern "C"{
|
||||
# include <libswscale/swscale.h> //sws_scale
|
||||
# include <libavutil/imgutils.h> //av_image_alloc
|
||||
}
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#define THUMB_SIZE 500
|
||||
|
||||
namespace matrix{
|
||||
|
||||
auth_data parse_auth_data(RJP_value* root){
|
||||
static const char* fields[] = {"username", "password", "homeserver", "access_token"};
|
||||
|
||||
RJP_search_res details[4];
|
||||
rjp_search_members(root, 4, fields, details, 0);
|
||||
if(!rjp_value_string_length(details[3].value)){
|
||||
return auth_data{details[0].value,
|
||||
details[1].value,
|
||||
details[2].value,
|
||||
raii::rjp_string{}};
|
||||
}
|
||||
return auth_data{details[0].value,
|
||||
details[1].value,
|
||||
details[2].value,
|
||||
details[3].value};
|
||||
}
|
||||
|
||||
client::client(const auth_data& a, const raii::string_base& useragent):
|
||||
m_curl(),
|
||||
m_useragent(useragent),
|
||||
m_homeserver(a.homeserver)
|
||||
{
|
||||
_acquire_access_token(a);
|
||||
}
|
||||
client::client(const auth_data& a, raii::string&& useragent):
|
||||
m_curl(),
|
||||
m_useragent(std::move(useragent)),
|
||||
m_homeserver(a.homeserver)
|
||||
{
|
||||
_acquire_access_token(a);
|
||||
}
|
||||
|
||||
const raii::rjp_string& client::access_token(void)const{
|
||||
return m_access_token;
|
||||
}
|
||||
const raii::rjp_string& client::userid(void)const{
|
||||
return m_userid;
|
||||
}
|
||||
const raii::string& client::useragent(void)const{
|
||||
return m_useragent;
|
||||
}
|
||||
void client::set_useragent(const raii::string_base& useragent){
|
||||
m_useragent = useragent;
|
||||
}
|
||||
void client::set_useragent(raii::string&& useragent){
|
||||
m_useragent = std::move(useragent);
|
||||
}
|
||||
|
||||
void client::set_display_name(const raii::string_base& newname){
|
||||
raii::string reply = _put_curl(raii::string("{\"displayname\":\"" + newname + "\"}"), m_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_urls.profile_picture(), raii::curl_llist());
|
||||
}
|
||||
|
||||
raii::rjp_string client::get_display_name(void)const{
|
||||
return _get_and_find(m_urls.displayname(), "displayname"_ss);
|
||||
}
|
||||
raii::rjp_string client::get_profile_picture(void)const{
|
||||
return _get_and_find(m_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_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_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;
|
||||
}
|
||||
|
||||
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_urls.create_room(), raii::curl_llist());
|
||||
}
|
||||
bool client::join_room(const raii::string_base& roomid)const{
|
||||
return _post_curl(raii::string(), m_urls.join_room(m_homeserver, m_access_token, m_curl.encode(roomid)), raii::curl_llist());
|
||||
}
|
||||
bool client::leave_room(const raii::string_base& roomid)const{
|
||||
return _post_curl(raii::string(), m_urls.leave_room(m_homeserver, m_access_token, m_curl.encode(roomid)), raii::curl_llist());
|
||||
}
|
||||
bool client::accept_invite(const membership_info& invite)const{
|
||||
return join_room(invite.roomid);
|
||||
}
|
||||
bool client::reject_invite(const membership_info& invite)const{
|
||||
return leave_room(invite.roomid);
|
||||
}
|
||||
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
|
||||
static std::vector<char> _create_image_thumbnail(fipImage& image, FREE_IMAGE_FORMAT type, size_t target_size){
|
||||
image.makeThumbnail(target_size);
|
||||
FreeImageIO fileout;
|
||||
std::vector<char> buffer;
|
||||
fileout.write_proc = [](void* ptr, unsigned int size, unsigned int nmemb, void* fp) -> unsigned int{
|
||||
std::vector<char>& buffer = *reinterpret_cast<std::vector<char>*>(fp);
|
||||
buffer.insert(buffer.end(), (char*)ptr, ((char*)ptr)+size*nmemb);
|
||||
return size*nmemb;
|
||||
};
|
||||
switch(type){
|
||||
case FIF_JPEG:
|
||||
return image.saveToHandle(type, &fileout, &buffer, JPEG_QUALITYGOOD | JPEG_SUBSAMPLING_411) ? buffer : std::vector<char>();
|
||||
case FIF_PNG:
|
||||
return image.saveToHandle(type, &fileout, &buffer, PNG_Z_BEST_COMPRESSION) ? buffer : std::vector<char>();
|
||||
default:
|
||||
return image.saveToHandle(type, &fileout, &buffer) ? buffer : std::vector<char>();
|
||||
};
|
||||
}
|
||||
static std::vector<char> _load_image_thumbnail_info(fipImage& image, image_info& info, FREE_IMAGE_FORMAT type){
|
||||
//create and upload thumbnail
|
||||
if(info.width > THUMB_SIZE || info.height > THUMB_SIZE){
|
||||
std::vector<char> thumb_data = _create_image_thumbnail(image, type, THUMB_SIZE);
|
||||
if(!thumb_data.size()){
|
||||
info.thumb_width = info.width;
|
||||
info.thumb_height = info.height;
|
||||
info.thumbsize = info.filesize;
|
||||
}else{
|
||||
info.thumb_width = image.getWidth();
|
||||
info.thumb_height = image.getHeight();
|
||||
info.thumbsize = thumb_data.size();
|
||||
return thumb_data;
|
||||
}
|
||||
}else{
|
||||
info.thumb_width = info.width;
|
||||
info.thumb_height = info.height;
|
||||
info.thumbsize = info.filesize;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
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 = _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_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
|
||||
static libavcodec::context _get_libav_jpg_context(AVRational src, size_t w, size_t h){
|
||||
AVCodec* jpg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
|
||||
libavcodec::context jctx(jpg_codec);
|
||||
jctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
|
||||
jctx->width = w;
|
||||
jctx->height = h;
|
||||
jctx->codec_id = AV_CODEC_ID_MJPEG;
|
||||
jctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
if(src.num == 0 || src.den == 0)
|
||||
jctx->time_base = AVRational{1, 30};
|
||||
else
|
||||
jctx->time_base = src;
|
||||
jctx.open();
|
||||
return jctx;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static void _change_color_range(SwsContext* sws_ctx){
|
||||
int dummy[4];
|
||||
int src_range, dest_range;
|
||||
int br, con, sat;
|
||||
sws_getColorspaceDetails(sws_ctx, (int**)&dummy, &src_range, (int**)&dummy, &dest_range, &br, &con, &sat);
|
||||
const int* coefs = sws_getCoefficients(SWS_CS_DEFAULT);
|
||||
src_range = 2;
|
||||
sws_setColorspaceDetails(sws_ctx, coefs, src_range, coefs, dest_range, br, con, sat);
|
||||
}
|
||||
static void _calc_video_thumbnail_dims(size_t target, size_t src_w, size_t src_h, size_t* w, size_t* h){
|
||||
size_t target_width = src_w;
|
||||
size_t target_height = src_h;
|
||||
if(target_width > target_height){
|
||||
if(target_width > target){
|
||||
target_height = target_height * ((float)target / target_width);
|
||||
target_width = target;
|
||||
}
|
||||
}else{
|
||||
if(target_height > target){
|
||||
target_width = target_width * ((float)target / target_height);
|
||||
target_height = target;
|
||||
}
|
||||
}
|
||||
*w = target_width;
|
||||
*h = target_height;
|
||||
}
|
||||
static libav::packet _create_video_thumbnail(libavfmt::input_context& src, libavcodec::context& src_ctx, int index, size_t targw, size_t targh){
|
||||
libavcodec::context jpg_context = _get_libav_jpg_context(src_ctx->time_base, targw, targh);
|
||||
libav::frame src_frame(src_ctx->width, src_ctx->height, src_ctx->pix_fmt);
|
||||
libav::frame jpg_frame(src_ctx->width, src_ctx->height, jpg_context->pix_fmt);
|
||||
|
||||
libav::packet packet;
|
||||
|
||||
SwsContext* sws_ctx = sws_getContext(src_ctx->width, src_ctx->height, src_ctx->pix_fmt,
|
||||
targw, targh, AV_PIX_FMT_YUV420P,
|
||||
SWS_BILINEAR, NULL, NULL, NULL);
|
||||
//_change_color_range(sws_ctx);
|
||||
|
||||
av_image_alloc(jpg_frame->data, jpg_frame->linesize, targw, targh, jpg_context->pix_fmt, 32);
|
||||
jpg_frame.set_freep();
|
||||
while(av_read_frame(src, packet) >= 0){
|
||||
if(packet->stream_index == index){
|
||||
avcodec_send_packet(src_ctx, packet);
|
||||
if(avcodec_receive_frame(src_ctx, src_frame) >= 0)
|
||||
break;
|
||||
}
|
||||
av_packet_unref(packet);
|
||||
}
|
||||
sws_scale(sws_ctx, src_frame->data, src_frame->linesize, 0, src_ctx->height, jpg_frame->data, jpg_frame->linesize);
|
||||
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
if(avcodec_send_frame(jpg_context, jpg_frame) < 0){
|
||||
return libav::packet(nullptr);
|
||||
}
|
||||
if(avcodec_receive_packet(jpg_context, packet) < 0){
|
||||
return libav::packet(nullptr);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
static raii::string get_libav_mimetype(const libav::fmt::input_context& ctx){
|
||||
const char* first = strstr(ctx->iformat->name, ",");
|
||||
if(first)
|
||||
return raii::string("video/" + raii::static_string(ctx->iformat->name, first - ctx->iformat->name));
|
||||
return raii::string("video/" + raii::static_string(ctx->iformat->name));
|
||||
}
|
||||
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
|
||||
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;
|
||||
_calc_video_thumbnail_dims(THUMB_SIZE, src_ctx->width, src_ctx->height, &thumbw, &thumbh);
|
||||
libav::packet packet = _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_urls.file_upload(), header, "content_uri"_ss);
|
||||
packet.reset();
|
||||
}
|
||||
|
||||
ret.width = src_ctx->width;
|
||||
ret.height = src_ctx->height;
|
||||
ret.mimetype = 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 = 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
|
||||
|
||||
raii::rjp_string client::send_file(const raii::string_base& room, const file_info& file)const{
|
||||
return _send_message(room, detail::_file_body(file));
|
||||
}
|
||||
raii::rjp_string client::send_image(const raii::string_base& room, const image_info& image)const{
|
||||
return _send_message(room, detail::_image_body(image));
|
||||
}
|
||||
raii::rjp_string client::send_video(const raii::string_base& room, const video_info& video)const{
|
||||
return _send_message(room, detail::_video_body(video));
|
||||
}
|
||||
raii::rjp_string client::send_audio(const raii::string_base& room, const audio_info& audio)const{
|
||||
return _send_message(room, detail::_audio_body(audio));
|
||||
}
|
||||
raii::rjp_string client::send_message(const raii::string_base& room, const raii::string_base& text)const{
|
||||
return _send_message(room, detail::_message_body(text));
|
||||
}
|
||||
void client::send_typing(const raii::string_base& room, bool active, int timeout)const{
|
||||
if(active)
|
||||
_put_curl(raii::string("{\"timeout\":" + raii::itostr(timeout) + ",\"typing\":true}"), m_urls.typing(m_homeserver, m_access_token, m_curl.encode(room), m_curl.encode(m_userid)), raii::curl_llist());
|
||||
else
|
||||
_put_curl("{\"typing\":false}"_ss, m_urls.typing(m_homeserver, m_access_token, m_curl.encode(room), m_curl.encode(m_userid)), raii::curl_llist());
|
||||
}
|
||||
|
||||
raii::rjp_string client::redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const{
|
||||
auto ret = _put_curl(raii::string("{\"reason\":\"" + reason + "\"}"), m_urls.redact(m_homeserver, m_access_token, m_curl.encode(roomid), m_curl.encode(eventid)), raii::curl_llist());
|
||||
if(!ret) return {};
|
||||
raii::rjp_ptr root(rjp_parse(ret.get()));
|
||||
if(!root) return {};
|
||||
|
||||
RJP_search_res res = rjp_search_member(root.get(), "event_id", 0);
|
||||
if(!res.value) return {};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
raii::rjp_string client::redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return redact_event(roomid, eventid, ""_ss);
|
||||
}
|
||||
|
||||
void client::logout(void){
|
||||
_get_curl(m_urls.logout(m_homeserver, m_access_token));
|
||||
m_urls.invalidate_accesstoken();
|
||||
}
|
||||
raii::string client::sync(size_t timeout){
|
||||
raii::string reply = _get_curl(m_urls.sync(m_homeserver, m_access_token, m_next_batch, raii::itostr(timeout)));
|
||||
|
||||
if(!reply)
|
||||
return {};
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return reply;
|
||||
|
||||
RJP_search_res res = rjp_search_member(root.get(), "next_batch", 0);
|
||||
if(!res.value)
|
||||
return reply;
|
||||
m_next_batch = res.value;
|
||||
|
||||
if(m_raw_callback != nullptr){
|
||||
m_raw_callback(*this, root);
|
||||
}
|
||||
res = rjp_search_member(root.get(), "rooms", 0);
|
||||
if(res.value){
|
||||
if(m_message_callback != nullptr)
|
||||
_handle_messages(res.value);
|
||||
if(m_membership_callback != nullptr)
|
||||
_handle_membership_events(res.value);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
Internal functions
|
||||
********************************/
|
||||
|
||||
void client::_handle_membership_events(RJP_value* rooms){
|
||||
RJP_search_res res = rjp_search_member(rooms, "invite", 0);
|
||||
if(res.value)
|
||||
_handle_invites(res.value);
|
||||
res = rjp_search_member(rooms, "join", 0);
|
||||
if(res.value)
|
||||
_handle_other_membership(res.value);
|
||||
}
|
||||
static membership_info _membership_info_from_json(RJP_value* event, RJP_value* roomid){
|
||||
static constexpr const char* search_terms[] = {"event_id", "sender", "state_key", "unsigned", "type", "origin_server_ts"};
|
||||
static constexpr size_t num_searches = sizeof(search_terms)/sizeof(search_terms[0]);
|
||||
RJP_search_res results[num_searches] = {};
|
||||
rjp_search_members(event, num_searches, search_terms, results, 0);
|
||||
for(size_t i = 0;i < num_searches;++i){
|
||||
if(!results[i].value)
|
||||
return {};
|
||||
}
|
||||
RJP_search_res age = rjp_search_member(results[3].value, "age", 0);
|
||||
if(!age.value) return {};
|
||||
|
||||
raii::rjp_string room_str = rjp_member_name(roomid);
|
||||
raii::rjp_string sender_str = results[1].value;
|
||||
raii::rjp_string event_str = results[0].value;
|
||||
raii::rjp_string eventtype_str = results[4].value;
|
||||
raii::rjp_string rec_str = results[2].value;
|
||||
|
||||
return membership_info{std::move(room_str), std::move(sender_str),
|
||||
std::move(event_str), std::move(eventtype_str),
|
||||
rjp_value_integer(results[5].value), rjp_value_integer(age.value),
|
||||
std::move(rec_str)};
|
||||
}
|
||||
static msg_info _message_info_from_json(RJP_value* event, RJP_value* roomid){
|
||||
static constexpr const char* searches[] = {"sender", "content", "event_id", "unsigned", "type", "origin_server_ts"};
|
||||
static constexpr size_t num_searches = sizeof(searches)/sizeof(searches[0]);
|
||||
RJP_search_res results[num_searches] = {};
|
||||
rjp_search_members(event, num_searches, searches, results, 0);
|
||||
for(size_t i = 0;i < num_searches;++i){
|
||||
if(!results[i].value)
|
||||
return msg_info{};
|
||||
}
|
||||
RJP_search_res msg = rjp_search_member(results[1].value, "msgtype", 0);
|
||||
if(!msg.value) return {};
|
||||
RJP_search_res body = rjp_search_member(results[1].value, "body", 0);
|
||||
if(!body.value) return {};
|
||||
RJP_search_res age = rjp_search_member(results[3].value, "age", 0);
|
||||
if(!age.value) return msg_info{};
|
||||
raii::rjp_string room_str = rjp_member_name(roomid);
|
||||
raii::rjp_string sender_str = results[0].value;
|
||||
raii::rjp_string eventid_str = results[2].value;
|
||||
raii::rjp_string eventtype_str = results[3].value;
|
||||
raii::rjp_string msgbody_str = body.value;
|
||||
return msg_info{std::move(room_str), std::move(sender_str),
|
||||
std::move(eventid_str), std::move(eventtype_str),
|
||||
rjp_value_integer(results[5].value), rjp_value_integer(age.value),
|
||||
msg::from_str(rjp_value_string(msg.value)), std::move(msgbody_str)};
|
||||
}
|
||||
void client::_handle_other_membership(RJP_value* join){
|
||||
for(RJP_value* roomid = rjp_get_member(join);roomid;roomid = rjp_next_member(roomid)){
|
||||
RJP_search_res res = rjp_search_member(roomid, "timeline", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
membership_info minfo = _membership_info_from_json(event, roomid);
|
||||
if(!minfo.roomid) continue;
|
||||
if(minfo.eventtype != "m.room.member"_ss) continue;
|
||||
|
||||
m_membership_callback(*this, minfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
void client::_handle_invites(RJP_value* invites){
|
||||
for(RJP_value* roomid = rjp_get_member(invites);roomid;roomid = rjp_next_member(roomid)){
|
||||
RJP_search_res res = rjp_search_member(roomid, "invite_state", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
membership_info minfo = _membership_info_from_json(event, roomid);
|
||||
if(!minfo.roomid) continue;
|
||||
m_membership_callback(*this, minfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
void client::_handle_messages(RJP_value* messages){
|
||||
RJP_search_res res = rjp_search_member(messages, "join", 0);
|
||||
if(!res.value) return;
|
||||
for(RJP_value* roomid = rjp_get_member(res.value);roomid;roomid = rjp_next_member(roomid)){
|
||||
res = rjp_search_member(roomid, "timeline", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
msg_info minfo = _message_info_from_json(event, roomid);
|
||||
m_message_callback(*this, minfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void client::_send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
_post_curl(""_ss, m_urls.read_receipt(m_homeserver, m_access_token, m_curl.encode(roomid), m_curl.encode(eventid)), raii::curl_llist());
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
raii::rjp_string client::_send_message(const raii::string_base& room, const raii::string_base& msg)const{
|
||||
raii::rjp_string reply = _post_and_find(
|
||||
msg,
|
||||
m_urls.send(m_homeserver, m_access_token, m_curl.encode(room)),
|
||||
raii::curl_llist(),
|
||||
"event_id"_ss);
|
||||
return reply;
|
||||
}
|
||||
size_t client::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
||||
(*data) += ptr;
|
||||
return size*nmemb;
|
||||
}
|
||||
|
||||
raii::string client::_get_curl(const raii::string_base& url)const{
|
||||
raii::string reply;
|
||||
m_curl.getreq();
|
||||
m_curl.seturl(url);
|
||||
m_curl.setheader(raii::curl_llist{});
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
CURLcode res = m_curl.perform();
|
||||
if(res != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
raii::string client::_post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string reply;
|
||||
m_curl.postreq();
|
||||
m_curl.setopt(CURLOPT_POSTFIELDS, postdata.get());
|
||||
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)postdata.length());
|
||||
m_curl.seturl(url);
|
||||
m_curl.setheader(header);
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
CURLcode res = m_curl.perform();
|
||||
if(res != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
struct put_data{
|
||||
const char* data;
|
||||
size_t len;
|
||||
};
|
||||
static size_t _put_read_curl_callback(char* buffer, size_t size, size_t nmemb, void* userdata){
|
||||
put_data* src = reinterpret_cast<put_data*>(userdata);
|
||||
size_t curl_size = size*nmemb;
|
||||
size_t to_copy = std::min(curl_size, src->len);
|
||||
memcpy(buffer, src->data, to_copy);
|
||||
src->len -= to_copy;
|
||||
src->data += to_copy;
|
||||
return to_copy;
|
||||
}
|
||||
raii::string client::_put_curl(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string reply;
|
||||
put_data data{putdata.get(), putdata.length()};
|
||||
m_curl.putreq();
|
||||
m_curl.setopt(CURLOPT_POSTFIELDS, putdata.get());
|
||||
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)putdata.length());
|
||||
m_curl.seturl(url);
|
||||
m_curl.setheader(header);
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
m_curl.setopt(CURLOPT_READFUNCTION, _put_read_curl_callback);
|
||||
m_curl.setopt(CURLOPT_READDATA, &data);
|
||||
m_curl.setopt(CURLOPT_INFILESIZE, (curl_off_t)data.len);
|
||||
CURLcode res = m_curl.perform();
|
||||
m_curl.setopt(CURLOPT_READDATA, NULL);
|
||||
m_curl.setopt(CURLOPT_READFUNCTION, NULL);
|
||||
if(res != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
raii::rjp_string client::_post_and_find(const raii::string_base& data, const raii::string_base& url,
|
||||
const raii::curl_llist& header, const raii::string_base& target)const
|
||||
{
|
||||
raii::string reply = _post_curl(data, url, header);
|
||||
if(!reply)
|
||||
return {};
|
||||
return _curl_reply_search(reply, target);
|
||||
}
|
||||
raii::rjp_string client::_get_and_find(const raii::string_base& url, const raii::string_base& target)const{
|
||||
raii::string reply = _get_curl(url);
|
||||
if(!reply)
|
||||
return {};
|
||||
return _curl_reply_search(reply, target);
|
||||
}
|
||||
raii::rjp_string client::_curl_reply_search(const raii::string_base& reply, const raii::string_base& target)const{
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return {};
|
||||
RJP_search_res res = rjp_search_member(root.get(), target.get(), 0);
|
||||
if(rjp_value_type(res.value) != json_string)
|
||||
return {};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
void client::_set_curl_defaults(void)const{
|
||||
m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L);
|
||||
m_curl.setopt(CURLOPT_NOPROGRESS, 1L);
|
||||
m_curl.setuseragent(m_useragent);
|
||||
m_curl.setopt(CURLOPT_MAXREDIRS, 50L);
|
||||
m_curl.setopt(CURLOPT_FOLLOWLOCATION, 1L);
|
||||
m_curl.forcessl(CURL_SSLVERSION_TLSv1_2);
|
||||
m_curl.setopt(CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
m_curl.setopt(CURLOPT_FAILONERROR, 1L);
|
||||
}
|
||||
raii::string client::_request_access_token(const auth_data& a)const{
|
||||
CURLcode result;
|
||||
raii::string postdata("{\"type\":\"m.login.password\", \"user\":\"" + raii::json_escape(a.name) + "\", \"password\":\"" + raii::json_escape(a.pass) + "\"}");
|
||||
raii::string reply;
|
||||
|
||||
m_curl.seturl(m_urls.login());
|
||||
m_curl.setpostdata(postdata);
|
||||
m_curl.postreq();
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
|
||||
result = m_curl.perform();
|
||||
|
||||
if(result != CURLE_OK)
|
||||
return {};
|
||||
|
||||
return reply;
|
||||
}
|
||||
void client::_get_new_access_token(const auth_data& a){
|
||||
m_urls = mat_url_list(m_homeserver);
|
||||
raii::string reply = _request_access_token(a);
|
||||
if(!reply)
|
||||
return;
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return;
|
||||
RJP_search_res token = rjp_search_member(root.get(), "access_token", 0);
|
||||
m_access_token = raii::rjp_string{token.value};
|
||||
token = rjp_search_member(root.get(), "user_id", 0);
|
||||
m_userid = raii::rjp_string{token.value};
|
||||
m_urls.repopulate_accesstoken(m_homeserver, m_access_token);
|
||||
m_urls.repopulate_userid(m_homeserver, m_access_token, m_curl.encode(m_userid));
|
||||
}
|
||||
void client::_acquire_access_token(const auth_data& a){
|
||||
_set_curl_defaults();
|
||||
if(a.access_token){
|
||||
m_access_token = a.access_token;
|
||||
raii::string reply;
|
||||
m_urls.repopulate_accesstoken(m_homeserver, m_access_token);
|
||||
reply = _get_curl(m_urls.whoami());
|
||||
|
||||
if(!reply){
|
||||
DEBUG_PRINT("Given access token is invalid! Getting new token\n");
|
||||
_get_new_access_token(a);
|
||||
return;
|
||||
}
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return;
|
||||
RJP_search_res id = rjp_search_member(root.get(), "user_id", 0);
|
||||
m_userid = raii::rjp_string(id.value);
|
||||
m_urls.repopulate_userid(m_homeserver, m_access_token, m_curl.encode(m_userid));
|
||||
}else{
|
||||
_get_new_access_token(a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
client::mat_url_list::mat_url_list(const raii::string_base& homeserver){
|
||||
_initial_populate(homeserver);
|
||||
}
|
||||
client::mat_url_list::mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
repopulate(homeserver, access_token, userid);
|
||||
}
|
||||
void client::mat_url_list::repopulate_accesstoken(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
m_create_room = s_proto + homeserver + "/_matrix/client/r0/createRoom?access_token=" + access_token;
|
||||
m_file_upload = s_proto + homeserver + "/_matrix/media/r0/upload?access_token=" + access_token;
|
||||
m_room_list = s_proto + homeserver + "/_matrix/client/r0/joined_rooms?access_token=" + access_token;
|
||||
m_whoami = s_proto + homeserver + "/_matrix/client/r0/account/whoami?access_token=" + access_token;
|
||||
}
|
||||
void client::mat_url_list::repopulate_userid(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
m_displayname = s_proto + homeserver + "/_matrix/client/r0/profile/" + userid + "/displayname?access_token=" + access_token;
|
||||
m_profile_picture = s_proto + homeserver + "/_matrix/client/r0/profile/" + userid + "/avatar_url?access_token=" + access_token;
|
||||
}
|
||||
void client::mat_url_list::repopulate(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
_initial_populate(homeserver);
|
||||
repopulate_accesstoken(homeserver, access_token);
|
||||
repopulate_userid(homeserver, access_token, userid);
|
||||
}
|
||||
void client::mat_url_list::invalidate_accesstoken(void){
|
||||
m_create_room.reset();
|
||||
m_file_upload.reset();
|
||||
m_room_list.reset();
|
||||
m_whoami.reset();
|
||||
m_displayname.reset();
|
||||
m_profile_picture.reset();
|
||||
}
|
||||
const raii::string& client::mat_url_list::create_room(void)const{
|
||||
return m_create_room;
|
||||
}
|
||||
const raii::string& client::mat_url_list::file_upload(void)const{
|
||||
return m_file_upload;
|
||||
}
|
||||
const raii::string& client::mat_url_list::room_list(void)const{
|
||||
return m_room_list;
|
||||
}
|
||||
const raii::string& client::mat_url_list::login(void)const{
|
||||
return m_login;
|
||||
}
|
||||
const raii::string& client::mat_url_list::alias_lookup(void)const{
|
||||
return m_alias_lookup;
|
||||
}
|
||||
const raii::string& client::mat_url_list::whoami(void)const{
|
||||
return m_whoami;
|
||||
}
|
||||
const raii::string& client::mat_url_list::displayname(void)const{
|
||||
return m_displayname;
|
||||
}
|
||||
const raii::string& client::mat_url_list::profile_picture(void)const{
|
||||
return m_profile_picture;
|
||||
}
|
||||
raii::string client::mat_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/logout?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::join_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/join?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::leave_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/leave?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const{
|
||||
if(!next_batch)
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/sync?access_token=" + access_token + "&timeout=" + timeout);
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/sync?access_token=" + access_token + "&timeout=" + timeout + "&since=" + next_batch);
|
||||
}
|
||||
raii::string client::mat_url_list::read_receipt(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/receipt/m.read/" + eventid + "?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::send(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/send/m.room.message?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::redact(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/redact/" + eventid + "/0?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/state/m.room.power_levels?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::presence(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/presence/" + userid + "/status?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::typing(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& userid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/typing/" + userid + "?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::kick(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/kick?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::ban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/ban?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::unban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/unban?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::invite(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/invite?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::room_members(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/members?access_token=" + access_token);
|
||||
}
|
||||
raii::string client::mat_url_list::password(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/account/password?access_token=" + access_token);
|
||||
}
|
||||
void client::mat_url_list::_initial_populate(const raii::string_base& homeserver){
|
||||
m_alias_lookup = s_proto + homeserver + "/_matrix/client/r0/directory/room/";
|
||||
m_login = s_proto + homeserver + "/_matrix/client/r0/login";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
335
src/matrix/client.cpp
Normal file
335
src/matrix/client.cpp
Normal file
@ -0,0 +1,335 @@
|
||||
/**
|
||||
This file is a part of 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(std::shared_ptr<internal::session_info>& ses):
|
||||
client_base(),
|
||||
m_ses(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());
|
||||
}
|
||||
bool client::join_room(const raii::string_base& roomid)const{
|
||||
return _post_curl(raii::string(), m_ses->urls.join_room(m_ses->homeserver, m_ses->access_token, m_curl.encode(roomid)), raii::curl_llist());
|
||||
}
|
||||
bool client::leave_room(const raii::string_base& roomid)const{
|
||||
return _post_curl(raii::string(), m_ses->urls.leave_room(m_ses->homeserver, m_ses->access_token, m_curl.encode(roomid)), raii::curl_llist());
|
||||
}
|
||||
bool client::accept_invite(const membership_info& invite)const{
|
||||
return join_room(invite.roomid);
|
||||
}
|
||||
bool client::reject_invite(const membership_info& invite)const{
|
||||
return leave_room(invite.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{
|
||||
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
|
||||
|
||||
|
||||
//send messages
|
||||
raii::rjp_string client::send_file(const raii::string_base& room, const file_info& file)const{
|
||||
return _send_message(room, detail::_file_body(file));
|
||||
}
|
||||
raii::rjp_string client::send_image(const raii::string_base& room, const image_info& image)const{
|
||||
return _send_message(room, detail::_image_body(image));
|
||||
}
|
||||
raii::rjp_string client::send_video(const raii::string_base& room, const video_info& video)const{
|
||||
return _send_message(room, detail::_video_body(video));
|
||||
}
|
||||
raii::rjp_string client::send_audio(const raii::string_base& room, const audio_info& audio)const{
|
||||
return _send_message(room, detail::_audio_body(audio));
|
||||
}
|
||||
raii::rjp_string client::send_message(const raii::string_base& room, const raii::string_base& text)const{
|
||||
return _send_message(room, detail::_message_body(text));
|
||||
}
|
||||
void client::send_typing(const raii::string_base& room, bool active, int timeout)const{
|
||||
if(active)
|
||||
_put_curl(raii::string("{\"timeout\":" + raii::itostr(timeout) + ",\"typing\":true}"), m_ses->urls.typing(m_ses->homeserver, m_ses->access_token, m_curl.encode(room), m_curl.encode(m_ses->userid)), raii::curl_llist());
|
||||
else
|
||||
_put_curl("{\"typing\":false}"_ss, m_ses->urls.typing(m_ses->homeserver, m_ses->access_token, m_curl.encode(room), m_curl.encode(m_ses->userid)), raii::curl_llist());
|
||||
}
|
||||
|
||||
raii::rjp_string client::redact_event(const raii::string_base& roomid, const raii::string_base& eventid, const raii::string_base& reason)const{
|
||||
auto ret = _put_curl(raii::string("{\"reason\":\"" + reason + "\"}"), m_ses->urls.redact(m_ses->homeserver, m_ses->access_token, m_curl.encode(roomid), m_curl.encode(eventid)), raii::curl_llist());
|
||||
if(!ret) return {};
|
||||
raii::rjp_ptr root(rjp_parse(ret.get()));
|
||||
if(!root) return {};
|
||||
|
||||
RJP_search_res res = rjp_search_member(root.get(), "event_id", 0);
|
||||
if(!res.value) return {};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
raii::rjp_string client::redact_event(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return redact_event(roomid, eventid, ""_ss);
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Internal functions
|
||||
********************************/
|
||||
|
||||
void client::_send_read_receipt(const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
_post_curl(""_ss, m_ses->urls.read_receipt(m_ses->homeserver, m_ses->access_token, m_curl.encode(roomid), m_curl.encode(eventid)), raii::curl_llist());
|
||||
}
|
||||
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;
|
||||
}
|
||||
raii::rjp_string client::_send_message(const raii::string_base& room, const raii::string_base& msg)const{
|
||||
raii::rjp_string reply = _post_and_find(
|
||||
msg,
|
||||
m_ses->urls.send(m_ses->homeserver, m_ses->access_token, m_curl.encode(room)),
|
||||
raii::curl_llist(),
|
||||
"event_id"_ss);
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
133
src/matrix/client_base.cpp
Normal file
133
src/matrix/client_base.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
This file is a part of rexy's matrix bot
|
||||
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_base.hpp"
|
||||
#include "raii/rjp_ptr.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
#include <cstdlib> //size_t
|
||||
#include <algorithm> //min, max
|
||||
|
||||
namespace matrix::internal{
|
||||
|
||||
client_base::client_base(void):
|
||||
m_curl()
|
||||
{
|
||||
_set_curl_defaults(""_ss);
|
||||
}
|
||||
void client_base::_set_curl_useragent(const raii::string_base& useragent){
|
||||
m_curl.setuseragent(useragent);
|
||||
}
|
||||
size_t client_base::_post_reply_curl_callback(char* ptr, size_t size, size_t nmemb, void* userdata){
|
||||
raii::string* data = reinterpret_cast<raii::string*>(userdata);
|
||||
(*data) += ptr;
|
||||
return size*nmemb;
|
||||
}
|
||||
raii::string client_base::_get_curl(const raii::string_base& url)const{
|
||||
raii::string reply;
|
||||
m_curl.getreq();
|
||||
m_curl.seturl(url);
|
||||
m_curl.setheader(raii::curl_llist{});
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
CURLcode res = m_curl.perform();
|
||||
if(res != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
raii::string client_base::_post_curl(const raii::string_base& postdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string reply;
|
||||
m_curl.postreq();
|
||||
m_curl.setopt(CURLOPT_POSTFIELDS, postdata.get());
|
||||
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)postdata.length());
|
||||
m_curl.seturl(url);
|
||||
m_curl.setheader(header);
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
CURLcode res = m_curl.perform();
|
||||
if(res != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
struct put_data{
|
||||
const char* data;
|
||||
size_t len;
|
||||
};
|
||||
static size_t _put_read_curl_callback(char* buffer, size_t size, size_t nmemb, void* userdata){
|
||||
put_data* src = reinterpret_cast<put_data*>(userdata);
|
||||
size_t curl_size = size*nmemb;
|
||||
size_t to_copy = std::min(curl_size, src->len);
|
||||
memcpy(buffer, src->data, to_copy);
|
||||
src->len -= to_copy;
|
||||
src->data += to_copy;
|
||||
return to_copy;
|
||||
}
|
||||
raii::string client_base::_put_curl(const raii::string_base& putdata, const raii::string_base& url, const raii::curl_llist& header)const{
|
||||
raii::string reply;
|
||||
put_data data{putdata.get(), putdata.length()};
|
||||
m_curl.putreq();
|
||||
m_curl.setopt(CURLOPT_POSTFIELDS, putdata.get());
|
||||
m_curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)putdata.length());
|
||||
m_curl.seturl(url);
|
||||
m_curl.setheader(header);
|
||||
m_curl.setopt(CURLOPT_WRITEFUNCTION, _post_reply_curl_callback);
|
||||
m_curl.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
m_curl.setopt(CURLOPT_READFUNCTION, _put_read_curl_callback);
|
||||
m_curl.setopt(CURLOPT_READDATA, &data);
|
||||
m_curl.setopt(CURLOPT_INFILESIZE, (curl_off_t)data.len);
|
||||
CURLcode res = m_curl.perform();
|
||||
m_curl.setopt(CURLOPT_READDATA, NULL);
|
||||
m_curl.setopt(CURLOPT_READFUNCTION, NULL);
|
||||
if(res != CURLE_OK)
|
||||
return {};
|
||||
return reply;
|
||||
}
|
||||
raii::rjp_string client_base::_post_and_find(const raii::string_base& data, const raii::string_base& url,
|
||||
const raii::curl_llist& header, const raii::string_base& target)const
|
||||
{
|
||||
raii::string reply = _post_curl(data, url, header);
|
||||
if(!reply)
|
||||
return {};
|
||||
return _curl_reply_search(reply, target);
|
||||
}
|
||||
raii::rjp_string client_base::_get_and_find(const raii::string_base& url, const raii::string_base& target)const{
|
||||
raii::string reply = _get_curl(url);
|
||||
if(!reply)
|
||||
return {};
|
||||
return _curl_reply_search(reply, target);
|
||||
}
|
||||
raii::rjp_string client_base::_curl_reply_search(const raii::string_base& reply, const raii::string_base& target)const{
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return {};
|
||||
RJP_search_res res = rjp_search_member(root.get(), target.get(), 0);
|
||||
if(rjp_value_type(res.value) != json_string)
|
||||
return {};
|
||||
return raii::rjp_string(res.value);
|
||||
}
|
||||
void client_base::_set_curl_defaults(const raii::string_base& useragent)const{
|
||||
m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L);
|
||||
m_curl.setopt(CURLOPT_NOPROGRESS, 1L);
|
||||
m_curl.setuseragent(useragent);
|
||||
m_curl.setopt(CURLOPT_MAXREDIRS, 50L);
|
||||
m_curl.setopt(CURLOPT_FOLLOWLOCATION, 1L);
|
||||
m_curl.forcessl(CURL_SSLVERSION_TLSv1_2);
|
||||
m_curl.setopt(CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
m_curl.setopt(CURLOPT_FAILONERROR, 1L);
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,8 +16,8 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fat_strings.hpp"
|
||||
#include "matrix.hpp"
|
||||
#include "matrix/fat_strings.hpp"
|
||||
#include "matrix/upload_info.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
#include "raii/util.hpp"
|
||||
|
||||
127
src/matrix/session.cpp
Normal file
127
src/matrix/session.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
This file is a part of 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/session.hpp"
|
||||
#include "raii/rjp_ptr.hpp"
|
||||
#include "raii/curler.hpp"
|
||||
#include "raii/util.hpp"
|
||||
|
||||
namespace matrix{
|
||||
|
||||
session::session(const auth_data& auth):
|
||||
m_ses(new internal::session_info),
|
||||
m_client(m_ses),
|
||||
m_sync(m_ses)
|
||||
{
|
||||
_populate_session_info(auth);
|
||||
m_client._set_curl_useragent(m_ses->useragent);
|
||||
m_sync._set_curl_useragent(m_ses->useragent);
|
||||
}
|
||||
|
||||
void session::set_useragent(const raii::string_base& agent){
|
||||
m_ses->useragent = agent;
|
||||
}
|
||||
void session::set_useragent(raii::string&& agent){
|
||||
m_ses->useragent = std::move(agent);
|
||||
}
|
||||
|
||||
client& session::get_client(void){
|
||||
return m_client;
|
||||
}
|
||||
const client& session::get_client(void)const{
|
||||
return m_client;
|
||||
}
|
||||
syncer& session::get_syncer(void){
|
||||
return m_sync;
|
||||
}
|
||||
const syncer& session::get_syncer(void)const{
|
||||
return m_sync;
|
||||
}
|
||||
|
||||
void session::invalidate(void){
|
||||
m_ses->useragent.reset();
|
||||
m_ses->homeserver.reset();
|
||||
m_ses->access_token.reset();
|
||||
m_ses->userid.reset();
|
||||
m_ses->urls.invalidate_accesstoken();
|
||||
}
|
||||
|
||||
void session::_populate_session_info(const auth_data& a){
|
||||
m_ses->useragent = a.useragent;
|
||||
m_ses->homeserver = a.homeserver;
|
||||
m_ses->access_token = a.access_token;
|
||||
auto reply = m_client._get_curl(mat_url_list::stat_whoami(m_ses->homeserver, m_ses->access_token));
|
||||
if(!reply){
|
||||
auto [token, id] = _get_new_access_token(a.name, a.pass, mat_url_list::login(m_ses->homeserver));
|
||||
if(token && id){
|
||||
m_ses->access_token = std::move(token);
|
||||
m_ses->userid = std::move(id);
|
||||
m_ses->urls.repopulate(m_ses->homeserver, m_ses->access_token, m_ses->userid);
|
||||
}else{
|
||||
invalidate();
|
||||
}
|
||||
}else{
|
||||
m_ses->urls.repopulate_accesstoken(m_ses->homeserver, m_ses->access_token);
|
||||
m_ses->userid = _get_userid();
|
||||
m_ses->urls.repopulate_userid(m_ses->homeserver, m_ses->access_token, m_ses->userid);
|
||||
}
|
||||
}
|
||||
raii::string session::_request_access_token(const raii::string_base& name, const raii::string_base& pass, const raii::string_base& loginurl)const{
|
||||
CURLcode result;
|
||||
raii::string postdata("{\"type\":\"m.login.password\", \"user\":\"" + raii::json_escape(name) + "\", \"password\":\"" + raii::json_escape(pass) + "\"}");
|
||||
raii::string reply;
|
||||
raii::curler& cur = m_client.m_curl;
|
||||
|
||||
cur.seturl(loginurl);
|
||||
cur.setpostdata(postdata);
|
||||
cur.postreq();
|
||||
cur.setopt(CURLOPT_WRITEFUNCTION, client::_post_reply_curl_callback);
|
||||
cur.setopt(CURLOPT_WRITEDATA, &reply);
|
||||
|
||||
result = cur.perform();
|
||||
|
||||
if(result != CURLE_OK)
|
||||
return {};
|
||||
|
||||
return reply;
|
||||
}
|
||||
raii::rjp_string session::_get_userid(void){
|
||||
auto reply = m_client._get_curl(mat_url_list::stat_whoami(m_ses->homeserver, m_ses->access_token));
|
||||
if(!reply)
|
||||
return {};
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return {};
|
||||
RJP_search_res id = rjp_search_member(root.get(), "user_id", 0);
|
||||
return raii::rjp_string{id.value};
|
||||
}
|
||||
std::pair<raii::rjp_string,raii::rjp_string> session::_get_new_access_token(const raii::string_base& name, const raii::string_base& pass, const raii::string_base& loginurl){
|
||||
raii::string reply = _request_access_token(name, pass, loginurl);
|
||||
if(!reply)
|
||||
return {};
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return {};
|
||||
std::pair<raii::rjp_string,raii::rjp_string> retval;
|
||||
RJP_search_res token = rjp_search_member(root.get(), "access_token", 0);
|
||||
retval.first = raii::rjp_string{token.value};
|
||||
token = rjp_search_member(root.get(), "user_id", 0);
|
||||
retval.second = raii::rjp_string{token.value};
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
156
src/matrix/syncer.cpp
Normal file
156
src/matrix/syncer.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/**
|
||||
This file is a part of 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/syncer.hpp"
|
||||
#include "raii/static_string.hpp"
|
||||
#include "raii/rjp_ptr.hpp"
|
||||
#include "raii/util.hpp"
|
||||
|
||||
namespace matrix{
|
||||
|
||||
syncer::syncer(std::shared_ptr<internal::session_info>& ses):
|
||||
client_base(),
|
||||
m_ses(ses){}
|
||||
|
||||
raii::string syncer::sync(size_t timeout){
|
||||
raii::string reply = _get_curl(m_ses->urls.sync(m_ses->homeserver, m_ses->access_token, m_next_batch, raii::itostr(timeout)));
|
||||
|
||||
if(!reply)
|
||||
return {};
|
||||
raii::rjp_ptr root(rjp_parse(reply));
|
||||
if(!root)
|
||||
return reply;
|
||||
|
||||
RJP_search_res res = rjp_search_member(root.get(), "next_batch", 0);
|
||||
if(!res.value)
|
||||
return reply;
|
||||
m_next_batch = res.value;
|
||||
|
||||
if(m_raw_callback != nullptr){
|
||||
m_raw_callback(root);
|
||||
}
|
||||
res = rjp_search_member(root.get(), "rooms", 0);
|
||||
if(res.value){
|
||||
if(m_message_callback != nullptr)
|
||||
_handle_messages(res.value);
|
||||
if(m_membership_callback != nullptr)
|
||||
_handle_membership_events(res.value);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
static membership_info _membership_info_from_json(RJP_value* event, RJP_value* roomid){
|
||||
static constexpr const char* search_terms[] = {"event_id", "sender", "state_key", "unsigned", "type", "origin_server_ts"};
|
||||
static constexpr size_t num_searches = sizeof(search_terms)/sizeof(search_terms[0]);
|
||||
RJP_search_res results[num_searches] = {};
|
||||
rjp_search_members(event, num_searches, search_terms, results, 0);
|
||||
for(size_t i = 0;i < num_searches;++i){
|
||||
if(!results[i].value)
|
||||
return {};
|
||||
}
|
||||
RJP_search_res age = rjp_search_member(results[3].value, "age", 0);
|
||||
if(!age.value) return {};
|
||||
|
||||
raii::rjp_string room_str = rjp_member_name(roomid);
|
||||
raii::rjp_string sender_str = results[1].value;
|
||||
raii::rjp_string event_str = results[0].value;
|
||||
raii::rjp_string eventtype_str = results[4].value;
|
||||
raii::rjp_string rec_str = results[2].value;
|
||||
|
||||
return membership_info{std::move(room_str), std::move(sender_str),
|
||||
std::move(event_str), std::move(eventtype_str),
|
||||
rjp_value_integer(results[5].value), rjp_value_integer(age.value),
|
||||
std::move(rec_str)};
|
||||
}
|
||||
static msg_info _message_info_from_json(RJP_value* event, RJP_value* roomid){
|
||||
static constexpr const char* searches[] = {"sender", "content", "event_id", "unsigned", "type", "origin_server_ts"};
|
||||
static constexpr size_t num_searches = sizeof(searches)/sizeof(searches[0]);
|
||||
RJP_search_res results[num_searches] = {};
|
||||
rjp_search_members(event, num_searches, searches, results, 0);
|
||||
for(size_t i = 0;i < num_searches;++i){
|
||||
if(!results[i].value)
|
||||
return msg_info{};
|
||||
}
|
||||
RJP_search_res msg = rjp_search_member(results[1].value, "msgtype", 0);
|
||||
if(!msg.value) return {};
|
||||
RJP_search_res body = rjp_search_member(results[1].value, "body", 0);
|
||||
if(!body.value) return {};
|
||||
RJP_search_res age = rjp_search_member(results[3].value, "age", 0);
|
||||
if(!age.value) return msg_info{};
|
||||
raii::rjp_string room_str = rjp_member_name(roomid);
|
||||
raii::rjp_string sender_str = results[0].value;
|
||||
raii::rjp_string eventid_str = results[2].value;
|
||||
raii::rjp_string eventtype_str = results[3].value;
|
||||
raii::rjp_string msgbody_str = body.value;
|
||||
return msg_info{std::move(room_str), std::move(sender_str),
|
||||
std::move(eventid_str), std::move(eventtype_str),
|
||||
rjp_value_integer(results[5].value), rjp_value_integer(age.value),
|
||||
msg::from_str(rjp_value_string(msg.value)), std::move(msgbody_str)};
|
||||
}
|
||||
void syncer::_handle_membership_events(RJP_value* rooms){
|
||||
RJP_search_res res = rjp_search_member(rooms, "invite", 0);
|
||||
if(res.value)
|
||||
_handle_invites(res.value);
|
||||
res = rjp_search_member(rooms, "join", 0);
|
||||
if(res.value)
|
||||
_handle_other_membership(res.value);
|
||||
}
|
||||
void syncer::_handle_other_membership(RJP_value* join){
|
||||
for(RJP_value* roomid = rjp_get_member(join);roomid;roomid = rjp_next_member(roomid)){
|
||||
RJP_search_res res = rjp_search_member(roomid, "timeline", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
membership_info minfo = _membership_info_from_json(event, roomid);
|
||||
if(!minfo.roomid) continue;
|
||||
if(minfo.eventtype != "m.room.member"_ss) continue;
|
||||
|
||||
m_membership_callback(minfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
void syncer::_handle_invites(RJP_value* invites){
|
||||
for(RJP_value* roomid = rjp_get_member(invites);roomid;roomid = rjp_next_member(roomid)){
|
||||
RJP_search_res res = rjp_search_member(roomid, "invite_state", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
membership_info minfo = _membership_info_from_json(event, roomid);
|
||||
if(!minfo.roomid) continue;
|
||||
m_membership_callback(minfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
void syncer::_handle_messages(RJP_value* messages){
|
||||
RJP_search_res res = rjp_search_member(messages, "join", 0);
|
||||
if(!res.value) return;
|
||||
for(RJP_value* roomid = rjp_get_member(res.value);roomid;roomid = rjp_next_member(roomid)){
|
||||
res = rjp_search_member(roomid, "timeline", 0);
|
||||
if(!res.value) continue;
|
||||
res = rjp_search_member(res.value, "events", 0);
|
||||
if(!res.value) continue;
|
||||
for(RJP_value* event = rjp_get_element(res.value);event;event = rjp_next_element(event)){
|
||||
msg_info minfo = _message_info_from_json(event, roomid);
|
||||
m_message_callback(minfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/matrix/url_list.cpp
Normal file
134
src/matrix/url_list.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
This file is a part of 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/url_list.hpp"
|
||||
#include "raii/string.hpp"
|
||||
|
||||
namespace matrix{
|
||||
|
||||
mat_url_list::mat_url_list(const raii::string_base& homeserver){
|
||||
_initial_populate(homeserver);
|
||||
}
|
||||
mat_url_list::mat_url_list(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
repopulate(homeserver, access_token, userid);
|
||||
}
|
||||
void mat_url_list::repopulate_accesstoken(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
m_create_room = s_proto + homeserver + "/_matrix/client/r0/createRoom?access_token=" + access_token;
|
||||
m_file_upload = s_proto + homeserver + "/_matrix/media/r0/upload?access_token=" + access_token;
|
||||
m_room_list = s_proto + homeserver + "/_matrix/client/r0/joined_rooms?access_token=" + access_token;
|
||||
m_whoami = stat_whoami(homeserver, access_token);
|
||||
}
|
||||
void mat_url_list::repopulate_userid(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
m_displayname = s_proto + homeserver + "/_matrix/client/r0/profile/" + userid + "/displayname?access_token=" + access_token;
|
||||
m_profile_picture = s_proto + homeserver + "/_matrix/client/r0/profile/" + userid + "/avatar_url?access_token=" + access_token;
|
||||
}
|
||||
void mat_url_list::repopulate(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid){
|
||||
_initial_populate(homeserver);
|
||||
repopulate_accesstoken(homeserver, access_token);
|
||||
repopulate_userid(homeserver, access_token, userid);
|
||||
}
|
||||
void mat_url_list::invalidate_accesstoken(void){
|
||||
m_create_room.reset();
|
||||
m_file_upload.reset();
|
||||
m_room_list.reset();
|
||||
m_whoami.reset();
|
||||
m_displayname.reset();
|
||||
m_profile_picture.reset();
|
||||
}
|
||||
const raii::string& mat_url_list::create_room(void)const{
|
||||
return m_create_room;
|
||||
}
|
||||
const raii::string& mat_url_list::file_upload(void)const{
|
||||
return m_file_upload;
|
||||
}
|
||||
const raii::string& mat_url_list::room_list(void)const{
|
||||
return m_room_list;
|
||||
}
|
||||
raii::string mat_url_list::login(const raii::string_base& homeserver){
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/login");
|
||||
}
|
||||
raii::string mat_url_list::stat_whoami(const raii::string_base& homeserver, const raii::string_base& access_token){
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/account/whoami?access_token=" + access_token);
|
||||
}
|
||||
const raii::string& mat_url_list::alias_lookup(void)const{
|
||||
return m_alias_lookup;
|
||||
}
|
||||
const raii::string& mat_url_list::whoami(void)const{
|
||||
return m_whoami;
|
||||
}
|
||||
const raii::string& mat_url_list::displayname(void)const{
|
||||
return m_displayname;
|
||||
}
|
||||
const raii::string& mat_url_list::profile_picture(void)const{
|
||||
return m_profile_picture;
|
||||
}
|
||||
raii::string mat_url_list::logout(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/logout?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::join_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/join?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::leave_room(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/leave?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::sync(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& next_batch, const raii::string_base& timeout)const{
|
||||
if(!next_batch)
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/sync?access_token=" + access_token + "&timeout=" + timeout);
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/sync?access_token=" + access_token + "&timeout=" + timeout + "&since=" + next_batch);
|
||||
}
|
||||
raii::string mat_url_list::read_receipt(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/receipt/m.read/" + eventid + "?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::send(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/send/m.room.message?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::redact(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& eventid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/redact/" + eventid + "/0?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::power_level(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/state/m.room.power_levels?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::presence(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& userid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/presence/" + userid + "/status?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::typing(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid, const raii::string_base& userid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/typing/" + userid + "?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::kick(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/kick?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::ban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/ban?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::unban(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/unban?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::invite(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/invite?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::room_members(const raii::string_base& homeserver, const raii::string_base& access_token, const raii::string_base& roomid)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/rooms/" + roomid + "/members?access_token=" + access_token);
|
||||
}
|
||||
raii::string mat_url_list::password(const raii::string_base& homeserver, const raii::string_base& access_token)const{
|
||||
return raii::string(s_proto + homeserver + "/_matrix/client/r0/account/password?access_token=" + access_token);
|
||||
}
|
||||
void mat_url_list::_initial_populate(const raii::string_base& homeserver){
|
||||
m_alias_lookup = s_proto + homeserver + "/_matrix/client/r0/directory/room/";
|
||||
}
|
||||
|
||||
}
|
||||
@ -28,37 +28,15 @@ namespace raii{
|
||||
m_length(len),
|
||||
m_data(nullptr){}
|
||||
string_base& string_base::operator=(const char* c){
|
||||
size_t len = strlen(c);
|
||||
if(len <= m_length){
|
||||
strcpy(m_data, c);
|
||||
}else{
|
||||
_free(m_data);
|
||||
m_data = _copy(c, len+1);
|
||||
if(!m_data){
|
||||
m_length = 0;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
m_length = len;
|
||||
return *this;
|
||||
return _copy_string(c, strlen(c));
|
||||
}
|
||||
string_base& string_base::operator=(const string_base& s){
|
||||
if(s.m_length <= m_length){
|
||||
strcpy(m_data, s.m_data);
|
||||
}else{
|
||||
_free(m_data);
|
||||
m_data = _copy(s.m_data, s.m_length+1);
|
||||
if(!m_data){
|
||||
m_length = 0;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
m_length = s.m_length;
|
||||
return *this;
|
||||
return _copy_string(s.m_data, s.m_length);
|
||||
}
|
||||
void string_base::reset(char* val){
|
||||
_free(m_data);
|
||||
m_data = val;
|
||||
m_length = val ? strlen(val) : 0;
|
||||
}
|
||||
|
||||
size_t string_base::length(void)const{
|
||||
@ -88,5 +66,24 @@ namespace raii{
|
||||
const char& string_base::operator[](size_t i)const{
|
||||
return m_data[i];
|
||||
}
|
||||
string_base& string_base::_copy_string(const char* s, size_t len){
|
||||
if(!len){
|
||||
_free(m_data);
|
||||
m_length = 0;
|
||||
return *this;
|
||||
}
|
||||
if(len <= m_length){
|
||||
strcpy(m_data, s);
|
||||
}else{
|
||||
_free(m_data);
|
||||
m_data = _copy(s, len+1);
|
||||
if(!m_data){
|
||||
m_length = 0;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
m_length = len;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
26
src/test.cpp
26
src/test.cpp
@ -26,23 +26,21 @@
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
|
||||
void sync_fn(matrix::client client, std::atomic_bool& should_quit){
|
||||
auto sync_reply = client.sync(0);
|
||||
client.set_message_callback([&](const matrix::client& client, const matrix::msg_info& msg)->void
|
||||
{
|
||||
if(msg.body == "!exit"_ss){
|
||||
void sync_fn(matrix::syncer& syn, std::atomic_bool& should_quit){
|
||||
auto sync_reply = syn.sync(0);
|
||||
|
||||
syn.set_message_callback([&](const matrix::msg_info& msg)->void{
|
||||
printf("%s\n", msg.body.get());
|
||||
if(!strcmp(msg.body.get(), "!exit")){
|
||||
should_quit = true;
|
||||
client.send_message(msg.roomid, "Shutting down..."_ss);
|
||||
}else if(msg.body == "!info"_ss){
|
||||
client.send_message(msg.roomid, "This is an example of a client which responds to commands!"_ss);
|
||||
}
|
||||
});
|
||||
|
||||
while(!should_quit){
|
||||
sync_reply = client.sync(30000);
|
||||
sync_reply = syn.sync(30000);
|
||||
}
|
||||
}
|
||||
void keyboard_fn(matrix::client client, std::atomic_bool& should_quit){
|
||||
void keyboard_fn(matrix::client& client, std::atomic_bool& should_quit){
|
||||
char buffer[2048];
|
||||
while(!should_quit){
|
||||
fgets(buffer, 2048, stdin);
|
||||
@ -59,13 +57,13 @@ int main(){
|
||||
const char* password = "password";
|
||||
const char* useragent = "rexy712s test bot";
|
||||
const char* homeserver = "matrix.org";
|
||||
matrix::auth_data auth{username, password, homeserver};
|
||||
matrix::auth_data auth{username, password, homeserver, useragent};
|
||||
|
||||
matrix::client matclient(auth, useragent);
|
||||
matrix::session ses(auth);
|
||||
|
||||
std::atomic_bool should_quit = false;
|
||||
std::thread sync_thread(sync_fn, matclient, std::ref(should_quit));
|
||||
std::thread key_thread(keyboard_fn, std::move(matclient), std::ref(should_quit));
|
||||
std::thread sync_thread(sync_fn, std::ref(ses.get_syncer()), std::ref(should_quit));
|
||||
std::thread key_thread(keyboard_fn, std::ref(ses.get_client()), std::ref(should_quit));
|
||||
|
||||
//one of these threads will always hang until another input is recieved
|
||||
sync_thread.join();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user