/** 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; } }