157 lines
6.1 KiB
C++
157 lines
6.1 KiB
C++
/**
|
|
This file is a part of r0nk, atlas_moon, and rexy's matrix client
|
|
Copyright (C) 2019 rexy712
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "matrix/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);
|
|
}
|
|
}
|
|
}
|
|
}
|