// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. namespace ircd::m { static string_view gen_password_hash(const mutable_buffer &, const string_view &); static room create_user_room(const user::id &, const room::id &, const json::members &contents); } ircd::m::user::reading::reading(const user &user) { if(!(room_id = viewing(user))) return; const user::room user_room { user }; const auto last_event_idx { user_room.get(std::nothrow, "ircd.read", room_id) }; const bool last_content_prefetched { prefetch(last_event_idx, "content") }; time_t last_ots {0}; get(last_event_idx, "origin_server_ts", last_ots); this->last_ots = milliseconds(last_ots) / 1000; get(std::nothrow, last_event_idx, "content", [this] (const json::object &content) { this->last_ts = content.get("ts"); this->last = json::string { content["event_id"] }; }); const user::room_account_data room_account_data { user, room_id }; room_account_data.get(std::nothrow, "m.fully_read", [this] (const string_view &key, const json::object &content) { this->full = json::string { content["event_id"] }; }); //TODO: XXX // full_ots presence::get(std::nothrow, user, [this] (const json::object &event) { this->currently_active = event.get("currently_active", false); }); } ircd::m::room::id::buf ircd::m::viewing(const user &user, size_t i) { const m::breadcrumbs breadcrumbs { user }; room::id::buf ret; breadcrumbs.for_each([&ret, &i] (const auto &room_id) { if(i-- > 0) return true; ret = room_id; return false; }); return ret; } bool ircd::m::is_oper(const user &user) { const m::room::id::buf control_room_id { "!control", my_host() }; return m::membership(control_room_id, user, "join"); } bool ircd::m::active(const user &user) { const m::user::room user_room { user.user_id }; const m::event::idx &event_idx { user_room.get(std::nothrow, "ircd.account", "active") }; return m::query(std::nothrow, event_idx, "content", [] (const json::object &content) { return content.get("value", false); }); } bool ircd::m::exists(const user &user) { return exists(user.user_id); } bool ircd::m::exists(const user::id &user) { // The way we know a user exists is testing if their room exists. const user::room user_room { user }; return m::exists(user_room); } bool ircd::m::my(const user &user) { return my(user.user_id); } ircd::m::user ircd::m::create(const m::user::id &user_id, const json::members &contents) { const m::user user { user_id }; const m::room::id::buf room_id { user.room_id() }; const m::room room { create_user_room(user_id, room_id, contents) }; return user; } ircd::m::room ircd::m::create_user_room(const user::id &user_id, const room::id &room_id, const json::members &contents) try { return create(room_id, me(), "user"); } catch(const std::exception &e) { if(m::exists(room_id)) return room_id; log::error { log, "Failed to create user %s room %s :%s", string_view{user_id}, string_view{room_id}, e.what() }; throw; } // // user::user // /// Generates a user-room ID into buffer; see room_id() overload. ircd::m::id::room::buf ircd::m::user::room_id() const { ircd::m::id::room::buf buf; return buf.assigned(room_id(buf)); } /// This generates a room mxid for the "user's room" essentially serving as /// a database mechanism for this specific user. This room_id is a hash of /// the user's full mxid. /// ircd::m::id::room ircd::m::user::room_id(const mutable_buffer &buf) const { assert(!empty(user_id)); const ripemd160::buf hash { ripemd160{user_id} }; char b58[size(hash) * 2]; return { buf, b58::encode(b58, hash), origin(my()) }; } ircd::m::event::id::buf ircd::m::user::oper() { const m::room::id::buf control_room_id { "!control", origin(my()) }; const m::room control_room { control_room_id }; return m::join(control_room, user_id); } ircd::m::event::id::buf ircd::m::user::deoper() { const m::room::id::buf control_room_id { "!control", origin(my()) }; const m::room control_room { control_room_id }; return m::leave(control_room, user_id); } ircd::m::event::id::buf ircd::m::user::activate() { const m::user::room user_room { user_id }; return send(user_room, m::me(), "ircd.account", "active", json::members { { "value", true } }); } ircd::m::event::id::buf ircd::m::user::deactivate() { const m::user::room user_room { user_id }; return send(user_room, m::me(), "ircd.account", "active", json::members { { "value", false } }); } ircd::m::event::id::buf ircd::m::user::password(const string_view &password) { char buf[64]; const auto supplied { gen_password_hash(buf, password) }; const m::user::room user_room { user_id }; return send(user_room, user_id, "ircd.password", user_id, { { "sha256", supplied } }); } bool ircd::m::user::is_password(const string_view &password) const try { char buf[64]; const auto supplied { gen_password_hash(buf, password) }; bool ret{false}; const m::user::room user_room { user_id }; const ctx::uninterruptible::nothrow ui; user_room.get("ircd.password", user_id, [&supplied, &ret] (const m::event &event) { const json::object &content { json::at<"content"_>(event) }; const auto &correct { unquote(content.at("sha256")) }; ret = supplied == correct; }); return ret; } catch(const m::NOT_FOUND &e) { return false; } catch(const std::exception &e) { log::critical { "is_password__user() :%s %s", string_view{user_id}, e.what() }; return false; } ircd::string_view ircd::m::gen_password_hash(const mutable_buffer &out, const string_view &supplied_password) { //TODO: ADD SALT const sha256::buf hash { sha256{supplied_password} }; return b64::encode_unpadded(out, hash); } // // user::room // ircd::m::user::room::room(const m::user::id &user_id, const vm::copts *const &copts, const event::fetch::opts *const &fopts) :room { m::user{user_id}, copts, fopts } { } ircd::m::user::room::room(const m::user &user, const vm::copts *const &copts, const event::fetch::opts *const &fopts) :user{user} ,room_id{user.room_id()} { static_cast(*this) = { room_id, copts, fopts }; } bool ircd::m::user::room::is(const room::id &room_id, const user::id &user_id) { const user::room user_room{user_id}; return user_room.room_id == room_id; }