Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
chrzaszcz committed Jul 26, 2021
1 parent eb68bf9 commit a6eaf5d
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 180 deletions.
6 changes: 6 additions & 0 deletions priv/mssql2012.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,20 @@ GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[last](
[server] nvarchar](250) NOT NULL,
[username] [nvarchar](250) NOT NULL,
[seconds] [int] NOT NULL,
[state] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_last_username] PRIMARY KEY CLUSTERED
(
[server] ASC
[username] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

CREATE INDEX i_last_server_seconds ON last (server, seconds);
GO

GO
SET ANSI_PADDING OFF
Expand Down
11 changes: 5 additions & 6 deletions priv/mysql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,16 @@ CREATE TABLE users (
) CHARACTER SET utf8mb4
ROW_FORMAT=DYNAMIC;


CREATE TABLE last (
username varchar(250) PRIMARY KEY,
server varchar(250),
username varchar(250),
seconds int NOT NULL,

state text NOT NULl
state text NOT NULL,
PRIMARY KEY (server, username)
) CHARACTER SET utf8mb4
ROW_FORMAT=DYNAMIC;

CREATE INDEX i_last_seconds ON last(seconds);

CREATE INDEX i_last_server_seconds ON last (server, seconds);

CREATE TABLE rosterusers (
server varchar(250) NOT NULL,
Expand Down
8 changes: 5 additions & 3 deletions priv/pg.sql
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ CREATE TABLE users (


CREATE TABLE last (
username varchar(250) PRIMARY KEY,
server varchar(250),
username varchar(250),
seconds integer NOT NULL,
state text NOT NULL
state text NOT NULL,
PRIMARY KEY (server, username)
);

CREATE INDEX i_last_seconds ON last USING btree (seconds);
CREATE INDEX i_last_server_seconds ON last USING btree (server, seconds);


CREATE TABLE rosterusers (
Expand Down
15 changes: 8 additions & 7 deletions src/admin_extra/service_admin_extra_accounts.erl
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,14 @@ get_sha(AccountPass) ->


-spec num_active_users(jid:server(), integer()) -> non_neg_integer().
num_active_users(Host, Days) ->
num_active_users(Domain, Days) ->
TimeStamp = erlang:system_time(second),
TS = TimeStamp - Days * 86400,
case catch mod_last:count_active_users(Host, TS) of
{'EXIT', _Reason} ->
0;
Val ->
Val
try
{ok, HostType} = mongoose_domain_api:get_domain_host_type(Domain),
mod_last:count_active_users(HostType, Domain, TS)
catch _:_ ->
0
end.


Expand Down Expand Up @@ -237,7 +237,8 @@ delete_old_user({LUser, LServer}, TimeStampNow, SecOlder) ->
SecOlder :: non_neg_integer()) -> boolean().
delete_old_user_if_nonactive_long_enough(JID, TimeStampNow, SecOlder) ->
{LUser, LServer} = jid:to_lus(JID),
case mod_last:get_last_info(LUser, LServer) of
{ok, HostType} = mongoose_domain_api:get_domain_host_type(LServer),
case mod_last:get_last_info(HostType, LUser, LServer) of
{ok, TimeStamp, _Status} ->
%% get his age
Sec = TimeStampNow - TimeStamp,
Expand Down
4 changes: 2 additions & 2 deletions src/admin_extra/service_admin_extra_last.erl
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ set_last(User, Server, Timestamp, Status) ->
JID = jid:make(User, Server, <<>>),
case ejabberd_auth:does_user_exist(JID) of
true ->
mod_last:store_last_info(JID#jid.luser, JID#jid.lserver, Timestamp, Status),
{ok, HostType} = mongoose_domain_api:get_host_type(JID#jid.lserver),
mod_last:store_last_info(HostType, JID#jid.luser, JID#jid.lserver, Timestamp, Status),
{ok, io_lib:format("Last activity for user ~s is set as ~B with status ~s",
[jid:to_binary(JID), Timestamp, Status])};
false ->
String = io_lib:format("User ~s@~s does not exist", [User, Server]),
{user_does_not_exist, String}
end.

27 changes: 12 additions & 15 deletions src/auth/ejabberd_auth_external.erl
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ check_password_extauth(HostType, LUser, LServer, Password) ->
Password :: binary(),
CacheTime :: integer()) -> boolean().
check_password_cache(HostType, LUser, LServer, Password, CacheTime) ->
case get_last_access(LUser, LServer) of
case get_last_access(HostType, LUser, LServer) of
online ->
check_password_internal(HostType, LUser, LServer, Password);
never ->
Expand Down Expand Up @@ -272,7 +272,7 @@ get_password_internal(HostType, LUser, LServer) ->
LServer :: jid:lserver(),
CacheTime :: integer()) -> false | binary().
get_password_cache(HostType, LUser, LServer, CacheTime) ->
case get_last_access(LUser, LServer) of
case get_last_access(HostType, LUser, LServer) of
online ->
get_password_internal(HostType, LUser, LServer);
never ->
Expand Down Expand Up @@ -348,14 +348,13 @@ is_fresh_enough(TimeStampLast, CacheTime) ->

%% @doc Code copied from mod_configure.erl
%% Code copied from web/ejabberd_web_admin.erl
-spec get_last_access(User :: jid:user(),
Server :: jid:server()
) -> online | never | mod_last_required | integer().
get_last_access(User, Server) ->
JID = jid:make(User, Server, <<>>),
-spec get_last_access(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
online | never | mod_last_required | integer().
get_last_access(HostType, LUser, LServer) ->
JID = jid:make(LUser, LServer, <<>>),
case ejabberd_sm:get_user_resources(JID) of
[] ->
case get_last_info(User, Server) of
case get_last_info(HostType, LUser, LServer) of
mod_last_required ->
mod_last_required;
not_found ->
Expand All @@ -368,13 +367,11 @@ get_last_access(User, Server) ->
end.


-spec get_last_info(User :: jid:user(),
Server :: jid:server()
) -> {ok, Timestamp :: integer(), Status :: binary()}
| not_found | mod_last_required.
get_last_info(User, Server) ->
case gen_mod:is_loaded(Server, mod_last) of
true -> mod_last:get_last_info(User, Server);
-spec get_last_info(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
{ok, Timestamp :: integer(), Status :: binary()} | not_found | mod_last_required.
get_last_info(HostType, LUser, LServer) ->
case gen_mod:is_loaded(HostType, mod_last) of
true -> mod_last:get_last_info(HostType, LUser, LServer);
_ -> mod_last_required
end.

Expand Down
123 changes: 71 additions & 52 deletions src/mod_last.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,33 @@
-behaviour(gen_mod).
-behaviour(mongoose_module_metrics).

-export([
start/2,
%% Gen_mod callbacks
-export([start/2,
stop/1,
config_spec/0,
process_local_iq/5,
supported_features/0]).

%% IQ and hook handlers
-export([process_local_iq/5,
process_sm_iq/5,
on_presence_update/5,
store_last_info/4,
get_last_info/2,
count_active_users/2,
remove_user/3,
session_cleanup/5
]).
on_presence_update/5,
session_cleanup/5]).

%% API
-export([store_last_info/5,
get_last_info/3,
count_active_users/3]).

-export([config_metrics/1]).

-define(MOD_LAST_BACKEND, mod_last_backend).
-ignore_xref([
{?MOD_LAST_BACKEND, count_active_users, 2},
{?MOD_LAST_BACKEND, get_last, 2},
{?MOD_LAST_BACKEND, remove_user, 2},
{?MOD_LAST_BACKEND, count_active_users, 3},
{?MOD_LAST_BACKEND, get_last, 3},
{?MOD_LAST_BACKEND, remove_user, 3},
{?MOD_LAST_BACKEND, init, 2},
{?MOD_LAST_BACKEND, set_last_info, 4},
{?MOD_LAST_BACKEND, set_last_info, 5},
behaviour_info/1, on_presence_update/5, process_local_iq/4,
process_sm_iq/4, remove_user/3, session_cleanup/5
]).
Expand Down Expand Up @@ -139,6 +143,8 @@ riak_config_spec() ->
format = none
}.

supported_features() -> [dynamic_domains].

%%%
%%% Uptime of ejabberd node
%%%
Expand Down Expand Up @@ -177,33 +183,32 @@ get_node_uptime() ->
process_sm_iq(Acc, _From, _To, #iq{type = set, sub_el = SubEl} = IQ, _Extra) ->
{Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}};
process_sm_iq(Acc, From, To, #iq{type = get, sub_el = SubEl} = IQ, _Extra) ->
Server = To#jid.lserver,
{Subscription, _Groups} =
mongoose_hooks:roster_get_jid_info(Server, To, From),
HostType = mongoose_acc:host_type(Acc),
{Subscription, _Groups} = mongoose_hooks:roster_get_jid_info(HostType, To, From),
MutualSubscription = Subscription == both,
RequesterSubscribedToTarget = Subscription == from,
QueryingSameUsersLast = (From#jid.luser == To#jid.luser) and
(From#jid.lserver == To#jid.lserver),
case MutualSubscription or RequesterSubscribedToTarget or QueryingSameUsersLast of
true ->
UserListRecord = mongoose_hooks:privacy_get_user_list(Server, To),
UserListRecord = mongoose_hooks:privacy_get_user_list(HostType, To),
{Acc1, Res} = mongoose_privacy:privacy_check_packet(Acc, To,
UserListRecord, To, From,
out),
{Acc1, make_response(IQ, SubEl, To, Res)};
{Acc1, make_response(HostType, IQ, SubEl, To, Res)};
false ->
{Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:forbidden()]}}
end.

-spec make_response(jlib:iq(), SubEl :: 'undefined' | [exml:element()],
-spec make_response(host_type(), jlib:iq(), SubEl :: 'undefined' | [exml:element()],
jid:jid(), allow | deny) -> jlib:iq().
make_response(IQ, SubEl, _, deny) ->
make_response(_HostType, IQ, SubEl, _, deny) ->
IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:forbidden()]};
make_response(IQ, SubEl, JID, allow) ->
make_response(HostType, IQ, SubEl, JID, allow) ->
#jid{luser = LUser, lserver = LServer} = JID,
case ejabberd_sm:get_user_resources(JID) of
[] ->
case get_last(LUser, LServer) of
case get_last(HostType, LUser, LServer) of
{error, _Reason} ->
IQ#iq{type = error,
sub_el = [SubEl, mongoose_xmpp_errors:internal_server_error()]};
Expand Down Expand Up @@ -232,48 +237,62 @@ make_response(IQ, SubEl, JID, allow) ->
children = []}]}
end.

get_last(LUser, LServer) ->
mod_last_backend:get_last(LUser, LServer).

-spec count_active_users(jid:lserver(), non_neg_integer()) -> non_neg_integer().
count_active_users(LServer, Timestamp) ->
mod_last_backend:count_active_users(LServer, Timestamp).

-spec on_presence_update(map(), jid:luser(), jid:lserver(), jid:lresource(),
Status :: binary()) -> map() | {error, term()}.
on_presence_update(Acc, LUser, LServer, _Resource, Status) ->
TimeStamp = erlang:system_time(second),
case store_last_info(LUser, LServer, TimeStamp, Status) of
ok -> Acc;
E -> E
end.

-spec store_last_info(jid:luser(), jid:lserver(), non_neg_integer(),
Status :: binary()) -> ok | {error, term()}.
store_last_info(LUser, LServer, TimeStamp, Status) ->
mod_last_backend:set_last_info(LUser, LServer, TimeStamp, Status).

-spec get_last_info(jid:luser(), jid:lserver())
-spec get_last_info(host_type(), jid:luser(), jid:lserver())
-> 'not_found' | {'ok', integer(), binary()}.
get_last_info(LUser, LServer) ->
case get_last(LUser, LServer) of
get_last_info(HostType, LUser, LServer) ->
case get_last(HostType, LUser, LServer) of
{error, _Reason} -> not_found;
Res -> Res
end.

-spec remove_user(mongoose_acc:t(), jid:user(), jid:server()) -> mongoose_acc:t().
remove_user(Acc, User, Server) ->
HostType = mongoose_acc:host_type(Acc),
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
R = mod_last_backend:remove_user(LUser, LServer),
R = mod_last_backend:remove_user(HostType, LUser, LServer),
mongoose_lib:log_if_backend_error(R, ?MODULE, ?LINE, {Acc, User, Server}),
Acc.

%% TODO fix
-spec session_cleanup(Acc :: map(), LUser :: jid:luser(), LServer :: jid:lserver(),
LResource :: jid:lresource(), SID :: ejabberd_sm:sid()) -> any().
session_cleanup(Acc, LUser, LServer, LResource, _SID) ->
on_presence_update(Acc, LUser, LServer, LResource, <<>>).
-spec on_presence_update(mongoose_acc:t(), jid:luser(), jid:lserver(), jid:lresource(), status()) ->
mongoose_acc:t().
on_presence_update(Acc, LUser, LServer, _Resource, Status) ->
store_last_info(Acc, LUser, LServer, Status).

-spec session_cleanup(mongoose_acc:t(), jid:luser(), jid:lserver(), jid:lresource(),
ejabberd_sm:sid()) ->
mongoose_acc:t().
session_cleanup(Acc, LUser, LServer, _LResource, _SID) ->
store_last_info(Acc, LUser, LServer, <<>>).

-spec store_last_info(mongoose_acc:t(), jid:luser(), jid:lserver(), status()) -> mongoose_acc:t().
store_last_info(Acc, LUser, LServer, Status) ->
HostType = mongoose_acc:host_type(Acc),
TimeStamp = erlang:system_time(second),
store_last_info(HostType, LUser, LServer, TimeStamp, Status),
Acc.

-spec store_last_info(host_type(), jid:luser(), jid:lserver(), timestamp(), status()) -> ok.
store_last_info(HostType, LUser, LServer, TimeStamp, Status) ->
case mod_last_backend:set_last_info(HostType, LUser, LServer, TimeStamp, Status) of
{error, Reason} ->
?LOG_ERROR(#{what => set_last_info_failed,
text => <<"Unexpected error while storing mod_last information">>,
user => LUser, server => LServer,
timestamp => TimeStamp, status => Status,
reason => Reason});
ok ->
ok
end.

-spec get_last(host_type(), jid:luser(), jid:lserver()) ->
{ok, timestamp(), status()} | {error, term()} | not_found.
get_last(HostType, LUser, LServer) ->
mod_last_backend:get_last(HostType, LUser, LServer).

-spec count_active_users(host_type(), jid:lserver(), timestamp()) -> non_neg_integer().
count_active_users(HostType, LServer, Timestamp) ->
mod_last_backend:count_active_users(HostType, LServer, Timestamp).

config_metrics(Host) ->
OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value}
Expand Down
Loading

0 comments on commit a6eaf5d

Please sign in to comment.