/**
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 .
*/
#include "matrix/session.hpp"
#include "raii/rjp_ptr.hpp"
#include "raii/curler.hpp"
#include "raii/util.hpp"
#include "raii/string_base.hpp"
#include "matrix/fat_strings.hpp"
#include "matrix/json_targets.hpp"
namespace matrix{
static raii::string create_auth_header(const raii::string_base& token){
return raii::string("Authorization: Bearer " + token);
}
session::session(void):
connection(std::make_shared()){}
session::session(const auth_data& auth):
connection(std::make_shared())
{
_populate_session_info(auth);
_set_curl_defaults(m_ses->useragent);
}
session::~session(void){
invalidate();
}
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);
}
void session::set_homeserver(const raii::string_base& hs){
m_ses->homeserver = hs;
}
void session::set_homeserver(raii::string&& hs){
m_ses->homeserver = std::move(hs);
}
void session::set_access_token(const raii::string_base& tok){
m_ses->access_token = tok;
m_ses->auth_header = create_auth_header(m_ses->access_token);
}
void session::set_access_token(raii::string&& tok){
m_ses->access_token = std::move(tok);
m_ses->auth_header = create_auth_header(m_ses->access_token);
}
netreturn session::login(void){
return _do_login(raii::string(), raii::string());
}
netreturn session::login(const raii::string_base& username, const raii::string_base& pass){
return _do_login(username, pass);
}
netreturn session::logout(void){
raii::string reply = _post_curl(raii::string(), m_ses->urls.logout(m_ses->homeserver, m_ses->access_token), raii::curl_llist());
return _create_netreturn(reply, http_status());
}
bool session::valid(void)const{
return m_valid;
}
netreturn session::change_password(const raii::string_base& oldpass, const raii::string_base& newpass){
raii::string reply = _post_curl(json::_empty(), m_ses->urls.password(m_ses->homeserver, m_ses->access_token), raii::curl_llist());
if(!reply)
return _create_netreturn(reply, http_status());
raii::rjp_ptr root(rjp_parse(reply));
netreturn retval = _create_netreturn(root, http_status());
if(!root)
return retval;
//attempt to change password via username/password login
RJP_search_res res = rjp_search_member(root.get(), json::session::session(), 0);
raii::string request = json::_change_psk_password(m_ses->userid, newpass, oldpass, raii::rjp_string(res.value));
reply = _post_curl(request, m_ses->urls.password(m_ses->homeserver, m_ses->access_token), raii::curl_llist());
return _create_netreturn(reply, http_status());
}
client session::spawn_client(void)const{
return client(m_ses);
}
syncer session::spawn_syncer(void)const{
return syncer(m_ses);
}
void session::invalidate(void){
m_valid = false;
m_ses->useragent.reset();
m_ses->homeserver.reset();
m_ses->access_token.reset();
m_ses->userid.reset();
m_ses->urls.invalidate_accesstoken();
}
netreturn session::_do_login(const raii::string_base& username, const raii::string_base& pass){
auto reply = _get_curl(client_url_list::stat_whoami(m_ses->homeserver, m_ses->access_token));
netreturn retval = _create_netreturn(reply, http_status());
if(!retval.ok()){
if(!username || !pass){
m_valid = false;
return retval;
}
auto newtoken = _get_new_access_token(username, pass, client_url_list::login(m_ses->homeserver));
auto& token = newtoken.value().first;
auto& id = newtoken.value().second;
if(token && id){
m_ses->access_token = std::move(token);
m_ses->auth_header = create_auth_header(m_ses->access_token);
m_ses->userid = std::move(id);
m_ses->urls.repopulate(m_ses->homeserver, m_ses->access_token, m_ses->userid);
m_valid = true;
}else{
m_valid = false;
}
return netreturn(std::move(newtoken.mxerror()), std::move(newtoken.mxerrorcode()), newtoken.httpstatus());
}else{
m_ses->urls.repopulate_accesstoken(m_ses->homeserver, m_ses->access_token);
netreturn uid = _get_userid();
if(!uid.ok()){
invalidate();
return netreturn(std::move(uid.mxerror()), std::move(uid.mxerrorcode()), uid.httpstatus());
}
m_ses->userid = std::move(uid.value());
m_ses->urls.repopulate_userid(m_ses->homeserver, m_ses->access_token, m_ses->userid);
m_valid = true;
return retval;
}
}
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;
if(m_ses->access_token)
m_ses->auth_header = create_auth_header(m_ses->access_token);
_do_login(a.name, a.pass);
}
raii::string session::_request_access_token(const raii::string_base& name, const raii::string_base& pass, const raii::string_base& loginurl)const{
raii::string postdata = json::_login_password(name, pass);
raii::string reply = _post_curl(postdata, loginurl, raii::curl_llist{});
return reply;
}
netreturn session::_get_userid(void){
return _get_and_find(client_url_list::stat_whoami(m_ses->homeserver, m_ses->access_token), json::session::userid());
}
netreturn> 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 _create_netreturn(reply, http_status());
raii::rjp_ptr root(rjp_parse(reply));
netreturn> retval = _create_netreturn(root, http_status());
if(!root)
return retval;
RJP_search_res token = rjp_search_member(root.get(), json::session::accesstoken(), 0);
retval.value().first = raii::rjp_string{token.value};
token = rjp_search_member(root.get(), json::session::userid(), 0);
retval.value().second = raii::rjp_string{token.value};
return retval;
}
}