2018-02-04 03:22:01 +01:00
|
|
|
// Matrix Construct
|
|
|
|
//
|
|
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
|
|
//
|
|
|
|
// 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.
|
2017-08-23 23:10:28 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
#include "sync.h"
|
2017-08-23 23:10:28 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::mapi::header
|
2018-04-11 00:20:47 +02:00
|
|
|
IRCD_MODULE
|
|
|
|
{
|
2018-04-22 08:50:16 +02:00
|
|
|
"Client 6.2.1 :Sync"
|
2018-04-11 00:20:47 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
decltype(ircd::m::sync::resource)
|
|
|
|
ircd::m::sync::resource
|
2018-04-11 00:20:47 +02:00
|
|
|
{
|
|
|
|
"/_matrix/client/r0/sync",
|
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
description
|
2018-04-11 00:20:47 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
decltype(ircd::m::sync::description)
|
|
|
|
ircd::m::sync::description
|
|
|
|
{R"(6.2.1
|
2017-09-26 06:42:07 +02:00
|
|
|
|
|
|
|
Synchronise the client's state with the latest state on the server. Clients
|
|
|
|
use this API when they first log in to get an initial snapshot of the state
|
|
|
|
on the server, and then continue to call this API to get incremental deltas
|
|
|
|
to the state, and to receive new messages.
|
|
|
|
)"};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::conf::item<ircd::milliseconds>
|
|
|
|
ircd::m::sync::args::timeout_max
|
2017-08-23 23:10:28 +02:00
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
{ "name", "ircd.client.sync.timeout.max" },
|
|
|
|
{ "default", 15 * 1000L },
|
2017-08-23 23:10:28 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::conf::item<ircd::milliseconds>
|
|
|
|
ircd::m::sync::args::timeout_min
|
2017-09-26 06:42:07 +02:00
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
{ "name", "ircd.client.sync.timeout.min" },
|
|
|
|
{ "default", 5 * 1000L },
|
2018-04-11 00:20:47 +02:00
|
|
|
};
|
2017-09-26 06:42:07 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::conf::item<ircd::milliseconds>
|
|
|
|
ircd::m::sync::args::timeout_default
|
2017-09-26 06:42:07 +02:00
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
{ "name", "ircd.client.sync.timeout.default" },
|
|
|
|
{ "default", 10 * 1000L },
|
2017-09-25 03:05:42 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
//
|
|
|
|
// GET sync
|
|
|
|
//
|
|
|
|
|
|
|
|
decltype(ircd::m::sync::method_get)
|
|
|
|
ircd::m::sync::method_get
|
2018-05-14 04:23:23 +02:00
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
resource, "GET", handle_get,
|
|
|
|
{
|
|
|
|
method_get.REQUIRES_AUTH,
|
|
|
|
-1s,
|
|
|
|
}
|
2018-05-14 04:23:23 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::resource::response
|
|
|
|
ircd::m::sync::handle_get(client &client,
|
|
|
|
const resource::request &request)
|
2018-09-01 11:57:15 +02:00
|
|
|
try
|
2017-08-23 23:10:28 +02:00
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
const args args
|
|
|
|
{
|
|
|
|
request
|
|
|
|
};
|
2018-04-11 00:20:47 +02:00
|
|
|
|
2019-01-04 02:21:02 +01:00
|
|
|
const std::pair<event::idx, event::idx> range
|
|
|
|
{
|
|
|
|
args.since, // start at since token
|
|
|
|
m::vm::current_sequence // stop at present
|
|
|
|
};
|
|
|
|
|
|
|
|
stats stats;
|
|
|
|
data data
|
2018-04-22 08:50:16 +02:00
|
|
|
{
|
2019-01-04 02:21:02 +01:00
|
|
|
stats, client, request.user_id, range, args.filter_id
|
2018-04-22 08:50:16 +02:00
|
|
|
};
|
|
|
|
|
2019-01-09 00:10:06 +01:00
|
|
|
log::debug
|
|
|
|
{
|
|
|
|
log, "request %s", loghead(data)
|
|
|
|
};
|
|
|
|
|
2019-01-04 02:21:02 +01:00
|
|
|
if(data.since > data.current + 1)
|
2018-04-22 08:50:16 +02:00
|
|
|
throw m::NOT_FOUND
|
|
|
|
{
|
2019-01-04 02:21:02 +01:00
|
|
|
"Since parameter is too far in the future..."
|
2018-04-22 08:50:16 +02:00
|
|
|
};
|
|
|
|
|
2018-11-05 03:11:37 +01:00
|
|
|
const size_t linear_delta_max
|
2018-04-22 08:50:16 +02:00
|
|
|
{
|
2018-11-05 03:11:37 +01:00
|
|
|
linear::delta_max
|
2018-04-22 08:50:16 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
const bool shortpolled
|
|
|
|
{
|
2019-01-09 00:10:06 +01:00
|
|
|
range.first > range.second?
|
2018-04-22 08:50:16 +02:00
|
|
|
false:
|
2019-01-09 00:10:06 +01:00
|
|
|
range.second - range.first <= linear_delta_max?
|
|
|
|
linear::handle(data):
|
|
|
|
polylog::handle(data)
|
2018-09-01 11:57:15 +02:00
|
|
|
};
|
|
|
|
|
2018-12-02 02:23:42 +01:00
|
|
|
// When shortpoll was successful, do nothing else.
|
|
|
|
if(shortpolled)
|
|
|
|
return {};
|
2018-09-01 11:57:15 +02:00
|
|
|
|
2018-12-02 02:23:42 +01:00
|
|
|
// When longpoll was successful, do nothing else.
|
2019-01-09 00:10:06 +01:00
|
|
|
if(longpoll::poll(client, data, args))
|
2018-12-02 02:23:42 +01:00
|
|
|
return {};
|
|
|
|
|
|
|
|
// A user-timeout occurred. According to the spec we return a
|
|
|
|
// 200 with empty fields rather than a 408.
|
|
|
|
const json::value next_batch
|
|
|
|
{
|
2019-01-04 23:47:01 +01:00
|
|
|
lex_cast(m::vm::current_sequence + 1), json::STRING
|
2018-12-02 02:23:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
return resource::response
|
|
|
|
{
|
|
|
|
client, json::members
|
|
|
|
{
|
|
|
|
{ "next_batch", next_batch },
|
|
|
|
{ "rooms", json::object{} },
|
|
|
|
{ "presence", json::object{} },
|
|
|
|
}
|
|
|
|
};
|
2018-04-22 08:50:16 +02:00
|
|
|
}
|
|
|
|
catch(const bad_lex_cast &e)
|
|
|
|
{
|
|
|
|
throw m::BAD_REQUEST
|
2018-04-17 02:57:41 +02:00
|
|
|
{
|
2018-04-22 08:50:16 +02:00
|
|
|
"Since parameter invalid :%s", e.what()
|
2018-04-17 02:57:41 +02:00
|
|
|
};
|
2018-04-22 08:50:16 +02:00
|
|
|
}
|
2018-04-17 02:57:41 +02:00
|
|
|
|
2019-01-09 00:10:06 +01:00
|
|
|
//
|
|
|
|
// polylog
|
|
|
|
//
|
|
|
|
|
|
|
|
bool
|
|
|
|
ircd::m::sync::polylog::handle(data &data)
|
|
|
|
try
|
|
|
|
{
|
|
|
|
data.commit();
|
|
|
|
|
|
|
|
// Top level sync object.
|
|
|
|
json::stack::object object
|
|
|
|
{
|
|
|
|
data.out
|
|
|
|
};
|
|
|
|
|
|
|
|
m::sync::for_each([&data]
|
|
|
|
(item &item)
|
|
|
|
{
|
|
|
|
json::stack::member member
|
|
|
|
{
|
|
|
|
data.out, item.member_name()
|
|
|
|
};
|
|
|
|
|
|
|
|
item.polylog(data);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
const json::value next_batch
|
|
|
|
{
|
|
|
|
lex_cast(data.current + 1), json::STRING
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack::member
|
|
|
|
{
|
|
|
|
object, "next_batch", next_batch
|
|
|
|
};
|
|
|
|
|
|
|
|
log::info
|
|
|
|
{
|
|
|
|
log, "polylog %s (next_batch:%s)",
|
|
|
|
loghead(data),
|
|
|
|
string_view{next_batch}
|
|
|
|
};
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch(const std::exception &e)
|
|
|
|
{
|
|
|
|
log::error
|
|
|
|
{
|
|
|
|
log, "polylog %s FAILED :%s",
|
|
|
|
loghead(data),
|
|
|
|
e.what()
|
|
|
|
};
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
2018-10-07 07:17:46 +02:00
|
|
|
//
|
|
|
|
// longpoll
|
|
|
|
//
|
|
|
|
|
|
|
|
decltype(ircd::m::sync::longpoll::notified)
|
|
|
|
ircd::m::sync::longpoll::notified
|
|
|
|
{
|
|
|
|
handle_notify,
|
|
|
|
{
|
|
|
|
{ "_site", "vm.notify" },
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
ircd::m::sync::longpoll::handle_notify(const m::event &event,
|
|
|
|
m::vm::eval &eval)
|
|
|
|
{
|
|
|
|
assert(eval.opts);
|
|
|
|
if(!eval.opts->notify_clients)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(!polling)
|
2018-11-16 03:36:26 +01:00
|
|
|
{
|
|
|
|
queue.clear();
|
2018-10-07 07:17:46 +02:00
|
|
|
return;
|
2018-11-16 03:36:26 +01:00
|
|
|
}
|
2018-10-07 07:17:46 +02:00
|
|
|
|
|
|
|
queue.emplace_back(eval);
|
|
|
|
dock.notify_all();
|
|
|
|
}
|
|
|
|
|
2018-12-02 02:23:42 +01:00
|
|
|
bool
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::m::sync::longpoll::poll(client &client,
|
2019-01-09 00:10:06 +01:00
|
|
|
data &data,
|
2018-09-01 09:51:40 +02:00
|
|
|
const args &args)
|
2017-09-25 03:05:42 +02:00
|
|
|
{
|
2018-10-07 07:17:46 +02:00
|
|
|
++polling;
|
|
|
|
const unwind unpoll{[]
|
2017-09-26 06:42:07 +02:00
|
|
|
{
|
2018-10-07 07:17:46 +02:00
|
|
|
--polling;
|
|
|
|
}};
|
2017-09-25 03:05:42 +02:00
|
|
|
|
2018-04-22 08:50:16 +02:00
|
|
|
while(1)
|
2017-09-26 06:42:07 +02:00
|
|
|
{
|
2018-11-16 03:36:26 +01:00
|
|
|
if(!dock.wait_until(args.timesout))
|
2018-12-02 02:23:42 +01:00
|
|
|
return false;
|
2018-11-16 03:36:26 +01:00
|
|
|
|
2018-10-07 07:17:46 +02:00
|
|
|
if(queue.empty())
|
2018-05-28 04:56:04 +02:00
|
|
|
continue;
|
|
|
|
|
2018-10-07 07:17:46 +02:00
|
|
|
const auto &a(queue.front());
|
|
|
|
const unwind pop{[]
|
|
|
|
{
|
2018-11-16 03:36:26 +01:00
|
|
|
if(polling <= 1)
|
|
|
|
queue.pop_front();
|
2018-10-07 07:17:46 +02:00
|
|
|
}};
|
|
|
|
|
2019-01-09 00:10:06 +01:00
|
|
|
if(handle(client, data, args, a))
|
2018-12-02 02:23:42 +01:00
|
|
|
return true;
|
2018-04-11 00:20:47 +02:00
|
|
|
}
|
|
|
|
}
|
2017-09-25 03:05:42 +02:00
|
|
|
|
2018-04-22 08:50:16 +02:00
|
|
|
bool
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::m::sync::longpoll::handle(client &client,
|
2019-01-09 00:10:06 +01:00
|
|
|
data &data,
|
2018-09-01 09:51:40 +02:00
|
|
|
const args &args,
|
2018-10-07 07:17:46 +02:00
|
|
|
const accepted &event)
|
2017-09-26 06:42:07 +02:00
|
|
|
{
|
|
|
|
const auto &room_id
|
|
|
|
{
|
2017-10-05 01:40:02 +02:00
|
|
|
json::get<"room_id"_>(event)
|
2017-09-26 06:42:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if(room_id)
|
2018-09-01 09:51:40 +02:00
|
|
|
{
|
|
|
|
const m::room room{room_id};
|
2019-01-09 00:10:06 +01:00
|
|
|
return handle(client, data, args, event, room);
|
2018-09-01 09:51:40 +02:00
|
|
|
}
|
2018-04-22 08:50:16 +02:00
|
|
|
|
|
|
|
return false;
|
2017-09-26 06:42:07 +02:00
|
|
|
}
|
|
|
|
|
2018-04-22 08:50:16 +02:00
|
|
|
bool
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::m::sync::longpoll::handle(client &client,
|
2019-01-09 00:10:06 +01:00
|
|
|
data &data,
|
2018-09-01 09:51:40 +02:00
|
|
|
const args &args,
|
2018-10-07 07:17:46 +02:00
|
|
|
const accepted &event,
|
2018-09-01 09:51:40 +02:00
|
|
|
const m::room &room)
|
2017-09-26 06:42:07 +02:00
|
|
|
{
|
2018-04-22 08:50:16 +02:00
|
|
|
const m::user::id &user_id
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2018-09-01 11:57:15 +02:00
|
|
|
args.request.user_id
|
2018-04-22 08:50:16 +02:00
|
|
|
};
|
2017-10-03 13:12:54 +02:00
|
|
|
|
2018-04-22 08:50:16 +02:00
|
|
|
if(!room.membership(user_id, "join"))
|
|
|
|
return false;
|
2018-04-11 00:20:47 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
const auto rooms
|
|
|
|
{
|
2018-09-01 11:57:15 +02:00
|
|
|
sync_rooms(client, user_id, room, args, event)
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const m::user::room ur
|
|
|
|
{
|
2018-09-01 11:57:15 +02:00
|
|
|
m::user::id
|
|
|
|
{
|
|
|
|
args.request.user_id
|
|
|
|
}
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<json::value> presents;
|
|
|
|
ur.get(std::nothrow, "ircd.presence", [&]
|
|
|
|
(const m::event &event)
|
|
|
|
{
|
|
|
|
const auto &content
|
|
|
|
{
|
|
|
|
at<"content"_>(event)
|
|
|
|
};
|
|
|
|
|
|
|
|
presents.emplace_back(event);
|
|
|
|
});
|
|
|
|
|
|
|
|
const json::members presence
|
|
|
|
{
|
|
|
|
{ "events", json::value { presents.data(), presents.size() } },
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto &next_batch
|
|
|
|
{
|
2019-01-09 00:10:06 +01:00
|
|
|
int64_t(data.current + 1)
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
resource::response
|
|
|
|
{
|
|
|
|
client, json::members
|
|
|
|
{
|
|
|
|
{ "next_batch", json::value { lex_cast(next_batch), json::STRING } },
|
|
|
|
{ "rooms", rooms },
|
|
|
|
{ "presence", presence },
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-22 08:50:16 +02:00
|
|
|
return true;
|
2017-10-03 13:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::m::sync::longpoll::sync_rooms(client &client,
|
|
|
|
const m::user::id &user_id,
|
|
|
|
const m::room &room,
|
|
|
|
const args &args,
|
2018-10-07 07:17:46 +02:00
|
|
|
const accepted &event)
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2018-09-13 10:45:12 +02:00
|
|
|
std::vector<std::string> r;
|
|
|
|
std::vector<json::member> m;
|
|
|
|
|
|
|
|
thread_local char membership_buf[64];
|
|
|
|
const auto membership
|
|
|
|
{
|
|
|
|
room.membership(membership_buf, user_id)
|
|
|
|
};
|
|
|
|
|
|
|
|
r.emplace_back(sync_room(client, room, args, event));
|
|
|
|
m.emplace_back(room.room_id, r.back());
|
|
|
|
|
|
|
|
const json::strung body
|
|
|
|
{
|
|
|
|
m.data(), m.data() + m.size()
|
|
|
|
};
|
|
|
|
|
|
|
|
return json::strung{json::members
|
|
|
|
{
|
|
|
|
{ membership, body }
|
|
|
|
}};
|
2018-09-01 09:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
ircd::m::sync::longpoll::sync_room(client &client,
|
|
|
|
const m::room &room,
|
|
|
|
const args &args,
|
2018-10-07 07:17:46 +02:00
|
|
|
const accepted &accepted)
|
2018-09-01 09:51:40 +02:00
|
|
|
{
|
|
|
|
const auto &since
|
|
|
|
{
|
|
|
|
args.since
|
|
|
|
};
|
|
|
|
|
2018-09-06 04:32:14 +02:00
|
|
|
const m::event &event{accepted};
|
2017-10-03 13:12:54 +02:00
|
|
|
std::vector<std::string> timeline;
|
2018-09-07 08:08:20 +02:00
|
|
|
if(defined(json::get<"event_id"_>(event)))
|
2018-09-06 04:32:14 +02:00
|
|
|
{
|
|
|
|
json::strung strung(event);
|
2018-10-07 07:17:46 +02:00
|
|
|
if(!!accepted.client_txnid)
|
2018-09-06 04:32:14 +02:00
|
|
|
strung = json::insert(strung, json::member
|
|
|
|
{
|
|
|
|
"unsigned", json::members
|
|
|
|
{
|
2018-10-07 07:17:46 +02:00
|
|
|
{ "transaction_id", accepted.client_txnid }
|
2018-09-06 04:32:14 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
timeline.emplace_back(std::move(strung));
|
|
|
|
}
|
2017-10-03 13:12:54 +02:00
|
|
|
|
2018-02-11 22:38:41 +01:00
|
|
|
const json::strung timeline_serial
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2018-02-11 22:38:41 +01:00
|
|
|
timeline.data(), timeline.data() + timeline.size()
|
2017-10-03 13:12:54 +02:00
|
|
|
};
|
|
|
|
|
2018-02-12 23:44:46 +01:00
|
|
|
std::vector<std::string> ephemeral;
|
2018-04-22 08:50:16 +02:00
|
|
|
if(json::get<"type"_>(event) == "m.typing") //TODO: X
|
|
|
|
ephemeral.emplace_back(json::strung{event});
|
|
|
|
|
|
|
|
if(json::get<"type"_>(event) == "m.receipt") //TODO: X
|
|
|
|
ephemeral.emplace_back(json::strung(event));
|
|
|
|
|
2018-02-12 23:44:46 +01:00
|
|
|
const json::strung ephemeral_serial
|
|
|
|
{
|
|
|
|
ephemeral.data(), ephemeral.data() + ephemeral.size()
|
|
|
|
};
|
|
|
|
|
2018-04-11 00:20:47 +02:00
|
|
|
const auto &prev_batch
|
|
|
|
{
|
|
|
|
!timeline.empty()?
|
|
|
|
unquote(json::object{timeline.front()}.get("event_id")):
|
|
|
|
string_view{}
|
|
|
|
};
|
|
|
|
|
|
|
|
//TODO: XXX
|
|
|
|
const bool limited
|
|
|
|
{
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
2018-10-14 12:17:21 +02:00
|
|
|
m::event::id::buf last_read_buf;
|
|
|
|
const auto &last_read
|
2018-04-22 08:50:16 +02:00
|
|
|
{
|
2018-10-14 12:17:21 +02:00
|
|
|
m::receipt::read(last_read_buf, room.room_id, args.request.user_id)
|
2018-09-07 15:25:40 +02:00
|
|
|
};
|
|
|
|
|
2018-09-14 15:21:19 +02:00
|
|
|
const auto last_read_idx
|
2018-09-07 15:25:40 +02:00
|
|
|
{
|
|
|
|
last_read && json::get<"event_id"_>(event)?
|
2018-09-14 15:21:19 +02:00
|
|
|
index(last_read):
|
|
|
|
0UL
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto current_idx
|
|
|
|
{
|
|
|
|
last_read_idx?
|
|
|
|
index(at<"event_id"_>(event)):
|
2018-09-07 15:25:40 +02:00
|
|
|
0UL
|
2018-04-22 08:50:16 +02:00
|
|
|
};
|
|
|
|
|
2018-09-14 15:21:19 +02:00
|
|
|
const auto notes
|
|
|
|
{
|
|
|
|
last_read_idx?
|
|
|
|
notification_count(room, last_read_idx, current_idx):
|
|
|
|
json::undefined_number
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto highlights
|
|
|
|
{
|
|
|
|
last_read_idx?
|
|
|
|
highlight_count(room, args.request.user_id, last_read_idx, current_idx):
|
|
|
|
json::undefined_number
|
|
|
|
};
|
|
|
|
|
2017-10-03 13:12:54 +02:00
|
|
|
const json::members body
|
|
|
|
{
|
2018-02-12 23:44:46 +01:00
|
|
|
{ "account_data", json::members{} },
|
|
|
|
{ "unread_notifications",
|
|
|
|
{
|
2018-09-14 15:21:19 +02:00
|
|
|
{ "highlight_count", highlights },
|
|
|
|
{ "notification_count", notes },
|
2018-02-12 23:44:46 +01:00
|
|
|
}},
|
|
|
|
{ "ephemeral",
|
|
|
|
{
|
|
|
|
{ "events", ephemeral_serial },
|
|
|
|
}},
|
|
|
|
{ "timeline",
|
|
|
|
{
|
2018-04-11 00:20:47 +02:00
|
|
|
{ "events", timeline_serial },
|
|
|
|
{ "prev_batch", prev_batch },
|
|
|
|
{ "limited", limited },
|
2018-02-12 23:44:46 +01:00
|
|
|
}},
|
2017-10-03 13:12:54 +02:00
|
|
|
};
|
|
|
|
|
2017-10-16 06:18:42 +02:00
|
|
|
return json::strung(body);
|
2017-10-03 13:12:54 +02:00
|
|
|
}
|
|
|
|
|
2018-10-07 07:17:46 +02:00
|
|
|
//
|
|
|
|
// linear
|
|
|
|
//
|
|
|
|
|
2018-11-05 03:11:37 +01:00
|
|
|
decltype(ircd::m::sync::linear::delta_max)
|
|
|
|
ircd::m::sync::linear::delta_max
|
|
|
|
{
|
|
|
|
{ "name", "ircd.client.sync.linear.delta.max" },
|
|
|
|
{ "default", 1024 },
|
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
bool
|
2019-01-04 23:47:01 +01:00
|
|
|
ircd::m::sync::linear::handle(data &data)
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2019-01-04 23:47:01 +01:00
|
|
|
auto &client{data.client};
|
|
|
|
json::stack::object object
|
|
|
|
{
|
|
|
|
data.out
|
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
uint64_t since
|
|
|
|
{
|
2019-01-04 23:47:01 +01:00
|
|
|
data.since
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|
2017-10-03 13:12:54 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
std::map<std::string, std::vector<std::string>, std::less<>> r;
|
|
|
|
|
|
|
|
bool limited{false};
|
|
|
|
m::events::for_each(since, [&]
|
|
|
|
(const uint64_t &sequence, const m::event &event)
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2019-01-04 23:47:01 +01:00
|
|
|
if(!r.empty() && (since - data.since > 128))
|
2018-09-01 09:51:40 +02:00
|
|
|
{
|
|
|
|
limited = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
since = sequence;
|
|
|
|
if(!json::get<"room_id"_>(event))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
const m::room room
|
|
|
|
{
|
|
|
|
json::get<"room_id"_>(event)
|
|
|
|
};
|
|
|
|
|
2019-01-04 23:47:01 +01:00
|
|
|
if(!room.membership(data.user.user_id))
|
2018-09-01 09:51:40 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
auto it
|
|
|
|
{
|
|
|
|
r.lower_bound(room.room_id)
|
|
|
|
};
|
|
|
|
|
|
|
|
if(it == end(r) || it->first != room.room_id)
|
|
|
|
it = r.emplace_hint(it, std::string{room.room_id}, std::vector<std::string>{});
|
|
|
|
|
|
|
|
it->second.emplace_back(json::strung{event});
|
|
|
|
return true;
|
2017-10-03 13:12:54 +02:00
|
|
|
});
|
2017-09-26 06:42:07 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
if(r.empty())
|
|
|
|
return false;
|
2017-10-03 13:12:54 +02:00
|
|
|
|
2018-09-13 10:45:12 +02:00
|
|
|
std::vector<json::member> events[3];
|
2018-04-11 00:20:47 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
for(auto &p : r)
|
2018-04-11 00:20:47 +02:00
|
|
|
{
|
2018-09-14 15:21:19 +02:00
|
|
|
const m::room::id &room_id{p.first};
|
2018-09-01 09:51:40 +02:00
|
|
|
auto &vec{p.second};
|
|
|
|
|
|
|
|
std::vector<std::string> timeline;
|
|
|
|
std::vector<std::string> state;
|
|
|
|
std::vector<std::string> ephemeral;
|
|
|
|
|
|
|
|
for(std::string &event : vec)
|
|
|
|
if(json::object{event}.has("state_key"))
|
|
|
|
state.emplace_back(std::move(event));
|
|
|
|
else if(!json::object{event}.has("prev_events"))
|
|
|
|
ephemeral.emplace_back(std::move(event));
|
|
|
|
else
|
|
|
|
timeline.emplace_back(std::move(event));
|
|
|
|
|
|
|
|
const json::strung timeline_serial{timeline.data(), timeline.data() + timeline.size()};
|
|
|
|
const json::strung state_serial{state.data(), state.data() + state.size()};
|
|
|
|
const json::strung ephemeral_serial{ephemeral.data(), ephemeral.data() + ephemeral.size()};
|
|
|
|
|
2018-10-14 12:17:21 +02:00
|
|
|
m::event::id::buf last_read_buf;
|
|
|
|
const auto &last_read
|
2018-09-07 15:25:40 +02:00
|
|
|
{
|
2019-01-04 23:47:01 +01:00
|
|
|
m::receipt::read(last_read_buf, room_id, data.user)
|
2018-09-07 15:25:40 +02:00
|
|
|
};
|
|
|
|
|
2018-09-14 15:21:19 +02:00
|
|
|
const auto last_read_idx
|
2018-09-07 15:25:40 +02:00
|
|
|
{
|
|
|
|
last_read?
|
2018-09-14 15:21:19 +02:00
|
|
|
index(last_read):
|
2018-09-07 15:25:40 +02:00
|
|
|
0UL
|
|
|
|
};
|
|
|
|
|
2018-09-14 15:21:19 +02:00
|
|
|
const auto notes
|
|
|
|
{
|
|
|
|
last_read_idx?
|
2019-01-04 23:47:01 +01:00
|
|
|
notification_count(room_id, last_read_idx, data.current):
|
2018-09-14 15:21:19 +02:00
|
|
|
json::undefined_number
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto highlights
|
|
|
|
{
|
|
|
|
last_read_idx?
|
2019-01-04 23:47:01 +01:00
|
|
|
highlight_count(room_id, data.user, last_read_idx, data.current):
|
2018-09-14 15:21:19 +02:00
|
|
|
json::undefined_number
|
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
const string_view prev_batch
|
2018-04-11 00:20:47 +02:00
|
|
|
{
|
2018-09-01 09:51:40 +02:00
|
|
|
!timeline.empty()?
|
|
|
|
unquote(json::object{timeline.front()}.at("event_id")):
|
|
|
|
string_view{}
|
2018-04-11 00:20:47 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
const json::members body
|
|
|
|
{
|
|
|
|
{ "ephemeral",
|
|
|
|
{
|
|
|
|
{ "events", ephemeral_serial },
|
|
|
|
}},
|
|
|
|
{ "state",
|
|
|
|
{
|
|
|
|
{ "events", state_serial }
|
|
|
|
}},
|
|
|
|
{ "timeline",
|
|
|
|
{
|
|
|
|
{ "events", timeline_serial },
|
|
|
|
{ "prev_batch", prev_batch },
|
|
|
|
{ "limited", limited },
|
|
|
|
}},
|
2018-09-07 15:25:40 +02:00
|
|
|
{ "unread_notifications",
|
|
|
|
{
|
2018-09-14 15:21:19 +02:00
|
|
|
{ "highlight_count", highlights },
|
|
|
|
{ "notification_count", notes },
|
2018-09-07 15:25:40 +02:00
|
|
|
}},
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|
2018-04-11 00:20:47 +02:00
|
|
|
|
2018-09-13 10:45:12 +02:00
|
|
|
thread_local char membership_buf[64];
|
2019-01-04 23:47:01 +01:00
|
|
|
const auto membership{m::room{room_id}.membership(membership_buf, data.user)};
|
2018-09-13 10:45:12 +02:00
|
|
|
const int ep
|
|
|
|
{
|
|
|
|
membership == "join"? 0:
|
|
|
|
membership == "leave"? 1:
|
|
|
|
membership == "invite"? 2:
|
|
|
|
1 // default to leave (catches "ban" for now)
|
|
|
|
};
|
|
|
|
|
|
|
|
events[ep].emplace_back(room_id, body);
|
|
|
|
};
|
|
|
|
|
|
|
|
const json::value joinsv
|
|
|
|
{
|
|
|
|
events[0].data(), events[0].size()
|
|
|
|
};
|
|
|
|
|
|
|
|
const json::value leavesv
|
|
|
|
{
|
|
|
|
events[1].data(), events[1].size()
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|
|
|
|
|
2018-09-13 10:45:12 +02:00
|
|
|
const json::value invitesv
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2018-09-13 10:45:12 +02:00
|
|
|
events[2].data(), events[2].size()
|
2017-10-03 13:12:54 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
const json::members rooms
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2018-09-13 10:45:12 +02:00
|
|
|
{ "join", joinsv },
|
|
|
|
{ "leave", leavesv },
|
|
|
|
{ "invite", invitesv },
|
2017-10-03 13:12:54 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
resource::response
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2018-04-22 08:50:16 +02:00
|
|
|
client, json::members
|
2017-10-03 13:12:54 +02:00
|
|
|
{
|
2019-01-09 00:10:06 +01:00
|
|
|
{ "next_batch", json::value { lex_cast(int64_t(data.current + 1)), json::STRING } },
|
2018-09-01 09:51:40 +02:00
|
|
|
{ "rooms", rooms },
|
|
|
|
{ "presence", json::object{} },
|
2017-10-03 13:12:54 +02:00
|
|
|
}
|
|
|
|
};
|
2018-04-22 08:50:16 +02:00
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
return true;
|
|
|
|
}
|
2018-04-22 08:50:16 +02:00
|
|
|
|
2019-01-04 02:21:02 +01:00
|
|
|
static long
|
|
|
|
ircd::m::sync::notification_count(const m::room &room,
|
|
|
|
const m::event::idx &a,
|
|
|
|
const m::event::idx &b)
|
|
|
|
{
|
|
|
|
return m::count_since(room, a, a < b? b : a);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
ircd::m::sync::highlight_count(const m::room &r,
|
|
|
|
const m::user &u,
|
|
|
|
const m::event::idx &a,
|
|
|
|
const m::event::idx &b)
|
|
|
|
{
|
|
|
|
using namespace ircd::m;
|
|
|
|
using proto = size_t (const user &, const room &, const event::idx &, const event::idx &);
|
|
|
|
|
|
|
|
static mods::import<proto> count
|
|
|
|
{
|
|
|
|
"m_user", "highlighted_count__between"
|
|
|
|
};
|
|
|
|
|
|
|
|
return count(u, r, a, a < b? b : a);
|
|
|
|
}
|